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>
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
* [web] Fix preview next-invoice date for cadence-change upgrades
When upgrading from a monthly plan to an annual plan (or vice versa) the
user pays for a full new-cadence term today, so the next payment is one
new-term-length from now — not the current cycle's period end. Previously
we always echoed subscription.periodEnd in the preview, which surfaced
the stale current-cycle date and misled the user into thinking they'd
be charged again ~25 days later.
makeChangePreview now compares the current and next plans' annual flag:
on a cadence flip it returns now + 1 year or now + 1 month; otherwise it
keeps the existing behaviour.
Closes#33283.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Format
* Fix next invoice date using priceincents
* Apply suggestions from code review
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
GitOrigin-RevId: 05b660ecb518c04b60e88f2ddc7531733245bdde
* Default interstitial to monthly plans except for upgrade, which defaults to user's existing subscription period
* Add tests for interstitial page period toggle defaults
GitOrigin-RevId: fa0ac41e7d8a7bf858b53e0940287b28ef21253d
* adding events for success and failure for import and export from latex
* adding the operation property to capture the import/export keyword
GitOrigin-RevId: 2e5482b3c7517b402fc151966975ca8718729683
* [docstore] add useSecondary flag to projectHasRanges
The rev-check for unarchiving always consults with the primary.
Two extra changes:
- Add a projection argument to peekDoc in order to skip lines download
from projectHasRanges.
- Add one retry to peekDoc to reduce chances of surfacing a rev-check
violation.
* [web] resync_projects: use the secondaries for all reads
* [web] add default value for useSecondary
* [docstore] add default value for useSecondary
* [k8s] docstore: set MONGO_HAS_SECONDARIES=true
GitOrigin-RevId: f15ec4fdc1cabe74c1eab87bec85f28d6f7a587d
* [web] Reject tracked changes notifications
feat: adding new tests
feat: adding rejected changes notifications
feat: adding tests for rejectchanges
feat: updating tests for rejecting notifications;
feat: adding in rejecting user, and improving subject and activity line
fix: moving to a params object instead of positionals for email building
feat: updating to use events triggered from applyUpdate in document-updater
feat: updating to send rejected author ids with rejected change notification instead of change ids
feat: moving rejected author notification determination to updateManager instead of RangesManager, which is used by other paths
feat: only map to author if changes were made
* fix: gate by user status not project status
* fix: unit tests post-rebase
---------
Co-authored-by: Kristina Hjertberg <kristina.hjertberg@overleaf.com>
GitOrigin-RevId: f992e1885c47d1a6cf776740769d6d4763f3cb7c
* [history-v1] add endpoint for downloading latest zip
* [web] address review feedback
* [web] tests: do not overwrite db.projects.overleaf, extend it
* [web] set includeReferer flag from downloading zip
GitOrigin-RevId: e63e549f004230086f82eccf03b43fd62bde6071
* [web] cleanup archived split-test assignments from user record on login
Co-authored-by: Anna Claire Fields <anna.fields@overleaf.com>
* [migrations] purge archived split tests from all users
Co-authored-by: Anna Claire Fields <anna.fields@overleaf.com>
* [web] add missing mock and update snapshot test
* [web] gracefully access db.users.splitTests
---------
Co-authored-by: Anna Claire Fields <anna.fields@overleaf.com>
GitOrigin-RevId: bd185074a402556d7b7c812208cf834dd52b27a5
* [web] Add Papers/ReadCube icon to plans page integrations
Closes#33493
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Delete old 200kB zotero logo
* Allow png use in logos
* Allow wrap
* [web] Share third-party integration icon list across plans and onboarding pages
Extract the icons array to services/web/app/src/util/third-party-icons.mjs
so the plans-2026 feature table and the try_premium onboarding page render
from a single source. The try_premium page now also includes Papers and
follows the plans page ordering.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [web] Allow ciam try-premium logo row to wrap
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GitOrigin-RevId: f5a52418cbe01d9e343092b552183dffa3ae78bd
* [web] add includeReferer flag to SplitTestHandler.getAssignment
* [web] tests: migrate User.getSplitTestAssignment to async/await
I don't want to fight with callbacks and optional arguments. Just move
it to async/await. New tests should use async/await, so there is no
point in making this work in callback-hell.
* [web] remove unused URL import
GitOrigin-RevId: 6251001e6ba7354f704fa663be8ef365ca0b9d23
* Add info to errors in ProjectLocator
* Update ProjectLocator.test.mjs
* Add info to errors in SSOConfigManager
* Update SSOConfigManager.test.mjs
GitOrigin-RevId: 5a13350af1808f3a16a4bc8a9946cbe8f15e6b3a