Commit Graph

28417 Commits

Author SHA1 Message Date
claude be8aef44fe feat(git-sync): independent toggles for project files and PDF push
Build and Deploy Verso / deploy (push) Successful in 12m48s
Two new boolean fields on the project (gitSyncPushFiles, gitSyncPushPdf,
both default true) let users control what gets pushed independently:
- "Push project files" switch — skip all docs/binary files when off
- "Push compiled PDF" switch — grayed out when no pdfPath is set

The push button and auto-push are disabled when both switches would
result in nothing being pushed. Config is stored in MongoDB so settings
persist per-project.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 23:21:37 +00:00
claude 9a474f7790 fix(git-sync): don't require clsiServerId to push PDF
In single-server CE deployments (Verso included) the CLSI does not
return a clsiServerId, so it was always undefined. The push condition
checked pdfPath && pdfBuildId && pdfClsiServerId && userId, meaning the
PDF was silently skipped every time in practice.

clsiServerId is optional in getOutputFileURL (single-server deployments
work without it), so only require pdfPath and pdfBuildId. Also remove
the inner try/catch so PDF fetch errors surface to the user instead of
being swallowed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 23:17:39 +00:00
claude 8ea6f6ecb1 fix(git-sync): wrap action buttons on narrow panels or long labels
flex-wrap: wrap lets buttons reflow to a second line instead of
overflowing. flex: 1 1 auto keeps them proportional within each row.
white-space: nowrap prevents individual button labels from breaking.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 23:02:28 +00:00
claude 0e18230d9a fix(git-sync): use SessionManager to read logged-in user id
Build and Deploy Verso / deploy (push) Successful in 11m46s
req.session.user is not the right path in Overleaf/Verso — the session
uses passport's structure. SessionManager.getLoggedInUserId(req.session)
is the standard way all other controllers access the user id.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 22:57:32 +00:00
claude 9c97e7c01a fix(git-sync): wrap controller in try/catch, fix rail icon, improve error display
Build and Deploy Verso / deploy (push) Successful in 21m33s
- Move userId + req.body reads inside try/catch in pushToGit and
  pullFromGit so any synchronous throw returns JSON, not an HTML 500
  (which made err.data undefined and showed only "Internal Server Error")
- Add extractError() helper in widget that tries err.data.error first,
  then err.message, then String(err) — surfaces the actual git failure
  message from the server log
- Change rail icon from 'merge' (not in unfilled-symbols list) to
  'autorenew' (sync arrows, already in the list) — fixes text showing
  when panel is closed and wrong icon when open

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 22:07:58 +00:00
claude e1533d979f feat(git-sync): add pull from remote (2-way sync)
Build and Deploy Verso / deploy (push) Has been cancelled
POST /project/:id/git-sync/pull clones the configured remote at depth 1,
walks all files under the configured subPath, and upserts each into the
Verso project using upsertDocWithPath (text) or upsertFileWithPath
(binary), with full folder creation via mkdirp. The .git directory is
skipped. Pull is additive/update-only — no Verso entities are deleted.

Text vs binary classification uses Settings.textExtensions (same list
the editor uses for file uploads), so .typ, .tex, .md, .yml etc. all
become editable docs while images and PDFs stay as files.

Frontend: "Pull from git" button added alongside "Push now".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 21:39:23 +00:00
claude d5de6550d4 fix(git-sync): wrap entire pushToGit in try/catch, drop git init -b flag
Build and Deploy Verso / deploy (push) Failing after 22m57s
- getConfig was outside try/catch so errors returned as HTML 500 instead
  of JSON, hiding the real message in the frontend
- git init -b main requires git ≥ 2.28; replaced with git init +
  symbolic-ref to support older git versions in the base image
