Support LaTeX and Quarto compilation in parallel
Build and Deploy Verso / deploy (push) Has been cancelled
Build and Deploy Verso / deploy (push) Has been cancelled
Verso now compiles both .tex (latexmk) and .qmd (Quarto) projects,
dispatching by the root file's extension rather than replacing one with
the other. LaTeX and Quarto projects can coexist on the same server.
CompileManager: re-import LatexRunner and add a _getRunner() dispatcher
that returns a uniform {run, isRunning, kill} interface. .qmd/.md/.Rmd
→ QuartoRunner; everything else (.tex/.ltx/.Rtex/.Rnw) → LatexRunner.
stopCompile now checks/kills both runners since it has no root path.
compiler-setting.tsx: restore the LaTeX engine choices (pdfLaTeX, LaTeX,
XeLaTeX, LuaLaTeX) alongside Quarto. The dropdown still controls which
TeX engine latexmk uses; actual engine dispatch is by file extension.
Dockerfile-base: reinstall TeX Live alongside Quarto (texlive-full minus
-doc/-lang- packages, plus xetex/luatex/biber/latexmk/texcount/chktex/
synctex). Restore TEXMFVAR for a writable LuaTeX cache. This brings back
a large image, which is the accepted cost of full LaTeX+Quarto support.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,10 @@
|
||||
|
||||
FROM phusion/baseimage:noble-1.0.3
|
||||
|
||||
# Makes sure LuaTeX cache is writable
|
||||
# -----------------------------------
|
||||
ENV TEXMFVAR=/var/lib/overleaf/tmp/texmf-var
|
||||
|
||||
# Update to ensure dependencies are updated
|
||||
# ------------------------------------------
|
||||
ENV REBUILT_AFTER="2026-05-21"
|
||||
@@ -65,6 +69,26 @@ RUN mkdir -p /opt/quarto-extensions \
|
||||
\
|
||||
&& chown -R www-data:www-data /opt/quarto-extensions
|
||||
|
||||
# Install TeX Live (for compiling .tex projects with latexmk)
|
||||
# -----------------------------------------------------------------------
|
||||
# Verso compiles .qmd with Quarto and .tex with latexmk; both engines live
|
||||
# side by side. We install (almost) all of texlive-full, excluding the -doc
|
||||
# and -lang- packages to keep the download from being needlessly huge while
|
||||
# still providing a complete LaTeX toolchain (latexmk, xetex, lualatex,
|
||||
# biber, texcount, chktex, synctex, etc.).
|
||||
# -----------------------------------------------------------------------
|
||||
RUN apt-get update \
|
||||
&& apt-cache depends texlive-full \
|
||||
| grep "Depends: " \
|
||||
| grep -v -- "-doc" \
|
||||
| grep -v -- "-lang-" \
|
||||
| sed 's/Depends: //' \
|
||||
| xargs apt-get install -y --no-install-recommends \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
texlive-xetex texlive-luatex texlive-bibtex-extra biber \
|
||||
latexmk texcount chktex fontconfig inkscape python3-pygments \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
# Set up overleaf user and home directory
|
||||
# -----------------------------------------
|
||||
|
||||
@@ -6,6 +6,7 @@ import logger from '@overleaf/logger'
|
||||
import OError from '@overleaf/o-error'
|
||||
import ResourceWriter from './ResourceWriter.js'
|
||||
import QuartoRunner from './QuartoRunner.js'
|
||||
import LatexRunner from './LatexRunner.js'
|
||||
import OutputFileFinder from './OutputFileFinder.js'
|
||||
import OutputCacheManager from './OutputCacheManager.js'
|
||||
import ClsiMetrics from './Metrics.js'
|
||||
@@ -39,6 +40,30 @@ const KNOWN_LATEXMK_RULES = new Set([
|
||||
|
||||
const LATEX_PASSES_RULES = new Set(['latex', 'lualatex', 'xelatex', 'pdflatex'])
|
||||
|
||||
// Quarto handles .qmd/.md/.Rmd sources; everything else (.tex, .ltx, .Rtex,
|
||||
// .Rnw) is compiled with latexmk via LatexRunner. Dispatch is by the root
|
||||
// file's extension, so LaTeX and Quarto projects can coexist on one server.
|
||||
function _isQuartoFile(rootResourcePath) {
|
||||
return /\.(qmd|md|rmd)$/i.test(rootResourcePath || '')
|
||||
}
|
||||
|
||||
// Return a runner with a uniform { run, isRunning, kill } interface so the
|
||||
// rest of CompileManager doesn't need to know which engine is in use.
|
||||
function _getRunner(rootResourcePath) {
|
||||
if (_isQuartoFile(rootResourcePath)) {
|
||||
return {
|
||||
run: (name, opts) => QuartoRunner.promises.runQuarto(name, opts),
|
||||
isRunning: name => QuartoRunner.isRunning(name),
|
||||
kill: name => QuartoRunner.promises.killQuarto(name),
|
||||
}
|
||||
}
|
||||
return {
|
||||
run: (name, opts) => LatexRunner.promises.runLatex(name, opts),
|
||||
isRunning: name => LatexRunner.isRunning(name),
|
||||
kill: name => LatexRunner.promises.killLatex(name),
|
||||
}
|
||||
}
|
||||
|
||||
function getCompileName(projectId, userId) {
|
||||
if (userId != null) {
|
||||
return `${projectId}-${userId}`
|
||||
@@ -195,8 +220,10 @@ async function doCompile(request, stats, timings) {
|
||||
|
||||
const compileName = getCompileName(request.project_id, request.user_id)
|
||||
|
||||
const runner = _getRunner(request.rootResourcePath)
|
||||
|
||||
try {
|
||||
await QuartoRunner.promises.runQuarto(compileName, {
|
||||
await runner.run(compileName, {
|
||||
directory: compileDir,
|
||||
mainFile: request.rootResourcePath,
|
||||
compiler: request.compiler,
|
||||
@@ -321,16 +348,21 @@ async function _saveOutputFiles({
|
||||
|
||||
async function stopCompile(projectId, userId) {
|
||||
const compileName = getCompileName(projectId, userId)
|
||||
// stopCompile has no root path, so check both runners — only one can be
|
||||
// active for a given compileName at a time.
|
||||
const isRunning =
|
||||
QuartoRunner.isRunning(compileName) || LatexRunner.isRunning(compileName)
|
||||
const lock = LockManager.getExistingLock(getCompileDir(projectId, userId))
|
||||
let lockReleased
|
||||
if (lock) {
|
||||
lockReleased = lock.waitForRelease()
|
||||
} else {
|
||||
if (!QuartoRunner.isRunning(compileName)) return
|
||||
if (!isRunning) return
|
||||
logger.warn({ projectId, userId }, 'found running compile without lock')
|
||||
lockReleased = Promise.resolve()
|
||||
}
|
||||
await QuartoRunner.promises.killQuarto(compileName)
|
||||
await LatexRunner.promises.killLatex(compileName)
|
||||
await lockReleased
|
||||
}
|
||||
|
||||
|
||||
+10
-1
@@ -7,7 +7,16 @@ import { usePermissionsContext } from '@/features/ide-react/context/permissions-
|
||||
import { ProjectCompiler } from '@ol-types/project-settings'
|
||||
import { useSetCompilationSettingWithEvent } from '@/features/editor-left-menu/hooks/use-set-compilation-setting'
|
||||
function getCompilerOptions(): Option<ProjectCompiler>[] {
|
||||
return [{ value: 'quarto', label: 'Quarto' }]
|
||||
// The actual compiler engine is chosen in CLSI by the root file's
|
||||
// extension (.qmd → Quarto, .tex → latexmk). This dropdown still lets
|
||||
// LaTeX users pick which TeX engine latexmk should use.
|
||||
return [
|
||||
{ value: 'quarto', label: 'Quarto' },
|
||||
{ value: 'pdflatex', label: 'pdfLaTeX' },
|
||||
{ value: 'latex', label: 'LaTeX' },
|
||||
{ value: 'xelatex', label: 'XeLaTeX' },
|
||||
{ value: 'lualatex', label: 'LuaLaTeX' },
|
||||
]
|
||||
}
|
||||
|
||||
export default function CompilerSetting() {
|
||||
|
||||
Reference in New Issue
Block a user