Rate limit: auto-compile requests already have a client-side debounce; skip
the 1-second server-side recently-compiled gate for them to avoid spurious
'too-recently-compiled' rejections that were blocking ~1/3 of Typst compiles.
PDF flicker: add _snapshotCanvases() fallback for browsers without element-level
View Transitions (Chrome <126, Firefox, Safari). Before setDocument() clears the
canvases it copies each rendered page to a positioned overlay; the overlay is
removed once the first page of the new document fires pagerendered, giving a
seamless old→new swap in all browsers. Chrome 126+ continues to use the
startViewTransition async callback path.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Replace token-link email with 6-digit code on SSO registration
Unverified SSO emails previously received a long-lived token link
(90-day TTL) via UserEmailsConfirmationHandler. This replaces that
flow with the same 6-digit code verification used for password
registration, redirecting through /registration/confirm-email.
- SSOManager.registerSSO now always confirms email (caller must
verify first); removes sendConfirmationEmail / _finishRegistration
- SSOController._signUp sends confirmation code and stores
pendingSSORegistration in session when IdP email_verified is false
- New SSOConfirmEmailHandler completes registration after code check
via completeSSOEmailConfirmation module hook
- OnboardingController confirm-email handlers accept
pendingSSORegistration alongside pendingUserRegistration
confirmEmailFromToken (POST /user/emails/confirm) removal is deferred
to a follow-up PR to avoid breaking in-flight 90-day tokens.
Closes#28607
* Fix unverified-email edge cases; Add ORCID e2e tests;
* Rename `confirmEmail` parameter to `emailVerifiedByIdP` in _signUp function
* Remove `sendConfirmationEmail`
* Mock getUserByAnyEmail in tests
* Extract _finishSSORegistration helper to deduplicate the register →
set session flags → allocate referral → finishSaasLogin → finishLogin
sequence shared by both the direct and deferred (code-confirmed) paths.
* Stop duplicating session data in pendingSSORegistration
analyticsId, splitTests, and referal_* are already in the session at
confirmation time — no need to copy them into pendingSSORegistration.
Re-fetch splitTests fresh on completion instead.
* Simplify the code
* Remove dead confirmEmail template
No callers remain after sendConfirmationEmail was deleted. The token-link
flow (confirmEmailFromToken) only validates tokens, never sends email.
* Remove dead reconfirmEmail template
* Address comments from Copilot
* Clear stale pending registration when starting a new flow
* Add unit tests for completeSSOEmailConfirmation
* Add `verificationMethod` param
* Fix camelcase issues
* Extract _createSSOUser and _registerAndFinish helpers to deduplicate registration logic
* Remove obscure "registration_error"
* Prevent FormTextIcon from shrinking
* Enable "email_already_registered_sso" error
* Misc. improvements to confirm-email-form.tsx
* Remove `UserEmailsConfirmationHandler` mock
Co-authored-by: Olzhas Askar <olzhas.askar@overleaf.com>
* Add info on sso_email.pug page
---------
Co-authored-by: Olzhas Askar <olzhas.askar@overleaf.com>
GitOrigin-RevId: d0196ebc6d81ff61bcd27726d0b899b743d08d64
* [web] Order plans in Change Plan modal consistently
Reorder the plans returned by `buildPlansListForSubscriptionDash` so the
Subscription page "Change plan" modal lists them top-to-bottom as:
1. Student annual
2. Student monthly
3. Standard monthly
4. Standard annual
5. Pro monthly
6. Pro annual
Previously `buildPlansList` produced three per-period buckets which the
dash function concatenated, giving an order that flipped per family.
Replace that with an explicit `CHANGE_PLAN_MODAL_PLAN_CODES` list so the
order matches the Design QA spec at a glance. The now-unused
`studentAccounts`, `individualMonthlyPlans`, `individualAnnualPlans`,
`groupMonthlyPlans`, and `groupAnnualPlans` buckets are dropped from
`buildPlansList` (no other callers).
Closes#34024
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [web] Update personal-plan acceptance test for new buildPlansList shape
The previous test asserted `buildPlansList().individualMonthlyPlans`,
which no longer exists after the change-plan modal reorder dropped the
per-period buckets. Move the assertion to
`buildPlansListForSubscriptionDash()`, which is where the personal-plan
exclusion is now enforced (via `CHANGE_PLAN_MODAL_PLAN_CODES`).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [web] Drop now-dead client-side plan filter
`IndividualPlansTable` used to filter out `paid-personal`,
`paid-personal-annual` and `institutional_commons` defensively because
the old `buildPlansListForSubscriptionDash` returned every non-group
plan that wasn't `hideFromUsers`. The previous commit pins the modal to
an explicit six-plan list (`CHANGE_PLAN_MODAL_PLAN_CODES`), so none of
those plan codes ever reach the frontend and the filter is dead. Remove
it and the now-unused `useMemo` import.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Revert "[web] Drop now-dead client-side plan filter"
This reverts commit 83e8448f2cfa2c68e44b749d5a2bc350a7443c6d.
We'll do that in a later cleanup
* Swap "Student monthly" and "Student annual" for consistency
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GitOrigin-RevId: 046a235e14e7ad6622288f5a5a723f5a4f7f14da
* [web] Redirect missing AI add-on purchase to subscription dashboard
The two error paths in `previewAddonPurchase` redirected to
`/user/subscription/plans#ai-assist`, but the `#ai-assist` anchor was
removed when the AI Assist add-on was retired, so users land at the top
of the plans page with no context. Align both with the other error
branches in the same function and the `plans-2026-phase-1` enabled
branch, which already redirect to
`/user/subscription?redirect-reason=ai-assist-unavailable` — the
subscription dashboard shows the matching warning alert
(`redirect-alerts.tsx`).
Update the acceptance test to match the new redirect target.
Closes#34074
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [web] Update ai-assist-unavailable warning to reflect bundled AI features
The previous copy said "AI Assist isn't available to you due to your
current subscription type", which read as a hard block. Now that the AI
Assist add-on has been retired and AI features are included with every
paid plan, the warning should point users to the pricing page instead of
implying their plan can't access AI at all.
Keep the existing translation key for now — a follow-up can rename it
once #33624 (AI page CTA destination) is resolved.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [web] Link the ai-assist-unavailable warning to the pricing page
* [web] Rename key `ai_assist_unavailable_due_to_subscription_type` -> `ai_assist_unavailable`
* [web] Update french and german translations
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GitOrigin-RevId: ae1319fa5b857d8f292de77c82ef0bda1c7ad144
* Allow admin access to user PATs
* Tests for new screen in admin panel
* Adding error for invalid token and way to parse error for OAuth 2
* Git bridge handles expired PAT
* Script for alerting on close to expiry and expired git tokens
* Refactoring and simplifying
* Updating email templates to match agreed docs
* tweak to email subject to include Overleaf
* Allowing dry run in scripts and general tidy up
* removing redundant tests and dry running script
* Fixing CI errors
* Adding new tab to admin test expectation
* Address PR feedback on oauth2-server changes
- Replace ad-hoc overleafErrorCode prop with a TokenExpiredError subclass
- Collapse listTokens/listTokensForAdmin into a single hook
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Adding cron definitions for alerting on expiring git pat
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
GitOrigin-RevId: 69b9fd901a201592a580c69abe7bd7d603e85d3a
- Hide the Present button when the current output is a PDF (it only makes
sense for HTML/RevealJS decks).
- Publish now supports PDF projects: snapshot output.pdf and serve it inline
via a small index.html wrapper at /p/:token, so link holders can view the
PDF straight from the published version.
- Add a Typst document outline (scans '=' headings) wired into the file
outline panel.
- Dashboard branding: enlarge the instance-name/version text and let the
sidebar Verso wordmark span the full column width.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add a Typst language (stream highlighting + completions) for .typ, and
Quarto completions (code chunks, callouts, cross-refs) for .qmd/markdown.
- Project dashboard: new Format column (Quarto/Typst/LaTeX) from the cheap
project compiler field, surfaced through the projects list API.
- Compiler dropdown: grey out engines that don't match the root file's
extension (.qmd->Quarto, .typ->Typst, .tex->LaTeX engines).
- Replace the Overleaf fill loader with an animated Verso logo: the four
quadrant circles drift on their own orbits while colour warms up with load
progress; reused on the token-access screen too.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Editor rail: the active item used the Overleaf green accent. Switch
--ide-rail-link-active-color/background to the blue scale (--blue-10/70,
--bg-info-03) to match the Verso palette.
- Footers: remove the default "Fork on GitHub!" right_footer item (redundant
with the "Built on Overleaf" link); right_footer now defaults to [].
- Login: move the hero wordmark into a full-width centered block and bump it to
max-width 480px so it's no longer constrained by the form column.
- Projects dashboard: restore the instance name in the top-left navbar (set
OVERLEAF_NAV_TITLE="Verso V1.0 Alpha") instead of the wordmark logo, and move
the full Verso wordmark to the sidebar's lower section (where the old
"Digital Science" mark sat). Revert HeaderLogoOrTitle to its title-first
behaviour now that the dashboard no longer passes a logo.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Three follow-ups after the visual-identity deploy:
- Footer: restore the React <Footer> on the projects dashboard (both
ProjectListDsNav and the legacy DefaultNavbarAndFooter). Removing it earlier
was an overcorrection — it now renders the Verso/AGPL thin footer rather than
the old "Powered by Overleaf" line. Other pages already kept the pug footer.
- Navbar brand: HeaderLogoOrTitle previously hid the logo whenever a nav title
was set, so on the dashboard only the "Verso" instance-name text showed and
the wired-up Verso logo never appeared. Make a configured logo (custom logo
or the Verso brand logo) take precedence over the title text; fall back to the
title only when no logo is provided (unchanged for other navbars).
- Login: enlarge the hero wordmark (max-width 260px -> 380px, full column width).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Introduce the Verso brand marks as self-contained SVGs with the EB Garamond
latin subset embedded as a base64 @font-face, so they render identically in
every context (favicon, CSS background, <img>, inline) with no runtime Google
Fonts dependency — important for the self-hosted alpha. Falls back to Georgia
serif if a browser ignores SVG-embedded fonts.
Assets:
- verso-square.svg — rounded "V" tile (200×200); used as favicon.svg and the
editor top-left toolbar logo.
- verso-logo.svg / verso-logo-dark.svg — wide "verso · ONLINE EDITOR" wordmark
(760×200), light + dark wordmark variants.
Wiring:
- favicon: public/favicon.svg replaced with the square mark.
- editor toolbar: --redesign-toolbar-logo-url (light + dark) -> verso-square.svg.
- projects dashboard navbar: ProjectListDsNav logo -> verso-logo(.dark), with
--navbar-brand-width widened to 200px to fit the wide wordmark.
- login page: centered Verso wordmark above the form; suppress the top navbar
so the hero logo stands alone (no competing Overleaf mark).
PNG favicons / apple-touch-icon are left as-is (no raster tooling available);
modern browsers use the SVG favicon.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Swap the Overleaf marketing footer (careers, pricing, 'for universities', …)
shown on the login and other auth pages for a clean Verso footer: copyright
(Aloïs Coquillard -> alocoq.fr), 'Built on Overleaf' (-> Overleaf repo), and on
the right the AGPL licence (-> repo LICENSE) and Source code (-> the Gitea
repo). This also satisfies the AGPL source-offer on the public domain.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a 'Python packages' button to the file-tree toolbar that opens a modal to
edit the project's requirements.vrf (one package per line, pip syntax), backed
by GET/POST /project/:id/python-requirements (read via ProjectEntityHandler,
write via EditorController.upsertDocWithPath, write-gated). The .vrf file is now
hidden from the file tree, so it is managed only through this editor rather than
appearing as a loose file. Adds python_packages / python_packages_help i18n.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Base image: add opencv-python-headless (cv2) and tqdm to the bundled
scientific stack, and python3-venv (needed to build per-project venvs).
Per-project dependencies: a project's requirements.txt is now installed into a
venv cached by its sha256 (python3 -m venv --system-site-packages, so the
bundled stack stays visible and only extra packages are installed); QuartoRunner
points Quarto at it via QUARTO_PYTHON. A per-hash flock serialises concurrent
builds; pip output is merged into output.log; on failure the render falls back
to the base interpreter. Venvs live under PYTHON_VENVS_DIR
(default /var/lib/overleaf/data/python-venvs).
Gating: PythonVenvGate.userCanInstallPython restricts installs to the project
owner + invited collaborators (ignorePublicAccess excludes anonymous/link
users), threaded to CLSI as allowPythonInstall on the editor compile,
presentation export, and publish paths. Behind OVERLEAF_ENABLE_PROJECT_PYTHON_VENV
(enabled in the deployment). Design doc updated; Phase 2 (egress policy) and
Phase 3 (venv eviction) remain.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The slide-PDF export failed because the CLSI runtime user has no writable
HOME, so Chromium's crashpad couldn't create its database and the browser
died on launch ('chrome_crashpad_handler: --database is required'). Give
decktape's Chromium a fresh writable temp dir via HOME/XDG_*/--user-data-dir
(plus --disable-gpu).
The standalone-HTML export kept returning the old non-embedded file partly
because the GET response had no cache headers, so the browser served its
cached copy; add Cache-Control: no-store to both export responses. Also
switch the embed-resources flags to the long '--metadata KEY:VALUE' form
(the documented Quarto syntax) to remove any ambiguity vs the '-M' alias.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* [web] Fix footer For Students link to activate student toggle
The footer link only set itm_referrer plus a #student-annual hash. The
plans page reads the active plan/period from `plan` and `period` query
params (PlansHelper.getPlansPageViewOptions), so the student tab never
activated from the footer link.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* syncStudentModeFromPlanType after in handleDeprecatedHash
* Change URL update to use replaceState in the pricing page
* Revert "Change URL update to use replaceState in the pricing page"
This reverts commit eac71f193029e3f1c75e0c97261d8a5982c0d35c.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GitOrigin-RevId: 69d689d0fe89fc68cefab9233739fc61da8f2ced
Insert a new "Do you offer discounts for nonprofits?" accordion item
under the educational group discount question in the "Overleaf
multi-license plans" FAQ tab. Routes the "contact sales" link through
the existing `faqContactLink` mixin so click tracking stays consistent
with the other FAQ contact links.
Closes#33494
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GitOrigin-RevId: 582517f1d1f1f7934610253c252cf0f8af2b68a2
The standalone-HTML export produced a non-self-contained file (no slide
CSS/JS, math or images when opened away from the server) because Quarto's
--metadata/-M flag uses KEY:VALUE (colon), not KEY=VALUE. '-M
embed-resources=true' silently registered a bogus key and left
embed-resources unset. Switch to colon syntax and also embed MathJax
(self-contained-math:true) so equations render offline.
For the slide PDF, add --disable-dev-shm-usage (the usual cause of
Chromium crashing inside a container with a small /dev/shm), and have the
export controller return the compile log as text/plain on failure so a
failed PDF export shows the real decktape/Chromium error instead of an
HTML page the browser saves as 'pdf.htm'.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
In RevealJS mode the download button becomes a 2-choice menu:
- Standalone HTML: a one-off compile with embed-resources (chalkboard and other
runtime-only plugins are dropped, since they don't survive self-containment),
yielding a single portable .html.
- Slide PDF: render the deck, then print it with decktape (headless Chromium)
to a faithful one-slide-per-page PDF.
Implementation:
- Dockerfile-base: install decktape + headless Chromium (open-source; deps via
playwright install-deps for Ubuntu-Noble correctness). Base-only change.
- QuartoRunner honours options.exportMode ('html-standalone' | 'pdf-slides');
exportMode is threaded web ClsiManager -> CLSI RequestParser -> CompileManager
-> runner.
- New GET /project/:id/presentation-export/:format compiles in the matching
export mode and streams the result as a download (PresentationExportController,
reusing ClsiManager.getOutputFileStream).
- pdf-hybrid-download-button shows the dropdown when the output is output.html;
PDF/LaTeX projects keep the single download button.
- i18n: download_as_standalone_html / download_as_pdf_slides (en + fr +
extracted-translations.json).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a project-members-only link tier and independent link rotation.
- Three tokens per project instead of two: publicToken (anyone), loginToken
(any logged-in user), memberToken (only users who can read the project).
serve() resolves the token to its tier and enforces accordingly — 'member'
requires AuthorizationManager.canUserReadProject.
- New POST /project/:id/publish-presentation/regenerate { tier } rotates a
single tier's token (invalidating only that old link), leaving the snapshot
and the other links intact.
- Share dialog now shows three links (members / logged-in / anyone), each with
its own Copy and Reset buttons; Publish refreshes, Unpublish removes all.
Preview button opens the logged-in-users link.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the single token + visibility toggle with two stable tokens per project
pointing at the same snapshot:
- publicToken → anyone with the link
- privateToken → any logged-in Verso user
This fixes both reported issues: changing visibility no longer mutates a link
(there's no toggle — both links always exist), and a public link can never
become private by accident. It also fixes public links redirecting to login:
access is now decided purely by which token was used (public token = open),
not a per-record flag.
- Model: storageId (snapshot dir) + publicToken + privateToken; drop token/
visibility.
- Manager.publish: mints both tokens once and reuses them on re-publish; serve
resolves a token to its record and treats the public token as open.
- Controller: returns { publicUrl, privateUrl }.
- Share dialog: shows the private and public links side by side, each with its
own copy button; Publish refreshes, Unpublish removes. Preview button opens
the private link.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A deck served at /p/:token (no trailing slash) made the browser resolve its
relative asset references (main_files/... CSS+JS) against /p/, 404ing them —
so the deck rendered as unstyled HTML with no reveal.js. Publish links now end
in a slash, and the bare /p/:token URL 301-redirects to /p/:token/, so relative
assets resolve under /p/:token/ and load correctly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CompileManager.compile debounces compiles via a Redis key set on every compile
(_checkIfRecentlyCompiled), returning {status:'too-recently-compiled',
outputFiles:[]} when the editor has just auto-compiled. Publishing called
compile() and then required output.html, so it threw "did not produce an HTML
presentation" — which is why Preview/Publish errored whenever the deck was
freshly compiled.
- CompileManager.compile: honour options.bypassRecentCompileCheck to skip the
debounce (still runs the normal autocompile-limit guards).
- PublishedPresentationManager: publish with bypassRecentCompileCheck, and put
the compile status in the error message for diagnosis.
- Controller: catch publish errors, log them, and return the message so the
Share dialog can show what went wrong instead of a generic error.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds the engine + API for publishing a project's compiled HTML/RevealJS deck as
a stable, standalone snapshot served at /p/:token, independent of the editor.
- PublishedPresentation model: one per project { token, visibility, buildId },
re-publishing keeps the same token so shared links stay stable.
- Manager.publish: compiles the project, then copies the HTML deck + its _files
assets + referenced media (now included thanks to the OutputFileFinder fix)
into a persistent snapshot dir (Settings.path.publishedPresentationsFolder,
override with PUBLISHED_PRESENTATIONS_PATH). Logs/aux are excluded.
- Routes: GET/POST/DELETE /project/:id/publish-presentation (owner/reader) for
status/publish/unpublish; public GET /p/:token(/*) serves the deck full-page.
Visibility is enforced in the handler: 'public' = anonymous, 'private' = any
logged-in Verso user. CSP is dropped on these responses so reveal.js renders.
Frontend entry points (share-modal section + top-right Preview button) follow.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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 <noreply@anthropic.com>
A project whose root file is a .typ file now compiles straight to PDF with
Typst, as a third engine beside Quarto (.qmd) and latexmk (.tex). Dispatch
stays purely extension-based.
CLSI:
- New TypstRunner.js: runs `quarto typst compile <main>.typ output.pdf` (reuses
the Typst bundled in Quarto, so no extra binary / Docker change). stderr is
merged into output.log.
- CompileManager: _isTypstFile + a TypstRunner branch in _getRunner, and
TypstRunner added to the isRunning check and stopCompile kill list.
- RequestParser: 'typst' added to VALID_COMPILERS.
web:
- settings.defaults: 'typ' added to validRootDocExtensions and the text
extensions (so .typ opens in the editor); 'typst' added to safeCompilers.
- output-files: the Quarto/Typst log parser (which already understands Typst
`error:`/`warning:` + `┌─ file:line:col` diagnostics) now also handles .typ
compiles, so their errors/warnings populate the log tabs.
Polish:
- New-project menu: "Blank Typst project" + "Example Typst project" in both the
main and welcome dropdowns, backed by createBasicProject/createExampleProject
flavour 'typst', a new mainbasic.typ template and an example-project-typst
presentation (math, an image, a table, lists).
- Compiler dropdown gains a "Typst" option (cosmetic; dispatch is by extension).
README updated: three compilers side by side, with a Writing-a-Typst-document
section.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the generic "Blank project" / "Example project" entries with four
flavour-specific ones in both the New-project dropdown and the welcome-screen
dropdown:
- Blank Quarto project -> empty main.qmd (format: typst)
- Blank LaTeX project -> empty main.tex
- Example Quarto project -> a Reveal.js presentation showcasing images, math,
a table, code and incremental lists (new template
project_files/example-project-quarto/)
- Example LaTeX project -> the existing LaTeX example
Backend: ProjectController.newProject now dispatches the `template` value
(blank_quarto/blank_latex/example_quarto/example_latex, plus the legacy
'example'/'none') to createBasicProject(flavour) / createExampleProject(flavour).
_createRootDoc takes a root-doc name so each flavour gets the right extension —
this also fixes the LaTeX example, whose root doc was wrongly created as
main.qmd, back to main.tex (matching the acceptance test). Signatures stay
backward compatible (flavour defaults: blank=quarto, example=latex).
Also refresh the README: Verso now runs Quarto and LaTeX side by side
(engine chosen by root-file extension), not Quarto instead of LaTeX.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Dockerfile-base: remove TeX Live (no longer needed), install Quarto
1.6.39 which bundles Typst for PDF output. This was the root cause
of all compile failures — the server-ce monolith never had Quarto.
QuartoRunner: run quarto via /bin/sh so stderr is merged into stdout
with 2>&1; write combined output to output.log (not output.stdout)
so the PDF-preview log panel picks it up and shows raw output.
Also write the log on error so failures are always visible.
CompileManager: guard DraftModeManager behind an isLatexFile check —
injecting LaTeX preamble commands into a .qmd file corrupts it and
causes a guaranteed compile failure when draft mode is requested.
ProjectCreationHandler + mainbasic.qmd: new projects now create
main.qmd with a minimal Quarto/Typst frontmatter instead of the
LaTeX main.tex; _createRootDoc names the file main.qmd accordingly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- compiler-setting.tsx: replace hardcoded LaTeX compiler list with a
single Quarto option; drop now-unused getMeta/lodash imports
- project-settings.ts: add 'quarto' to ProjectCompiler union type
- ClsiManager: detect main.qmd as a default root document (preferred
over main.tex); replace hasMainFile boolean with detectedMainFile
so we know which filename to use
- settings.defaults.js: add 'qmd' to validRootDocExtensions so .qmd
files appear as selectable root documents in the UI
- ProjectRootDocManager: sort main.qmd before main.tex in the root
doc candidate list
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* [clsi] add request flag for isCompileFromHistory
* [clsi] derive cacheKey for history snapshot from compile dir
* [clsi] migrate convert project to document to compile from history
* [clsi] address review feedback
* [web] determine root doc at the time of converting the project
* [web] wait for flush before starting document conversion
* [saas-e2e] add tests for root doc override when converting project
GitOrigin-RevId: 71c578030949b89f3a74e7f7ab882dfa9c98c17a