From 2d4ca6f13a20c0c401253cafbcd5c7cbcbb5a545 Mon Sep 17 00:00:00 2001 From: claude Date: Mon, 1 Jun 2026 13:08:57 +0000 Subject: [PATCH] Fix LaTeX projects failing to compile (HTTP 500, no logs) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Project.compiler defaults to settings.defaultLatexCompiler ('quarto' in this fork), so every .tex project carried compiler='quarto'. Since the CLSI runner is chosen by file extension, a .tex root still goes to LatexRunner, whose _buildLatexCommand threw `unknown compiler: quarto` — surfacing as an opaque HTTP 500 with no compile log. - LatexRunner: fall back to pdfLaTeX when the compiler isn't a known TeX engine instead of throwing. Universal safety net (covers existing projects, uploads and GitHub imports already saved with compiler='quarto'). - ProjectCreationHandler: store a sensible compiler per flavour at creation via a shared _flavourConfig helper — blank/example LaTeX → 'pdflatex', Typst → 'typst', Quarto → 'quarto' — so the compiler dropdown reflects the engine and LatexRunner receives a valid one directly. Co-Authored-By: Claude Opus 4.8 --- services/clsi/app/js/LatexRunner.js | 14 +++--- .../Project/ProjectCreationHandler.mjs | 48 ++++++++++++------- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/services/clsi/app/js/LatexRunner.js b/services/clsi/app/js/LatexRunner.js index 2fd3afa279..f705edfb27 100644 --- a/services/clsi/app/js/LatexRunner.js +++ b/services/clsi/app/js/LatexRunner.js @@ -197,13 +197,13 @@ function _buildLatexCommand(mainFile, opts = {}) { command.push(...opts.flags) } - // TeX Engine selection - const compilerFlag = COMPILER_FLAGS[opts.compiler] - if (compilerFlag) { - command.push(compilerFlag) - } else { - throw new Error(`unknown compiler: ${opts.compiler}`) - } + // TeX Engine selection. A .tex project may carry a non-LaTeX compiler value + // (e.g. 'quarto', the fork-wide default for Project.compiler) because the + // runner is chosen by file extension, not by this setting. In that case fall + // back to pdfLaTeX rather than throwing — throwing here surfaces as an opaque + // HTTP 500 with no compile log. + const compilerFlag = COMPILER_FLAGS[opts.compiler] || COMPILER_FLAGS.pdflatex + command.push(compilerFlag) // We want to run latexmk on the tex file which we will automatically // generate from the Rtex/Rmd/md file. diff --git a/services/web/app/src/Features/Project/ProjectCreationHandler.mjs b/services/web/app/src/Features/Project/ProjectCreationHandler.mjs index a716c1c575..ed81024807 100644 --- a/services/web/app/src/Features/Project/ProjectCreationHandler.mjs +++ b/services/web/app/src/Features/Project/ProjectCreationHandler.mjs @@ -85,26 +85,38 @@ async function createProjectFromSnippet(ownerId, projectName, docLines) { return project } -async function createBasicProject(ownerId, projectName, flavour = 'quarto') { - const project = await _createBlankProject(ownerId, projectName) - - // Verso compiles .qmd with Quarto, .tex with latexmk and .typ with Typst; - // the root file's extension selects the runner (see CompileManager in CLSI), - // so a blank project's flavour is just a choice of template + root name. - let templateName, rootDocName +// Per-flavour blank-project template, root document name and stored compiler. +function _flavourConfig(flavour) { switch (flavour) { case 'latex': - templateName = 'mainbasic.tex' - rootDocName = 'main.tex' - break + return { + templateName: 'mainbasic.tex', + rootDocName: 'main.tex', + compiler: 'pdflatex', + } case 'typst': - templateName = 'mainbasic.typ' - rootDocName = 'main.typ' - break + return { + templateName: 'mainbasic.typ', + rootDocName: 'main.typ', + compiler: 'typst', + } default: - templateName = 'mainbasic.qmd' - rootDocName = 'main.qmd' + return { + templateName: 'mainbasic.qmd', + rootDocName: 'main.qmd', + compiler: 'quarto', + } } +} + +async function createBasicProject(ownerId, projectName, flavour = 'quarto') { + // Verso compiles .qmd with Quarto, .tex with latexmk and .typ with Typst; + // the root file's extension selects the runner (see CompileManager in CLSI). + // Each flavour is a choice of template + root name + the compiler stored on + // the project (so the compiler dropdown reflects the engine and LatexRunner + // never receives a non-LaTeX compiler for a .tex project). + const { templateName, rootDocName, compiler } = _flavourConfig(flavour) + const project = await _createBlankProject(ownerId, projectName, { compiler }) const docLines = await _buildTemplate(templateName, ownerId, projectName) await _createRootDoc(project, ownerId, docLines, rootDocName) @@ -187,7 +199,11 @@ async function createExampleProject( attributes = {}, flavour = 'latex' ) { - const project = await _createBlankProject(ownerId, projectName, attributes) + const { compiler } = _flavourConfig(flavour) + const project = await _createBlankProject(ownerId, projectName, { + ...attributes, + compiler, + }) let result switch (flavour) {