- 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>
- Instance name: stamp the nav title with the build number at deploy time
("Verso V0.<run> alpha") via a sed placeholder fed by GITHUB_RUN_NUMBER,
instead of the static "Verso V1.0 Alpha".
- Title typeface: self-host the EB Garamond latin subset (same one embedded in
the logo SVGs) and apply it to .navbar-title so the instance name matches the
Verso wordmark.
- Sidebar wordmark: let the logo fill the full sidebar column width (drop the
160px cap).
- Project filters: switch the ds-nav active state (filter selection + theme
toggle) from the green tokens to the blue scale, matching the rail.
- Present button: rename the presentation toolbar action from "Preview" to
"Present" / "Présenter" and add a tooltip explaining it publishes the
presentation and opens it in a new tab. New keys present /
present_publishes_and_opens_in_new_tab in en, fr and extracted-translations.
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>
The file-tree "Python packages" button rendered the literal text
"deployed_code" in the icon font because that glyph was missing from the
outlined/unfilled Material Symbols subset (MaterialSymbolsRoundedUnfilledPartialSlice.woff2),
so the ligature never resolved. The toolbar buttons all use the unfilled
variant, so switching this one to the full filled font would look inconsistent.
Add 'deployed_code' to unfilled-symbols.mjs and regenerate the subset woff2
(same Google Fonts request build-unfilled.mjs makes) so the box/package icon
renders, matching the other outlined toolbar icons.
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>
Build #78 failed in the compile step while Yarn Classic prepared the
@replit/codemirror-* git deps: fetching esbuild's per-platform binaries
returned truncated tarballs ("the file appears to be corrupt" / missing
.yarn-tarball.tgz). The tmpfs classic cache is fresh each build, so there is no
stale entry to blame and nothing to fall back to — it is a transient download
failure (builds #75-77 passed with an identical Dockerfile).
Wrap both the install and compile steps in a 3-attempt retry loop that wipes
the Yarn Classic cache (/usr/local/share/.cache/yarn) and re-fetches before
giving up, dumping pack.log on final failure. The persistent Berry cache and
YARN_NETWORK_CONCURRENCY=1 are unchanged.
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>
#74 corrupted the persistent fallback cache again despite serialising the
fetch, so the cause isn't a write race: BuildKit evicts part of that persistent
cache mount between builds (the first build after each id bump always passed,
later ones failed). Mount /usr/local/share/.cache/yarn as tmpfs so it's clean
every build and nothing can be half-evicted; the Berry cache stays persistent.
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 an 'Add collaborators' heading above the email-invite section in the share
modal so it's visually distinct from the presentation-sharing section.
Add the missing French 'not_found' key so the 404 page shows 'Introuvable'
instead of the raw 'not_found' (the 404 pug template translates server-side;
the key existed in en.json but not fr.json).
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>
Option A: when a {python} cell fails with ModuleNotFoundError/ImportError, the
log now suggests the exact PyPI package to add (with a module->package map, e.g.
cv2 -> opencv-python, sklearn -> scikit-learn), names the Verso requirements
file, and notes it could instead be a local module — so the langmuirthermalstudy
case isn't mistaken for a PyPI package.
Switch the per-project requirements file from requirements.txt to a Verso-
specific requirements.vrf (so it won't be confused with arbitrary .txt files);
QuartoRunner now looks for requirements.vrf, and 'vrf' is registered as an
editable text extension. The dedicated in-UI editor (and hiding it from the
file tree) follows in a separate change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The global python3 kernelspec hardcodes /usr/bin/python3, so even with
QUARTO_PYTHON pointing at the project venv, Quarto launched the kernel in the
base interpreter — packages installed into the venv (e.g. openpyxl) were not
importable. Register a python3 kernelspec inside the venv via
'ipykernel install --sys-prefix' (kernel.json argv -> the venv's python); since
Quarto runs kernel discovery through QUARTO_PYTHON, the venv's kernelspec is
found ahead of the global one and the kernel runs in the venv.
Bump the completion marker (.verso-complete -> .verso-ready) so venvs built
before this change are rebuilt with the kernelspec.
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 web build's 'yarn install' re-prepares the git-sourced @replit/codemirror-*
deps whenever the Berry cache misses (BuildKit GCs it between builds). Each
prepare uses Yarn Classic, which pulls every esbuild platform binary into the
single shared /usr/local/share/.cache/yarn folder; running several prepares in
parallel races and corrupts it ('tar content corrupt', EEXIST, missing
.yarn-tarball.tgz). Bumping the cache id only cleared it until the next
cache-miss build (#69).
Serialise Yarn's fetch with YARN_NETWORK_CONCURRENCY=1 on the install and
compile steps so the prepares no longer write that cache concurrently, and bump
the fallback cache id (v2 -> v3) once more to discard the currently-corrupt
cache. Slightly slower fetch, but no more random cache corruption.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Quarto's own jupyter wrapper (/opt/quarto/share/jupyter/jupyter.py ->
notebook.py) does 'from yaml import safe_load', so executing a {python} cell
failed with ModuleNotFoundError: No module named 'yaml'. The minimal jupyter
stack didn't pull PyYAML in (psutil/ipython already come via ipykernel), so
add pyyaml explicitly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Captures the proposed requirements.txt -> cached virtualenv approach (keyed by
hash, --system-site-packages, QUARTO_PYTHON), its guard rails (auth gating,
egress restriction, resource caps) given anonymous write is enabled, lifecycle
(eviction, failure UX), a phased rollout, and the open decisions.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When a {python} cell fails with ModuleNotFoundError/ImportError, the Quarto
log parser now emits an actionable error ('Python package "X" is not
installed on the server') noting which scientific packages are pre-installed,
instead of leaking an opaque traceback line.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pre-install numpy, pandas, scipy, matplotlib, seaborn, scikit-learn, sympy,
plotly and tabulate so the common data-science libraries are available to
Quarto's Python code cells out of the box. matplotlib uses the headless Agg
backend automatically in the compile environment.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Quarto executes ```{python}``` cells via a Jupyter kernel, but the base image
had no Jupyter ('Jupyter: (None)') and the runtime user (www-data) couldn't
create Quarto's log dir or Jupyter's runtime dir ('Permission denied: mkdir
/var/www/.local/...').
Install the headless Jupyter execution stack (jupyter-client, nbclient,
nbformat, ipykernel) for the system python3 Quarto uses, and register a
system-wide python3 kernelspec under /usr/local/share/jupyter. Also make
/var/www/.local writable by www-data so Quarto/Jupyter can write their
runtime/log files (mirrors the existing /var/www/.cache setup).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The web compile step failed packing the git-sourced @replit/codemirror-*
deps with 'tar content corrupt' / EEXIST / missing .yarn-tarball.tgz errors,
all under /usr/local/share/.cache/yarn/v6 — i.e. a corrupted BuildKit
fallback-cache mount (likely left half-written by an interrupted build), not
a code or dependency change. Bump the fallback cache id so BuildKit
allocates a fresh empty cache; the berry and webpack caches are untouched.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The HTML/PDF export links were plain downloads that left the browser
silently spinning during the server-side render and, on failure, saved an
error page as pdf.txt/pdf.htm. Replace them with fetch-based downloads that
show a modal: a spinner with a 'this can take up to a minute' message while
compiling, and the actual compile log inline if the export fails. The user
can dismiss at any time; a stale request that finishes after dismissal no
longer reopens the modal. Adds the three i18n keys (en/fr + extracted).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
embed-resources cannot be enabled from the CLI: Quarto only honours it when
nested under the format, and a document's own format block fully overrides
project/CLI metadata (confirmed in Quarto docs). So --metadata embed-resources
was silently ignored and the 'standalone' HTML was the ordinary non-embedded
deck referencing a sibling _files/ dir — unstyled, no math, no images once
downloaded on its own.
For the html-standalone export, render a temporary copy of the root .qmd with
embed-resources/self-contained-math enabled and chalkboard disabled inside its
revealjs block (replacing an existing chalkboard key rather than duplicating
it), then clean the temp file up. Falls back to the original file if the deck
isn't an editable nested-revealjs document, so the export is never worse than
before.
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
* [web] Stop bolding AI features unconditionally on the interstitial
The four `strong: true` flags on the AI features in `sectionMain2026`
caused those rows to render bold on every interstitial visit, regardless
of paywall context. The original intent (per Design QA #34022) was for
boldness to highlight the features relevant to the specific paywall the
user came from (e.g. AI paywall -> AI features bolded) — that
conditional logic was never wired up, and currently no `purchaseReferrer`
or paywall reason is plumbed through to the feature config.
Remove the unconditional `strong: true` so the cards render consistently
with the pricing page. Reintroduce conditional bolding in a follow-up
once the paywall→features mapping is scoped by design.
Closes#34022
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Remove `card-include-strong` and related code
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GitOrigin-RevId: 2112214217f3b53d34518efbca546082ce559e26
* [web] Render "Try for free instead" CTA as link, not button
Design QA wants the "Try for free instead" CTAs on the pricing and
interstitial pages styled as marketing links (`link-monospace link-lg`)
rather than the current `btn-ghost` button. Add a `link` button type to
the `plans-cta` mixin that drops the `btn` class and applies the link
classes, and set `buttonType: 'link'` on the six `try_for_free_instead`
CTAs (plans-individual, plans-student, interstitial-payment).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Make link smaller
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GitOrigin-RevId: f911698a9bfa19f8180e58edb3cebcea90468cbd
* Add tests on plurals
* Update `collabs_per_proj` and its pluralisations
* Update `n_user` and its pluralisations
* Update `showing_x_results` and its pluralisations
* Update `show_x_more_projects` and its pluralisations
* `bin/run web npm run extract-translations`
* Populate `_plural` keys in non-en locales
For 2-form languages (da, de, es, fi, fr, it, nl, no, pt, sv, tr), copy
the existing bare-key value into the new `_plural` sibling to prevent
i18next from falling back to English for count!=1.
Also remove orphan singular keys (`collabs_per_proj_single`,
`showing_1_result*`) left over from the previous commits.
Bare-key values remain in their original plural form pending translator
review — count=1 will still render the plural form in non-en until
translators flip those to singular. Multi-form (cs, pl, ru) and
single-form (ja, ko, zh-CN, zh-TW) locales are unchanged.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Flip non-en bare-key values to singular form
Per review, the i18next v3 plural convention uses the bare key for count=1
(singular) and `_plural` for count!=1. The non-en bare-key values were
left as the original plural form by the previous commit so the `_plural`
siblings could be copied from them; this commit flips the bare values to
the singular form per language.
Languages where singular and plural noun forms coincide (Finnish, Swedish,
Turkish) are unchanged.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Apply suggestions from code review
Co-authored-by: Olzhas Askar <olzhas.askar@gmail.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Olzhas Askar <olzhas.askar@gmail.com>
GitOrigin-RevId: 628513ca792c2dcce023247e52b7320e2741cc54
* move Review Toggle into the toolbar
* cleaning up and adding a comment
* adding the cursor styling
* adding isolation on writefull toolbar to adjust z-index of writefull toolbar
* fixing the dark mode colours for review dropdown trigger
* Fix review mode switcher dark mode styles
GitOrigin-RevId: 36847e0debdc4dce5f96492261d25e7cc46b2e96
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>