- apt-get update before git install to avoid stale package list failures

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 21:28:52 +00:00
claude 71f1b928e9 feat: default typst preview to PDF mode, mark Live/WASM as experimental
Build and Deploy Verso / deploy (push) Successful in 13m3s
The WASM renderer has ongoing stability issues so PDF (server) is now
the default for new projects / browsers that haven't chosen a mode yet.
The Live (browser) option remains available in the compile dropdown but
is labelled "experimental" in italic to set expectations.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 21:12:27 +00:00
claude f22ee608a7 feat(git-sync): auto-push on compile, PDF path, icon fix
Build and Deploy Verso / deploy (push) Has been cancelled
- Icon: change rail tab from integration_instructions to merge
- Auto-push on compile: toggle stored in localStorage, watches compile
  context; when compiling goes true→false with no error, triggers a push
  automatically (including the latest build's PDF if configured)
- PDF destination path: new gitSyncPdfPath field; backend fetches the
  compiled PDF from CLSI (buildId + clsiServerId passed from frontend)
  and writes it at the configured path in the repo; silently skipped if
  no recent compile or field is blank
- Push now always sends current buildId/clsiServerId so PDF is included
  without needing a separate save step

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 21:07:13 +00:00
claude 9f1c9babf7 fix(git-sync): auto-save config before pushing
Build and Deploy Verso / deploy (push) Successful in 11m3s
Push now always saves the current form values first, so clicking Push
without a prior Save no longer returns "No git remote configured".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 20:52:59 +00:00
claude 44a9adcea1 ci: add OVERLEAF_ENABLE_GIT_SYNC to test deployment env
Build and Deploy Verso / deploy (push) Successful in 1m25s
The flag was added to the prod k8s manifest but missing from the
inline deployment spec in the test workflow — causing the Integrations
tab to stay hidden in the test environment.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 20:31:23 +00:00
claude a17d21c0ca feat(git-sync): add optional subdirectory field
Build and Deploy Verso / deploy (push) Successful in 12m29s
Project files can now be pushed into a subfolder of the target repo
rather than always going to the root. The path is sanitised on the
backend (strips leading/trailing slashes, rejects traversal with ..).
An empty value (default) keeps the existing root behaviour.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 20:13:37 +00:00
claude a7a569303c ci: allow deploy-verso to trigger from git-bridge-test branch
Build and Deploy Verso / deploy (push) Has been cancelled
- Add git-bridge-test to push trigger branches
- Pass the triggering branch through to the k8s buildkit job via sed
  substitution (same pattern as NAV_TITLE), so workflow_dispatch from
  any branch also clones the right ref instead of always cloning main

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 20:06:24 +00:00
claude 8214ca6121 feat: add pragmatic git sync — push project to external git remote
Adds a "Git Sync" section in the Integrations rail panel that lets
project owners configure an HTTPS remote URL (with embedded auth token)
and force-push all project files as a single commit.

Backend:
- GitSyncHandler: assembles project docs + binary files into a temp dir,
  runs git init/commit/push --force, then cleans up
- GitSyncController: GET/POST /project/:id/git-sync (configure),
  POST /project/:id/git-sync/push (trigger)
- Project model: gitRemote field
- Dockerfile: ensures git is present at runtime
- Env flag: OVERLEAF_ENABLE_GIT_SYNC=true (set in k8s manifest)

Frontend:
- GitSyncWidget: URL input + Save + Push Now buttons, success/error feedback
- Integrations panel: shows widget when gitSyncEnabled
- Rail: shows Integrations tab when gitSyncEnabled (was only gitBridgeEnabled)
- i18n: en + fr translations

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 20:01:16 +00:00
claude 4f5dad383b fix(typst-preview): use persistent session to avoid Rc ownership panics
Build and Deploy Verso / deploy (push) Successful in 1m5s
Every call to renderToSvg({ artifactContent }) internally routes through
runWithSession, which calls session.free() after fn resolves. Because the JS
GC may still hold a reference to the first RenderSession wrapper, the next
render's Rc::try_unwrap() panics with 'attempted to take ownership of Rust
value while it was borrowed'.

Fix: use renderer.createModule(data) once to create a persistent RenderSession
that is never freed during the component's lifetime. Subsequent renders call
session.manipulateData({ action: 'reset', data }) (synchronous, no ownership
transfer) + session.renderToSvg({ container }) which routes through
withinOptionSession's renderSession fast-path — bypassing runWithSession and
its session.free() entirely.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 15:49:50 +00:00
claude eedf4b50f6 fix(typst-preview): use RenderByContentOptions to avoid Rust aliasing
Build and Deploy Verso / deploy (push) Successful in 10m35s
Replace runWithSession + manipulateData + session.renderToSvg with the
direct RenderByContentOptions form: renderer.renderToSvg({ format: 'vector',
artifactContent, container }).

The session-based API kept hitting 'recursive use of an object detected
which would lead to unsafe aliasing in rust' because runWithSession holds
a mutable borrow of the session while renderToSvg also takes one —
regardless of whether you call renderer.renderToSvg({ renderSession }) or
session.renderToSvg(). The content-based form creates and disposes the
session internally without any caller-visible borrow.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 15:22:56 +00:00
claude c6d71e58b7 fix(typst-preview): fix recursive Rust aliasing error and stale renders
Build and Deploy Verso / deploy (push) Successful in 12m44s
Two bugs fixed:

1. 'recursive use of an object' Rust error: inside runWithSession(), calling
   renderer.renderToSvg({ renderSession: session }) passes the session to the
   renderer while runWithSession already holds it — double-aliasing the same
   Rust object. Fixed by using session.renderToSvg({ container }) directly.

2. Stale preview after edits: concurrent doRender calls (compile finishes
   while previous render is still in progress) would both enter runWithSession
   simultaneously, causing the Rust error and leaving the view frozen. Fixed
   with a render guard (isRenderingRef) that queues the latest vectorData and
   flushes it once the current render completes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 14:52:49 +00:00
claude 06085cda21 fix(csp): allow WebAssembly instantiation via wasm-unsafe-eval
Build and Deploy Verso / deploy (push) Successful in 11m54s
WebAssembly.instantiateStreaming() requires 'wasm-unsafe-eval' in the
script-src CSP directive. Unlike 'unsafe-eval', this only permits WASM
compilation and does not allow arbitrary eval() calls.

Needed for the typst.ts WASM preview (both compiler and renderer).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 14:35:26 +00:00
claude 8515a899ac fix(typst-preview): fix race condition and error-masking in WASM preview
Build and Deploy Verso / deploy (push) Successful in 12m35s
Two bugs were causing a brief red error then blank screen:

1. triggerCompile closed over `view` in useCallback deps, so every time
   the CodeMirror view reference changed, useEffect terminated and
   recreated the entire worker. Fixed by reading view via a ref, making
   triggerCompile stable (empty dep array).

2. When 'compiled' arrived before the renderer WASM finished loading,
   the old code called setStatus('ready') to silently skip rendering —
   this cleared any existing error and left a blank screen. Fixed by
   buffering the vectorData in pendingVectorRef and flushing it once
   the renderer is ready.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 14:05:41 +00:00
claude 200bff4ecb feat(typst): browser-side live preview via typst.ts WASM
Build and Deploy Verso / deploy (push) Successful in 12m51s
Adds a dual-mode Typst preview: a new "Live (browser)" mode compiles and
renders Typst documents entirely in-browser using typst.ts WASM (28 MB
compiler + 1 MB renderer). The existing server-side PDF mode is preserved
and selectable via a new "Preview mode" section in the recompile dropdown,
visible only for Typst projects.

Architecture:
- Web Worker (typst-preview-worker.ts) runs the WASM compiler; queues
  compile requests so only the latest compile runs after each keypress
- TypstWasmPreview component initialises the renderer on the main thread,
  listens to changedAt from the compile context, debounces at 400 ms, and
  renders SVG into a container div via renderToSvg
- typstPreviewMode ('wasm'|'pdf') is persisted per-project in localStorage
- isTypstProject, changedAt, typstPreviewMode, setTypstPreviewMode are
  exposed through both LocalCompileContext and DetachCompileContext
- Fonts loaded from jsDelivr CDN (text subset only) on first use
- Phase 1: single-file Typst only (no #include, no images)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 13:42:39 +00:00
claude b5cf5f9e7b docs: add AI writing assistant to alpha-4 TODO
Build and Deploy Verso / deploy (push) Successful in 59s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 12:29:15 +00:00
claude 60d48ae532 docs: release alpha 3 — remove in-progress marker, fix export description
Build and Deploy Verso / deploy (push) Has been cancelled
Build and Deploy Verso (prod) / deploy (push) Successful in 1m19s
Mark Alpha 3 as released (drop the "in progress" qualifier) and correct
the bidirectional format export entry: only LaTeX ↔ Typst conversion is
available; DOCX, Markdown and HTML exports are not yet implemented.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
alpha-3
2026-06-19 11:32:41 +00:00
claude a796577199 fix(security): restrict publish-presentation routes to project owners
Build and Deploy Verso / deploy (push) Successful in 10m54s
Read-only collaborators and token-link users could publish, unpublish,
and rotate presentation share tokens. Change all three write endpoints
from ensureUserCanReadProject to ensureUserCanAdminProject so only the
project owner can perform these actions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 10:17:24 +00:00
claude 1814cba458 fix(mobile): extend CSS breakpoints to cover Tor Browser spoofed viewport
Build and Deploy Verso / deploy (push) Successful in 10m11s
Tor Browser sets viewport width to ~980px to resist fingerprinting, so
@media (max-width: 767px) never fires on a real phone using it. Add the
secondary condition (pointer: coarse) and (max-width: 1024px) — same
dual-check already used in JS — to all mobile CSS overrides in
ide-lumiere.scss and project-list-lumiere.scss. This activates the font
scaling, toolbar height increase, auto-zoom prevention, and project page
layout fixes on Tor Browser.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 08:24:29 +00:00
claude 4c5db24963 fix(mobile): prevent editor auto-zoom on iOS/Android
Build and Deploy Verso / deploy (push) Successful in 10m28s
Browsers zoom any focused editable element (including contenteditable)
whose font-size is below 16px. Apply font-size: max(1rem, 1em) on
.cm-content inside the mobile media query so the 16px floor is enforced
while still respecting user-set sizes larger than that.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 07:01:20 +00:00
claude 2d44c920fd fix(mobile): scale up editor UI chrome fonts and toolbar on mobile
Build and Deploy Verso / deploy (push) Successful in 12m33s
Adds a @media (max-width: 767px) block scoped to .ide-redesign-main
that bumps the CSS font-size tokens one step each and increases the
toolbar height, making buttons, labels, and panel headers readable
on a phone without touching the CodeMirror editor font size (which
is controlled by user settings independently).

Also reverts the unintended rail auto-collapse from the previous
commit — collapsing the sidebar was not the requested change.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 22:23:23 +00:00
claude 7bc60a4e10 feat(mobile): collapse rail by default; tighten project page header layout
Build and Deploy Verso / deploy (push) Has been cancelled
Editor:
- Auto-collapse the file-tree rail on mobile so the editor+PDF panels
  occupy the full screen width instead of sharing it with a 15% sidebar.
  The user can still open the rail from the toolbar; the collapse only
  fires on first load or when switching to a mobile viewport.

Project page (Lumiere):
- Move the XS/M/L zoom buttons from the mobile filter-pill toolbar into
  a row with the New Project button, so neither is stranded alone.
- The search bar gets its own full-width row above the actions row.
- Desktop layout is unchanged (search + new-project stay side-by-side;
  zoom stays in the title row). `display:contents` makes the wrapper
  div transparent to the desktop flex container.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 22:18:28 +00:00
claude 51d0314c1c fix(mobile): detect touch devices with spoofed viewport width
Build and Deploy Verso / deploy (push) Successful in 10m17s
Tor Browser and Firefox with privacy.resistFingerprinting report a
desktop-sized viewport (~980px) even on a real Android phone. This made
window.matchMedia('(max-width: 767px)') return false, so isMobile was
always false on Tor, leaving the editor in side-by-side (horizontal)
layout instead of the expected vertical stack.

Fix: add a secondary check using `(pointer: coarse) and (max-width:
1024px)`. Touch hardware is not spoofed by fingerprinting resistance,
so this reliably catches phones and tablets regardless of the reported
viewport width. Applied to both getInitialLayout() and the live
isMobile state in main-layout.tsx.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 14:31:56 +00:00
claude 8fc71d677c Fix thumbnail quality and mobile vertical split layout
Build and Deploy Verso / deploy (push) Successful in 10m23s
Thumbnails: update the actual thumbnail endpoint (ConversionController.js
thumbnailFromBuild) to quality=90 and width=794. The previous fix targeted
ConversionManager.js which handles preview mode, not the thumbnail route
called by ThumbnailManager.mjs.

Mobile layout: move the isMobile guard before the stored-preference check
in getInitialLayout(). The autoSave race fix (build 274) stopped future
bad writes, but a stale 'flat' in localStorage was still being read on
every load, blocking the mobile check. Mobile now always starts in
verticalSplit regardless of any stored value.

CI: add node --check on all server-side .mjs files in the Dockerfile,
after source copy and before webpack compile, so syntax errors like the
escaped-backtick incident fail the build immediately.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 13:48:44 +00:00
claude 8a821bc91d Fix escaped backtick syntax error in EmailBuilder.mjs
Build and Deploy Verso / deploy (push) Successful in 12m49s
sed substitution literal-escaped the backtick and \${ in the
groupSSOReauthenticate subject line, producing invalid JS.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 13:13:05 +00:00
claude 33fa5986c8 Email rebranding, mobile filter alignment fix, minor UI cleanup
Build and Deploy Verso / deploy (push) Has been cancelled
Email:
- Replace Overleaf green palette with Verso teal (#2a9d8f) for CTA
  buttons, links, and logo text
- Rename force-overleaf-style CSS class to force-verso-style in all
  email body templates and layout
- Replace all user-visible "Overleaf" strings with settings.appName
  across email subjects, bodies, and sign-offs (EmailBuilder.mjs)
- Remove Overleaf CEO signature from onboarding email; substitute
  "The Verso Team"
- Fix utm_source=overleaf → utm_source=verso in onboarding links
- Point git token docs URL to local siteUrl instead of docs.overleaf.com
- Fix hard-coded Overleaf green (#0F7A06) in SSO disabled email link

Mobile filter chips:
- Switch from overflow-x:auto (still leaks through ancestor flex chain)
  to flex-wrap:wrap with flex:1 1 auto on each chip so all chips in a
  row grow to equal width — no overflow, clean alignment
- Toolbar becomes flex-column: chips on top, zoom below-right

Misc:
- Remove import_typst_file option from new-project menu
- Add interface_language to extracted-translations.json whitelist

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 12:56:34 +00:00
claude f629f6a50c Mobile polish: max thumbnail quality, tab-bar filter chips, fix vertical split default
Build and Deploy Verso / deploy (push) Successful in 10m12s
- CLSI thumbnails: bump to 794px/q90 (matches preview quality) for
  crisp display on 2x/3x phone screens
- Lumière filter chips → underline tab bar: single scrollable row with
  teal active indicator, no more wrapping alignment issues; zoom buttons
  separated by a vertical divider on the right
- Fix editor vertical split default on mobile: disable react-resizable-panels
  autoSaveId on mobile to prevent a stale collapsed PDF pane from firing
  onCollapse → changeLayout('flat') and overriding the verticalSplit
  default set by getInitialLayout()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 12:19:10 +00:00
claude c79ac23a15 Improve thumbnail quality and fix mobile editor default layout
Build and Deploy Verso / deploy (push) Successful in 10m18s
Thumbnails: increase CLSI thumbnail from 190px/q50 to 400px/q80.
At 190px/50% JPEG quality, images are noticeably blurry on 2x phone
screens (source needs 380px device pixels but source is only 190px).

Editor mobile layout: getInitialLayout() was returning sideBySide for
any stored 'split' preference (set from a desktop session), even on
mobile. sideBySide on mobile renders vertically via the isMobile check
in main-layout, but the stated default was still wrong. Now on mobile,
any stored value other than 'flat' maps to verticalSplit so the
top-bottom split is always the default; flat is preserved so a user
who explicitly chose editor-only keeps that preference.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 11:44:34 +00:00
claude 5aab716245 lumiere mobile: wrap filter pills instead of horizontal scroll
Build and Deploy Verso / deploy (push) Successful in 12m8s
overflow-x: auto only clips content when every ancestor has a definite
width; that constraint wasn't met so the pills were still expanding the
page. Switch to flex-wrap: wrap so pills flow onto multiple rows and
never cause horizontal overflow. Also allow the toolbar itself to wrap
so the zoom control can drop to a new row if needed.

Remove redundant sidebar hide rule — SidebarDsNav already has d-none
d-md-flex so it was never contributing to the overflow.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 11:07:16 +00:00
claude 90df7f5cc2 lumiere mobile: hide sidebar to fix 2x page width
Build and Deploy Verso / deploy (push) Successful in 11m15s
The project-list-wrapper flex row includes the sidebar (min-width:200px)
alongside the content area. On a 375px phone this totals ~575px, causing
the 2x horizontal overflow. Hide the sidebar on mobile — the filter pills
in the mobile toolbar already cover all its navigation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 10:50:37 +00:00
claude d12a39329b lumiere mobile: fix overflow, style ⋮ button, show tile actions on touch
Build and Deploy Verso / deploy (push) Successful in 16m58s
1. Page width overflow: stack lumiere-header as column on mobile so
   lumiere-header-actions (flex-shrink:0) no longer forces the container
   wider than the viewport. Search form and new-project button each take
   full width on their own line.

2. ⋮ button in compact row: override dropdown-table-button-toggle inside
   lumiere-compact-actions with Lumière-palette colours and tighter
   padding, replacing the table-context styling.

3. Tile view action strip: add @media (hover: none) rule so
   lumiere-card-actions is always opacity:1 on touch devices (phones have
   no cursor, so the hover-reveal pattern is unusable).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 10:00:50 +00:00
claude 6f419477df lumiere: fix mobile overflow + restore tile view with XS/M/L zoom
Build and Deploy Verso / deploy (push) Successful in 1m42s
Overflow fix: ActionsCell (up to 9 icon buttons) was assigned to the
`auto` column in the compact row, blowing out the row to ~300px.
Replace with ActionsDropdown (single ⋮ button) on mobile via
d-md-none / d-none d-md-flex, same pattern as the classic table.

Tile view: remove the isMobile force to compact (effectiveScale=0).
Add a mobile-only toolbar row (d-flex d-md-none) combining the filter
pills and a new XS/M/L zoom control:
  - XS (scale=0)  → compact rows
  - M  (scale=1)  → 2 tiles per row  (CSS: repeat(2, 1fr))
  - L  (scale≥1.35) → 1 tile per row (CSS: 1fr, class --mobile-1col)

The --lum-card-scale inline var is suppressed on mobile so CSS media
query controls thumbnail height (0.85 for 2-col, 1.3 for 1-col).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 08:54:30 +00:00
claude a004f21688 Fix mobile ergonomics on Lumière project list page
Build and Deploy Verso / deploy (push) Successful in 14m58s
Compact row (xs): wrap format/owner/date in a lumiere-compact-meta div
that uses display:contents on desktop (preserving the 6-column grid) and
switches to a 2-row grid layout on mobile — row 1: checkbox/name/actions,
row 2: format·owner·date. Actions are always visible on touch devices.

Filter access: add a horizontal scrollable filter-pill bar (d-md-none)
to the Lumière header so users can switch between All/Yours/Shared/Archived/
Trashed on phone without needing the desktop sidebar.

Also: hide zoom control on mobile (d-md-none), auto-force compact view on
mobile via useIsMobile hook, fix search form min-width overflow on xs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 08:22:27 +00:00
claude 065534819c Fix file tree refresh after convert and compiler sync on set-as-main
Build and Deploy Verso / deploy (push) Successful in 14m4s
- Convert: backend now returns parentFolderId+isNew; frontend calls
  dispatchCreateDoc directly so the new file appears without a page refresh
- Set as main: infer compiler from file extension and POST both rootDocId
  and compiler together; updateProject propagates the change to the
  editor settings dropdown and project list immediately

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 07:50:02 +00:00
claude b0b389dc4c feat: set-as-main for .typ/.qmd; fix compiler filter; fix ZIP import compiler
Build and Deploy Verso / deploy (push) Successful in 14m56s
Set as main document (context menu):
- canSetRootDocId used selectedEntityIds so .typ/.qmd files couldn't be
  set as main via right-click on an unselected file.
  Now computed locally from contextMenuEntityId (same pattern as convert)
  using isValidTeXFile which already covers .typ, .qmd, .tex etc.

Compiler filter (editor settings):
- docs?.find(id) could return undefined due to ID format mismatch,
  causing all engines to show as available for non-LaTeX projects.
  Added findInTree fallback so the root doc name is always resolved.

ZIP import compiler:
- Projects created from ZIP always got defaultLatexCompiler ('quarto')
  regardless of content.
- findRootDocFileFromDirectory now also searches for .typ and .qmd root
  files after finding no .tex file.
- ProjectUploadManager now infers the compiler from the root doc
  extension and sets it on the project after import.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 21:56:39 +00:00
claude bc131f6440 fix: convert items show for unselected files; add success toast
Build and Deploy Verso / deploy (push) Successful in 15m23s
canRename required selectedEntityIds.size === 1, so right-clicking an
unselected file hid the convert items even though contextMenuEntityId
was correctly set. Replace canRename with !fileTreeReadOnly for the
convert-specific gate, which is the actual write-access check needed.

Also add showExportDocumentSuccess so the user sees the warning toast
on successful conversion instead of silent nothing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 21:18:24 +00:00
claude ff7de70a61 fix: per-file convert — DuplicateNameError + first-click no-op
Build and Deploy Verso / deploy (push) Successful in 21m44s
Two bugs:
1. Converting when output already exists threw DuplicateNameError (400).
   Now overwrites existing doc via setDocument instead of failing.

2. Right-clicking an unselected file left contextMenuEntityId null,
   so the first click on Convert silently did nothing. Added
   contextMenuEntityId to FileTreeMainContext, set it on right-click
   and on the … button click; FileTreeItemMenuItems now uses it for
   the convert hooks rather than relying on selectedEntityIds.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 20:35:50 +00:00
claude dbb7899ca3 docs: clean up Alpha 3 section, add Known Issues
Build and Deploy Verso / deploy (push) Successful in 15m23s
Remove RevealJS thumbnails (redundant with Features section) and Upload
reliability (still unresolved). Add Known Issues section documenting
the large file upload timeout.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 19:58:52 +00:00
claude 7eeabc93aa fix: send empty JSON body on convert to avoid body-parser 400
postJSON without options sends Content-Type: application/json with no
body; body-parser's BodyParserWrapper intercepts the resulting
SyntaxError and returns 400 before the route handler runs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 19:58:48 +00:00
claude ac2315bc8e feat: per-file Convert in explorer menu + fix export success toast
Build and Deploy Verso / deploy (push) Successful in 9m50s
- Add "Convert to Typst (.typ)" in the file tree context menu for .tex
  docs, and "Convert to LaTeX (.tex)" for .typ docs. Clicking runs pandoc
  on the file content and creates the converted file in the same folder.
- New backend endpoint POST /project/:id/doc/:id/convert/:type that reads
  the doc from document-updater, runs pandoc directly, and creates the
  result via ProjectEntityUpdateHandler (file tree updates via socket).
- Rewrite the export success toast for typst and latex conversions: no
  more link to /contact, replaced with a plain warning that errors are
  expected (pandoc does not support all constructs).
- Add i18n keys: convert_to_typst, convert_to_latex,
  typst_export_feedback_message, latex_export_feedback_message (EN + FR)
  and all four to extracted-translations.json.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 15:18:31 +00:00
claude de22dcf87f docs: add mobile layout to Alpha 3 release notes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 14:58:28 +00:00
claude e82f5fbead docs: update Alpha 3 release notes and fix Overleaf description
Add top/bottom split view and bidirectional format export (LaTeX↔Typst,
LaTeX→DOCX/Markdown/HTML) to the Alpha 3 feature list. Fix the security
section which incorrectly referred to Overleaf as a LaTeX/Typst editor —
Overleaf only supports LaTeX.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 14:56:45 +00:00
claude d64365aa56 Add missing i18n keys to extracted-translations.json
Build and Deploy Verso / deploy (push) Successful in 14m28s
top_bottom_split_view, export_as_typst, and export_as_latex were absent
from the whitelist, so translations-loader.js stripped them from the
locale bundles at build time — causing raw keys to show in the UI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 14:46:31 +00:00
claude 2d2a85f06f Fix typst export 500, add export-as-latex for typst projects
Build and Deploy Verso / deploy (push) Successful in 10m4s
- clsi-nginx: allow hyphens in project-id regex — conversion IDs are UUIDs
  which nginx was rejecting, causing 500 on file download after conversion
- CLSI ConversionController/Manager: add 'latex' export type (typst→latex via pandoc)
- Web: add 'latex' to SUPPORTED_CONVERSION_TYPES
- Frontend: add Export as LaTeX button (visible only for typst projects)
- Fix visibility logic: export-as-latex shows for typst, export-as-typst shows for latex
- Add export_as_latex translation key (en + fr)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 11:55:41 +00:00
claude b545d08939 Fix four reported bugs: typst export 500, export shown for all project types, Lumiere tile button layout, i18n
Build and Deploy Verso / deploy (push) Successful in 15m7s
- ConversionController.js: add typst to CONVERSION_CONFIGS (missing entry caused 400→500 chain)
- export-project-with-conversion-button: hide button for non-LaTeX projects (typst/quarto) via compiler check
- project-list-lumiere.tsx + scss: revert lumiere-card-actions back inside .lumiere-card (put them back like they were)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 11:12:50 +00:00