Skip to content

feat(documents): add visual LaTeX authoring and reusable templates - #353

Merged
yaacovcorcos merged 39 commits into
ScientFactory:mainfrom
nattikahana:codex/source-derived-writing-canvas
Oct 20, 2026
Merged

yaacovcorcos merged 39 commits into
ScientFactory:mainfrom
nattikahana:codex/source-derived-writing-canvas

Conversation

@nattikahana

@nattikahana nattikahana commented Sep 23, 2026 •

Copy link
Copy Markdown

Landing state (19 October 2026)

This branch was rewritten on 19 October 2026, at the owner's request (the collaborator was informed beforehand), so it can land as it is. Its earlier history contained merges of non-main branches, which the upstream provenance check rejects. It is now 31 linear commits on main (e19ef4a0b2). The original 364 commits are kept unchanged under the tag archive/pr353-history (e261c9d600). This replaces the interim landing PR #589, which is closed.

The work itself, in three layers (each typechecks on its own):

  1. feat(desktop): the native file-publication helper, its build and packaging, and its desktop bootstrap.
  2. feat(documents): visual LaTeX authoring and reusable templates, with the server, shared packages and web editor together.
  3. docs: user and internal documentation and the seam manifests. The four generated thesis-responsiveness-*.html reports (27k lines) were left out; they stay in the archive tag.

Then 28 fixes, each one concern with the test that proves it, for what the paging batch broke or left untested:

  • Reading position:
    • The line at the top of the screen no longer jumps when text above it rewraps, or for a frame when formulas finish loading.
    • A deliberate scroll (reopening where you were reading, the caret followed) is no longer undone; on Linux, reopening landed 47 px off.
  • Run-in headings (\paragraph) stay on their paragraph's line.
  • Display formulas no longer grow when their editor opens; clicks on the line above a short display reach that line.
  • Containment: the block being edited is not contained, so its menus are positioned correctly.
  • Footnotes: a click in a footnote's editor places the cursor; paragraphs stay in order around a footnote; notes are only set on pages the planner draws.
  • Undo and redo: redo of a table insertion keeps its exact source change; undo of a paragraph split, typing and join restores the source exactly.
  • Typing into a new formula: keys typed while its editor is prepared (from the Math menu or the keyboard) are kept.
  • Closing mid-formula no longer throws.
  • Columns and titles: a word continuing in the next column keeps its hyphen; line starts after hyphenation are measured correctly; a two-column \maketitle spans both columns.
  • Source: a split paragraph's boundary spaces are owned once; the typing snapshot that failed to restore is kept.
  • Tests: expectations for paged layout and slow Linux runners, in test-only commits.

The final code is identical to the state verified on #589: 13 Codex review rounds, all LaTeX layout tests, all 13 required CI checks green, and benchmarks on the owner's thesis and 300 synthetic pages (no slowdown from the latest fixes).

Known limits, to address after landing:

  • Pages match the PDF only with the document's own fonts (for example, Arial on Linux).
  • Very long documents (300 pages) paginate late and type slower; thesis-length documents are fine.
  • Four rare issues already in this code (Codex round 5): concurrent recovery copies across windows, recovery gating after an interrupted native save, \includeonly inside \iffalse, and a second \input on one line when saving a personal template.
  • On Linux CI, the formula-loading check still sees a single 2.8 px move in about 1 of 5 runs (it was 14–102 px every run).
  • Some template 26/28 double-click tests flake on Linux independently of these changes.
  • LatexVisualEditor.tsx (about 10,600 lines) should be split into modules.

Before marking ready: a Windows save test (collaborator) and a hands-on pass in the dev app (owner).


LaTeX documents need a writing surface that stays connected to editable source, coordinated saving, and the compiled PDF. This PR adds a source-derived, paged Visual editor alongside Source, PDF, and Split, plus document creation and reusable templates.

Visual supports included chapters, scientific structures, references and bibliography, tables, figures, contextual editing, and source-preserving fallback for unsupported LaTeX. Local edits update immediately; compiled PDF remains authoritative for TeX-dependent typesetting. Personal templates retain included source and bibliography files. Compatible renames preserve active editors, source undo, recovery, and revision checks.

The writing toolbar and contextual footer preserve the innermost editing selection across menus. Nested math supports stable slot guides, centered empty-argument carets, bracket insertion, portable color export, current spoken labels, and the shared Cut/Copy/Paste/Select All context menu. Hebrew and mixed-language rendering use source-derived language, fonts, direction, and direction-aware tables, statements, and footnote separators.

Split selection and navigation

Double-clicking Visual or PDF reveals source only in Split and preserves ordinary word/cell selection. Each pane retains its own cursor and selection; the source selection remains until Source interaction, which also cancels delayed navigation. Returning to Visual or PDF preserves a manual source selection. Navigation uses native source selections instead of a persistent whole-line overlay, and the PDF gesture hint popup is removed.

Visual maps authored ranges inside table cells, captions, title fields, footnotes, description items, literal code, figures, framed content, and matrix/cases/aligned-equation cells. Native selection endpoints take precedence over stale pointer coordinates. Verified PDF word selections refine SyncTeX's mapped source line; ambiguous results retain line-level precision. PDF navigation retains Yaacov's compiled-source revision and freshness checks, including target session admission and cancellation of stale queued reveals.

Visual selections reuse the shared Ask in chat action. Read-only snapshots expose selected content, geometry, source ownership, and revision through the existing selection system. Citation reopening verifies the captured mapping. Shared source/chat support from #519 is merged into main and included here; there is no pending merge-order dependency on that PR.

Editing, saving, and reading

Markdown and LaTeX share revision-aware saving, retained recovery evidence, and file-version controls. Native Visual edits use lossless source edit plans. Guarded atomic publication, fresh compiler outputs, and captured-revision PDF export checks are retained.

Documents settings provides Auto-compile, On save (default), and Manual compilation modes. Automatic mode waits for settled saves and a pause in editing; explicit save/rebuild flushes pending edits. Optional compiler installation preserves the working default. AI builds preserve an already-open document's view and reading position without reactivating background tabs.

Visual prepares complete pages before revealing them, retains reusable geometry/formula results within the shared estimated 24 MiB budget, and protects active editing. Completed passive reading DOM is retained within a separate 64 MiB admission estimate, without an independent block-count ceiling. Equation activation keeps passive ink and numbering visible until the connected MathLive input has painted stable ink, then transfers focus while respecting newer user intent. Page remounts use a paint fence and preparation spinner, with five-second fallback/Retry. Worker preparation, bounded active math inputs, deferred mounting, interruptible pagination, incremental geometry, source columns, and medical-template content layouts are included. Visual and PDF share horizontal fit/scroll behavior.

Synchronization and verification

Published head 5f6342e468 includes alignment commit b87625d49d, which merges ScientFactory main 84a7e2663b into the PR's prior head 1fb52333c0. All existing editor and contributor ancestry is preserved; no rebase or force push. The eight incoming commits merge cleanly. The three overlapping files (UPSTREAM.md and the packaging script/test) preserve both native publication payload support and main's Windows-only WSL package exclusions.

  • Current combined head: server, desktop, web, contracts and scripts typechecking pass; whole-repository formatting and whitespace checks pass.
  • Focused lint on the integration changes completes with warnings; the wider touched-file lint run still fails on the existing raw ResizeObserver rule in latexVisualPaginationExtension.ts, also present at dcac0f447.
  • Before synchronization, the local selection candidate passed 32 Chromium browser checks and 20 focused unit tests, covering templates 26/28, RTL clicks, inner objects, and retained selections. Its browser fixture is now adapted to the native source-edit owner.
  • Yaacov's prior PR qualification records 19 Chromium page/opening/layout checks plus passing formatting, web typechecking, and production web build. Earlier integration evidence covers guarded publication, compilation settings, Hebrew, accessibility, packaging, and isolated desktop launch.
  • These prior test/build results apply to their recorded candidates. The focused scanner and Visual efficiency checks below qualify their respective corrections. Full tests and desktop smoke were not rerun for the combined head; it is not claimed fully qualified. The current production web build passes as recorded below.

Remaining qualification gaps include broader lint/full-suite failures, 21 previously reproduced Visual source-transaction unit failures, native acceptance for scrolling and every compilation mode. Complex macro/package layouts retain exact-source fallback; cross-file selections require separate quotes. Rapid undo in active math and the imported parser limit beyond ten matrix columns remain known limits. Personal templates do not collect every figure, logo, or local package dependency.

Existing synthetic matrix editing evidence, not fresh qualification of this head:

Matrix row-spacing comparison

matrix-source-roundtrip-demo.webm

Documentation impact: Updated — docs/user/latex.md, docs/user/export-conversations.md, docs/internals/scient-latex-visual.md, docs/internals/scient-latex.md, docs/internals/scient-latex-visual-authoring-proposal.md, docs/internals/scient-pdf-reader.md, docs/design/editing-commands-placement.md, docs/operations/local-dev-app.md, docs/reports/editor-computer-use-stress-20261012.md, docs/reports/latex-reading-retention-20261013.md, and UPSTREAM.md.

Implementation and synchronization: GPT-6.1-Sol through Scient's Codex harness; original contributor history retained. The PR remains draft with auto-merge disabled.

Declaration-scanner repair

Published correction: 7ec809b3f843b65479b5f71e0e4a649890ddd552, on top of 8474d9d8b2. The exact four-file delta is selectively integrated from f70edd24d6; its stable patch ID is unchanged. No other shared DocStart work was staged or included.

Comment/newline trivia before declaration stars or unbraced names now uses the same bounded trivia reader as argument scanning. DeclareDocumentCommand is recognized, so unused replacement text containing begin{comment} no longer hides the actual document body. The previously reported declaration-scanner blocker is resolved.

Current integrated correction: 21 focused unit cases pass, covering the full lexical suite and mounted body edit/undo/exact redo; 3 Chromium keyboard cases pass, covering typing, keyboard undo and exact redo for all three declaration forms. Whole web typechecking, four-file formatting, focused lint (two existing warnings), brand check and whitespace checks pass. The broader mounted-editor run was stopped in favor of the bounded regression checks; it is not claimed passing.

Standalone owner evidence covers 182 unit and 24 Chromium cases on its isolated snapshot. The complete suite, build and desktop smoke were not rerun for this correction; broader qualification limits above remain in effect. The review app was not restarted and the PR remains draft with auto-merge disabled.

Visual preparation efficiency and cold-scroll repair

Three focused commits are delivered on top of the scanner repair:

  • e35dbd5edb pauses idle cache admission at capacity, resumes on released space, uses constant-time background-first LRU, and retains terminal model/context fallbacks for oversized formula keys and explicit Retry.
  • 2c04471a6e retains one source anchor through cold preparation, fences same-top navigation/selection/source changes, reuses compatible compact top-level paragraph geometry, and reuses empty detached local measurement hosts.
  • 1fb52333c0 coalesces completion reconciliation, targets pending page invalidation, distinguishes mounted readiness from reusable cache availability, and keeps headers, footers and page numbers outside content-area masks.

Only the frozen 16-path incremental slice is included; current PR changes and the shared observeResize integration adapter are preserved. No shared DocStart staging, app lifecycle, native build or user-document action was performed.

Integrated delivery qualification: 48 Chromium cases pass (47 page/viewport/opening/pressure/snapshot/local-measurement cases plus the original large-scroll stress case); 25 focused unit cases pass (13 cache/font/retention/preview cases plus 12 pagination cases). Whole web typechecking, owned 16-file formatting, whitespace checks and production web build pass. Scoped lint remains red on two unchanged baseline raw ResizeObserver calls in latexViewport.ts and latexVisualPaginationExtension.ts, with two warnings; no new raw observer is introduced by this slice.

The previously reproduced 443px cold-scroll displacement is resolved in the integrated automated check, with its original one-pixel assertion unchanged. It is removed from the current known-failure list. This evidence does not establish every-hardware/native scrolling acceptance, native byte-changing persistence or compiler execution. The historical broader Visual unit gap and other qualification limits above remain disclosed; the complete suite and desktop smoke were not rerun.

Latest main integration qualification

Main alignment commit: b87625d49d535d9afd3a3319d5e1f1258d7b23db, with parents 1fb52333c0 and 84a7e2663b. Main was rechecked after push and remains at that revision. The merge imports Antigravity lifecycle/auth ownership and WSL runtime packaging/permission corrections; no LaTeX renderer source changed.

  • 403 affected tests pass, 16 skipped: packaging 94; changed provider regression suites 256/5 skipped; WSL and lifecycle contracts 53/11 skipped.
  • All five affected typecheck tasks pass: server, desktop, web, contracts and scripts. Existing Effect suggestions remain informational.
  • Whole-repository formatting, packaging/qualification-script lint, branding and whitespace checks pass.
  • Current server bundle (including service launcher) and production web build pass.

The review app and shared DocStart working tree were left unchanged. This merge was checked in the isolated delivery checkout. Full repository tests, whole lint, new desktop smoke, actual Windows/WSL packaging and live provider/native document acceptance were not rerun; earlier broader editor qualification gaps remain disclosed. Draft status and disabled auto-merge are retained.

Mounted reading retention and equation handoff

Delivered on top of the aligned main snapshot:

  • 2e856f1f9a retains completed passive DOM under a 64 MiB estimated admission budget, including idle completions and compatible cached blocks. Source, text, attributes and formula shadow markup contribute to the estimate; visited views take priority. The separate reusable cache remains 24 MiB.
  • 1116b4821f keeps original reading ink and numbering visible until stable painted MathLive readiness, then transfers focus without disconnecting the custom element. Pending input protection, identity/context/source/selection/readonly cancellation, newer focus intent, deadline and local Retry are covered.
  • 5f6342e468 records the policy and bounded qualification, including the report-only format correction.

Only the frozen 18-path incremental slice and its report-format patch are included. Current PR source, resize adapter and MathField accessibility/context-menu/selection changes are preserved. No shared DocStart index/HEAD/source edits, diagnostic files, native builds, app lifecycle or user-document writes are included.

Integrated qualification on this snapshot: 59 Chromium cases pass and 5 fail out of 64; the five failure titles match the supplied font-correct pre-slice control exactly, with unchanged assertions and no additional failure titles. Passing checks include inline/display typing and undo, held readiness, pending input protection, cancellation, Retry, newer focus intent, accessible labels, retention/return behavior and the unchanged original one-pixel scroll assertion. 15 focused units pass. Whole web typecheck, scoped 18-file formatting, whitespace checks and production web build pass. Scoped lint still reports the unchanged raw ResizeObserver in latexViewport.ts plus four existing warnings; whole lint is not claimed green.

The five existing browser failures concern display-whitespace prose input, plain/mixed-font local layout, prose input beside title/statements, and structural editing under a fallback overlay. They remain qualification gaps, alongside the earlier broader Visual unit gap. Full repository tests, native saving/compiler acceptance and live acceptance of this exact pushed head were not rerun.

The owner's independent 104-page reading surface and measured retention evidence are recorded in the bounded retention report; those observations qualify that separate source snapshot, not native acceptance of this pushed branch. Memory figures are admission estimates, not physical RAM or total heap. The review app remains available; draft status and disabled auto-merge are unchanged.

Clipboard source preservation

Copying a LaTeX selection containing references, citations, links or footnotes now preserves supported commands in the text clipboard instead of dropping atom nodes. The extracted helper shares the source adapter's command admission, retains plain/math/table behavior, and keeps normal cut/paste/undo ownership.

Validation at d9464132e6: 150 focused units and 15 Chromium cases pass, including browser Copy/Cut/Undo shortcuts, readonly and nested selections, Hebrew roundtrip/reopen, 2,000 command atoms, and 30 no-math copy/paste/undo cycles. Whole formatting/lint and combined build pass; 46 type tasks are covered after two isolated setup/resource retries; isolated desktop launch smoke passes. Full recursive tests remain red outside this scope, and two representative existing editor failures match the unmodified base; the broad affected-editor rerun was interrupted after stalling. The smoke verifies launch only, not native clipboard/IME acceptance. DocStart and its live source/profile were untouched.

Documentation impact: Updated docs/internals/scient-latex-visual.md. Implementation/review: Codex harness, gpt-6.1-sol.

Renderer and pagination follow-up

Updated in e261c9d600d504833bd05cf822243cc21ba669a4: renderer coverage and PDF-calibrated typography, mixed-column/long tables, image crop/rotation, landscape pages, paging reuse, selection and typing-recovery improvements. The legacy dashed PAGE BREAK marker is removed in every layout state; authored page commands remain source-backed flow controls.

The integration preserves the existing main merge and integration fixes. Shared recovery storage retains the reconnect-aware transaction helper and adds the Visual input stores. Seven unrelated dirty local files were excluded.

Prior isolated browser comparison: thesis 13/13 pages; heavy template 54/54 pages, all 69 photo instances loaded, no source fallback blocks. Exact float placement and page-spanning frame borders remain approximate; TeX drawings require explicit preview. These are prior comparison results, not a fresh acceptance run against this integrated head.

For this delivery, formatting, focused recovery-code lint and diff-whitespace checks passed (lint retains existing schema-hoisting warnings). Test suites and full integrated desktop checks were not run, following the owner's test-run instruction. Review template 29's cropped/rotated gallery, landscape sheet, repeated longtable bands and final-row edit; then check cross-environment selection, typing/undo and save/reopen.

The earlier fork review PR contains the detailed implementation, discarded approaches and remaining qualification checklist. Its old delivery/base instructions are superseded: the code is now included directly in #353.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL labels Sep 23, 2026
@nattikahana
nattikahana changed the base branch from codex/latex-visual-20260920 to main September 25, 2026 13:41
@yaacovcorcos

yaacovcorcos commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

Review update and direction — 2026-09-29

This replaces the review posted on 2026-09-26. It covers two things: a recheck of the branch at abe28c1b3e, and the direction for the LaTeX editor as part of a shared document-editing system. Your next assignment will follow in a separate comment.

Thank you for the quick and thorough response to the first review. The source-fidelity work in 1cd0c51390 addresses the most important problems.

1. Recheck at abe28c1b3e

The same reproductions as before were rerun through the branch's own document-model functions.

Fixed

Earlier finding Now
Paragraphs containing ~ could not be edited Edits are accepted, and ~ is preserved
--/--- rewritten to Unicode dashes; \textit rewritten to \emph Both preserved as written
Multi-line paragraphs collapsed onto one line Line breaks preserved
Adding a table row dropped cell formatting \textit{A} survives
Table labels escaped as text \label{tab:x_z} preserved
Insertions into an included file silently omitted root declarations Refused instead of breaking the build
The default table needed booktabs The default is now plain
indexGeneration was a required contract field Now optional
MathLive loaded for Source-only users The Visual view loads on demand
About 2,900 lines of unused PDF-overlay code, a duplicate revision store, and a personal-notes doc Removed
The Source view used a separate editor It uses the shared file editor again

Still open

  1. The page-layout editor writes more than it changes. Changing only the font size on \documentclass[11pt]{article} still adds letterpaper, \usepackage[margin=1.8in,letterpaper]{geometry}, \setlength{\parindent}{1.5em}, and \setlength{\parskip}{0pt plus 1pt}, which pins the class-default margins and spacing. Only the setting that changed should be written.
  2. % scient-hidden-author: is still written into the preamble when an author is hidden. Scient state should not be stored as comments in the user's source.
  3. The MathLive bundle patch remains. The choice of math input is now an explicit decision in the shared plan (see below), so please don't invest further in the patch until that is settled.
  4. Builds on save. The branch still changes the default from rebuilding on save to rebuilding only on request. That is a product decision, and it is tracked separately.
  5. Launcher files. See section 2.

2. Correction: the development-launcher changes

The first review listed the desktop development-launcher and backend PID-handoff changes under this PR's scope. They came from earlier commits on the #326 branch that this branch was built on; you did not write them. Those fixes are now handled on main by #377. Please remove these files from this branch; your Windows bundled-renderer default (scripts/dev-runner.ts and its note in local-dev-app-windows.md) can stay:

  • apps/desktop/scripts/{dev-app-process,dev-electron,electron-launcher}.mjs and their tests;
  • apps/desktop/src/app/{DesktopConfig,DesktopEnvironment}.ts and apps/desktop/src/backend/{DesktopBackendConfiguration,DesktopBackendManager}.ts, with their tests;
  • apps/server/src/desktopDevelopmentBackendPidHandoff.ts, its test, and its import in apps/server/src/bin.ts;
  • the launcher changes in scripts/local-dev-app.mjs, its test, and docs/operations/local-dev-app.md.

3. Direction: one document-editing system

The LaTeX editor is becoming part of a shared document-editing system with the Markdown editor. The plan is in the design record in #373 (docs/internals/scient-document-editing.md). It is still a proposal, and comments there are welcome. It builds on the accepted rich Markdown editor contract, which remains the rulebook for source preservation, sessions, verification, and performance budgets.

In short:

  • One system, two formats. One document-editing system is developed through improvements to the Markdown editor and tested early against LaTeX, with no waiting until the end to see whether LaTeX fits.
  • Two modes of one Scient editor. The header, toolbar, footer, menus, and common interactions are the same. The document canvas differs only where that helps the writer. Simulated pagination becomes an optional capability, and page positions come from the built PDF when its mapping is current.
  • Several ideas from this PR are adopted for both editors: the footer, the Documents panel, the Insert menu, document checks (from Review), zoom, the symbol catalog, table presets, the shared table picker, and the keyboard scopes and writing-command list.
  • Shared foundations, extracted through Markdown:
    • one document session for every file editor, replacing separate draft and save paths;
    • a format-adapter contract that verifies the smallest affected region, not the whole document on every keystroke;
    • a floating object-editor layer;
    • shared editor chrome.
  • ProseMirror directly is the recommended base for document editors. It will be validated by a thin LaTeX path on the shared core before it is settled.

What this means for this PR now:

  • It stays one PR. Splitting it is not the focus; quality is.
  • Ownership. Yaacov sets the direction for the LaTeX work. You continue on it through concrete assignments, each with a bounded area, the shared interface to build against, and acceptance criteria. The first one will follow shortly.
  • Where effort pays off. The LaTeX logic (the translator, construct coverage, layout profile, templates, symbol catalog) carries forward regardless of framework.
  • Please avoid:
    • new Tiptap-specific building blocks (React node views through Tiptap, or reliance on StarterKit behaviors), since these would have to move later;
    • new draft or save mechanisms;
    • changes to shared modules (the keyboard system, math input, scient/writing/, the Markdown editor) without agreeing them first.

How this was checked: the branch's document-model functions were run on small inputs at abe28c1b3e, and the changed files were read. No visual review was done. CI has not run on this draft.

@yaacovcorcos

yaacovcorcos commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Hands-on review of the Visual editor

@nattikahana, I spent a session using the Visual editor at 78faa21915 on a real paper: a short heat-equation article with theorems, proofs, labelled align blocks, cases, matrices and \eqref. Below is what I found, with the likely cause of each item in the code.

First, what works well:

  • Saving is careful. An edit to one paragraph changed exactly the bytes I typed and left every theorem, proof and remark byte-for-byte identical.
  • Paragraphs, headings, inline maths and standalone equations render close to the PDF.
  • Refusals are honest. When the editor refuses a block, it keeps the source exact.

The numbers below are for discussion; please reply with them.

Where to start: the small fixes marked (small fix). Items marked (after the architecture notes) touch how blocks are projected and saved. We're writing a short architecture note that will affect them, so please hold those until it's posted.


Editing

1. Equations with \label can't be edited (after the architecture notes)

Any equation containing \label, \tag, \notag or \nonumber becomes a grey "Preserved LaTeX" code block. In real papers, most equations that are referred to have a label.

  • Cause: parseLatexVisualMathSource refuses them (latexVisualDocument.ts:527), so the maths editor never drops a label. The intent is right.
  • Direction: keep labels and tags outside what the maths editor sees and restore them on save, the way \section{…}\label{…} already survives edits.

Labelled align shown as Preserved LaTeX

2. Theorem, proof and remark bodies show a stripped preview and can't be edited (after the architecture notes)

$u_0 \in L^2(\Omega)$ shows as u_0 L^2(), t \ge 0 as t 0, \frac{1}{2}\frac{d}{dt} as "12 d dt", and Poincar\'e as its code. The file is safe: the field is locked, and edits elsewhere don't touch these blocks.

  • Cause: parseScientificEnvironment stores previewText(bodySource) (latexVisualDocument.ts:1433, :670) and the view displays it as if it were the content. Any maths makes the body read-only.
  • Direction: see 6. A body the editor can't handle should render its maths, or show its source, never a cleaned-up preview. Longer term, project the body as real paragraphs and maths.

Theorem and proof bodies with maths stripped

3. Locked blocks point to an "Edit LaTeX" button that doesn't exist (small fix)

Clicking the block does nothing either (LatexVisualEditor.tsx:1235).

  • Direction:
    • clicking the block, or a small button on it, opens that block's source;
    • the label names the real action;
    • a locked block shows its content rendered (as it will print) with a small "source only" mark, rather than as a code listing.

4. An outside change can delete the title on the next key press (small fix, please prioritise)

  1. Type in the body and let it save.
  2. The file changes from outside (an agent, or the Source pane).
  3. The selection lands on the title block, and the next key replaces it, removing \maketitle from the file.
  • Cause: after installing the new projection with the selection restored, LatexVisualEditor.tsx:3413 calls editor.view.updateState(EditorState.create({ schema, doc, plugins })) without a selection, so it falls to the start of the document.

  • Direction:

    • keep and clamp the selection there;
    • never leave a whole-block selection on the title unless the user chose it;
    • add a regression test for "outside change, then type".

    A scratch browser test that reproduces it: repro.

5. Editing an editable theorem or abstract rebuilds the whole block (after the architecture notes)

Found by calling the translator directly, not yet through the app. A body edit re-serializes the block from its attributes (serializeScientificPreview, latexVisualDocument.ts:1908), even though sourceMeta.bodyRange is computed:

  • a \label moves after \begin;
  • wrapping and indentation collapse;
  • \emph{…} around an abstract, or a \textbf{} title, is dropped.

Related, in the translator:

  • adding bold to an italic word writes one \textbf{} per letter;
  • inserting a block right after a display equation inserts a blank line, which splits the paragraph that continued below it;
  • splitting a paragraph turns \textit into \emph and -- into "–".
  • Direction: replace only the edited range and keep everything else, as the table path already does.

Rendering

6. The Visual view should either render a block faithfully or show its source, never an approximation (after the architecture notes)

Today there are three ways a block is shown: rendered, source (the grey block), and a previewText string. The third produces 2 above: text that looks like content but isn't. Also, \eqref shows the label name ([eq:bc]) where the PDF shows "(3)". Showing the number, with the label on hover, would be nicer, but isn't required.

  • To keep this true over time:

    • a small set of real documents: theorems, proofs, labelled equations, accents, tables, figures;
    • a test that fails if any block is displayed from a preview string;
    • a side-by-side check against the PDF whenever the translator changes.

    The capability table in the assignment below should say, per construct, whether it renders, whether it's editable, and what happens otherwise.

Visual view: preserved align, gaps before inline maths, stripped theorem body

Page appearance

7. Gaps before inline maths and \eqref chips (small fix)

Each formula and chip starts with about 22px of extra space. That's the paragraph's text-indent (scient-latex.css:867), inherited by the inline-block wrappers, which then indent their own first line. text-indent: 0 on .scient-latex-visual-inline-math and .scient-latex-visual-command fixes it (measured in a browser). The gap is visible before ν in the screenshot above.

8. Space after headings is about twice the space between other blocks (small fix)

.scient-latex-visual-document is a vertical flex column (scient-latex.css:796), so a heading's bottom margin and the next block's top margin add up instead of collapsing. Normal block layout gives LaTeX-like spacing.

9. Remove the underline under the title while editing it (small fix)

scient-latex.css:1583.

Title with focus underline

Top bar and header

10. The paragraph-style button changes width, so the whole bar jumps (small fix)

The button is as wide as its label: "Proof" and "Enumerate-Resume" have very different widths (scient-latex.css:2808, label at LatexVisualEditor.tsx:4273).

  • Direction:
    • a fixed width that fits the common styles (about 12 characters); only the word inside changes;
    • longer names get "…", with the full name in the tooltip;
    • the bar itself never changes shape while writing: the Insert menu keeps the same items, with the ones that don't apply disabled; the shortcut hint appears outside the bar; controls that don't fit move into a More menu instead of disappearing (the Markdown editor's DockOverflowRow does this).

Paragraph style button showing Proof

11. The paragraph-style menu mixes three kinds of things

It holds about 35 entries:

  • styles of the current paragraph;
  • lists, which are also in the list button next to it;
  • blocks you insert: Theorem, Proof, Abstract, Bibliography, code, verse.

Several names come from LyX and are unclear to most users. For example, "Address" and "Right Address" are flushleft/flushright, "LyX-Code" is lstlisting, and "Enumerate-Resume" continues the numbering. The Unnumbered group repeats every heading with a *. Proposition is missing, and "Remarks" is plural.

  • Direction, matching the Markdown editor:
    • the style menu holds Text, the heading levels and Quote;
    • a "Numbered" switch on the selected heading replaces the Unnumbered group;
    • lists live only in the list button;
    • everything else moves to Insert, grouped and with plain names.

Paragraph style menu

12. Status labels repeat each other and wrap (small fix)

"Rebuild required" (scientLatexSurfaceModel.ts:446) and the "PDF needs rebuilding" chip (ScientLatexSurface.tsx:1056) come from the same condition. The app's File status also shows the save state, so saving is reported twice. .scient-latex-status wraps onto a second line.

  • Direction:
    • one save indicator, the shared one;
    • the build state on the button itself ("Rebuild PDF");
    • a header that stays on one line.

Header status cluster

13. One Export menu instead of separate "Export ▸ Word" and "Export PDF" buttons

Use the same Export menu the Markdown editor has in its More menu (MarkdownPdfExportMenuItems). For LaTeX, PDF stays tied to the latest build.

14. The "Review before export" dialog can go

Its first two checks cover this file only; the build already reports repeated labels and unresolved references across the whole document. The third check only finds Scient's starter text. It opens as a blocking dialog even when there's nothing to report.

Review before export dialog

15. Zoom field (small fix)

  • The value keeps two decimals (LatexVisualZoomControls.tsx:22), and the preset-list arrow takes room inside the 52px box, so "79.36" is cut to "79.3".
  • The buttons sit on white pills (scient-latex.css:499).
  • Direction: whole percentages, like the PDF reader (pdfReaderModel.ts:24), and one zoom control shared with it.

Zoom control Zoom control on hover

16. Pressing Update PDF makes the whole view jump (small fix)

The panes have fixed sizes, so the likely cause is the header wrapping onto a different number of lines while building: a spinner, the label change, the Cancel button, and the stale chip going away. Recording the top of .scient-latex-content before, during and after a build confirms it. A one-line header (12) should fix it.

PDF building

17. When the PDF builds: decided (updated 2026-10-01; the full statement is rule 6 of the architecture note)

By default, typing doesn't request a build. The PDF updates when asked, or when the user has clearly stopped.

Trigger What happens
The PDF comes into view No PDF yet, or out of date: build. Current: show it
Cmd+S Save now. Then build if the PDF is in view. Nothing handles Cmd+S today
Update PDF Always available, whatever the view or setting
Idle: the PDF is in view and out of date, and for about 45 seconds the user has neither edited nor interacted with the PDF Build once
"As I type", only when chosen in the setting Also build after a 2–3 second pause in editing, while the PDF is in view
  • "In view" means PDF mode, or Split with the PDF selected as its preview. Split with Visual doesn't count.
  • A setting, "Update the PDF automatically", has three choices: when idle (default), as I type, never. "Never" turns off only the two timed rows; opening the PDF, Cmd+S and Update PDF work the same under every choice.
  • Interacting with the PDF means scrolling, zooming, selecting, searching, or moving the pointer over it. A resting pointer doesn't count.
  • After a timed build, successful or failed, the next timed build needs a changed input.
  • Every build runs in the background and swaps in only when ready:
    • the old PDF stays usable, with no blank screen and no spinner over the page;
    • only a successfully published PDF replaces it, in one step, keeping zoom and rotation, the same page number (limited to the new page count) and the same scroll position;
    • at most one build waits in line;
    • a failed build changes nothing on screen except a small mark on the button.

Who does what: we write the scheduler and the setting, and you connect the LaTeX surface to it once we have agreed the interface. Until then, builds stay explicitly requested, so there is nothing to build for this item now. Please don't add a separate scheduler in the branch. What is yours now is item 18, and Update PDF saying why when it can't run instead of staying disabled.

18. The PDF banner says "The latest build failed" when the PDF is only out of date (small fix)

ScientPdfReader.tsx:637 shows it whenever bindingStatus === "stale", which in this branch is the normal state between builds. It also appends the server's "Sources changed. Rebuild to update the PDF." in a warning colour.

  • Direction:
    • out of date: no banner, just "Rebuild PDF" on the button;
    • a real failure: a small mark on the Update PDF button, with the details and the log behind a click.
  • Generally: messages short, light, and not alarming unless the user needs to act.

PDF banner

Footer

19. "N source-only blocks" opens the source of the block at the cursor, not a source-only block (small fix)

It also switches to Split (LatexVisualEditor.tsx:4169). It would be clearer if it stepped through the source-only blocks and opened each one's source in place.

Footer source-only blocks

Dialogs

20. Writing shortcuts: the title and inputs run to the card's edges (small fix)

WritingShortcutsDialog.tsx:40 puts them directly in DialogPopup. Using DialogHeader and DialogPanel gives the standard padding.

Writing shortcuts dialog

Found in the code, worth checking

  • Recovery can bring back an older copy. "Restore" replaces the whole document from the browser-storage draft without checking the file's current version (visualDrafts.ts:26), which can undo agent edits made since. Also, a refused edit is checkpointed before it's accepted (LatexVisualEditor.tsx:2988), so it can be offered later as recovered text.
  • A refused save reverts what the user typed (LatexVisualEditor.tsx:2912). A refused edit should stay in the editor, marked unsynced, with Keep and Discard.
  • Paragraphs with accents (\'e) or a % comment become fully locked blocks (parseInline, latexVisualDocument.ts:357).

(after the architecture notes: saving and recovery will move onto one shared document session per file.)

Ours, no need to build in this PR

These are shared with the Markdown editor, and we'll do them as shared work (#373):

  • Documents card: replaced by one small "type (.tex/.md) + name" control.
  • File rename from the breadcrumb: the Markdown rename button, extended to .tex.
  • A Documents page in Settings, including the choice of when the PDF updates automatically.
  • The shared editor frame: top bar, footer, status, Export, find, zoom.
  • Where the document outline lives: an open question for both editors.

Underlying causes, for context

Most items come from four patterns. The architecture note will address them:

  1. One attribute serves both display and write-back, and for blocks the editor partly understands it holds a lossy preview.
  2. Several block types are rebuilt from attributes instead of patched in place.
  3. The editor is rebuilt on outside changes instead of receiving a mapped transaction, which loses the selection, history and focus.
  4. The header and footer are built around their content (they wrap and change with state), and some shell pieces are LaTeX copies of shared ones.

Thanks for all the work on this. Happy to discuss any item here.

yaacovcorcos added a commit that referenced this pull request Oct 1, 2026
Answer the design record's hard questions for the LaTeX Visual editor and say
how the structural items from the #353 review should be built: what is shown,
how edits are written back, how outside changes are adopted, undo, refused
edits, saving and building, and interpretation context.

Record three decisions in the design record: the owner's side owns the
complete LaTeX persistence migration; one existing cross-file edit kind stays
as a stated exception; and the build policy (nothing rebuilds while writing or
reading, with an idle rebuild and a setting).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@yaacovcorcos

Copy link
Copy Markdown
Contributor

Architecture note, and an update to item 17

@nattikahana, thank you for the two fixes (39619117d7, 6d5cb5e8c4). Two things to read:

1. The architecture note the review promised is here: docs/internals/scient-latex-visual-architecture-note.md (part of #373).

It covers how the structural items should be built. In short:

  • Saving and recovery are ours. We will move LaTeX onto the shared document session. Please don't add or rework draft, journal or save mechanisms meanwhile.
  • Your labelled-equation fix is the pattern to follow: keep what the editor doesn't own, and patch only the changed range.
  • Seven rules, each tied to a review item:
    1. show content without dropping or changing its meaning;
    2. edit only the range that changed;
    3. keep the user's interaction across outside changes;
    4. keep undo only where it is proven safe;
    5. never lose refused input;
    6. keep saving and building as separate facts;
    7. one source per file.
  • For statement bodies, please start with a thin prototype on one statement type before changing the node structure broadly.
  • The framework question (Tiptap or plain ProseMirror) is not decided here. Repairs are fine; please talk to us before adding substantial new Tiptap-specific infrastructure.

The table near the end of the note maps each review item to who does it and when.

2. Item 17 (when the PDF builds) is updated in the review above. Nothing rebuilds while typing. The PDF builds when it is opened and out of date, on Cmd+S while it is visible, on request, and once after about 45 seconds without edits or interaction with the PDF. Every build runs in the background and swaps in only when ready.

Questions and disagreements are welcome, here or on #373.

yaacovcorcos added a commit that referenced this pull request Oct 1, 2026
The request the #353 review and the architecture note refer to: a capability
table for the LaTeX translator and the translator-level tests that back it.
Editor- and saving-level tests, and the timing measurements, stay on the
owner's side.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@yaacovcorcos

Copy link
Copy Markdown
Contributor

Contributor request, and a request to realign with main

@nattikahana, two things, whenever you have a moment.

1. The contributor request is now written down. The review and the architecture note both mention it: docs/internals/scient-latex-visual-contributor-request.md (part of #373). Could you please have a look?

In short, it asks for evidence, not fixes:

  • a capability table for the translator: for each construct, how it is shown, what can be edited, and what happens otherwise;
  • translator-level tests that back the table, with the cases that fail today marked as expected failures.

It comes after the small fixes from the review. The editor- and saving-level tests and the timing measurements are on our side. If anything in it looks wrong or too much, please say so.

2. Could you please realign the branch with the latest main? It is currently 99 commits behind and has conflicts. Doing it before the next round of fixes should keep the conflicts small.

One update to the architecture note since yesterday: rule 6 (when the PDF builds) now says that we write the build scheduler and the setting, and you only connect the LaTeX surface to it once we have agreed the interface. So there is nothing to build for review item 17 for now. Items 17 and 18 in the review above are updated to match.

Thank you again for the quick fixes.

@yaacovcorcos

Copy link
Copy Markdown
Contributor

@nattikahana, a short supplement to the hands-on review. We went through the translator again at 6d5cb5e8c4 and found a few cases worth adding. None of them changes the direction; they make the existing rules measurable.

One thing first: we pushed a small commit to your branch. 9aa5337b61 removes the file name next to the Source / Split / Visual / PDF switch, because the breadcrumb directly above already shows it. Please pull before your next push.

How these were checked

Each case below was reproduced by projecting a small source, applying one edit through applyLatexVisualDocumentChange (or planVisualProjectEdit for the project case), and reading the result. They are translator results, not app sessions. The exact sources, edits and arguments are in the test file at the end of this comment.

Cases that change the source in ways the user didn't ask for

1. Formatting wraps every character. Bold on the word Hello:

\textbf{H}\textbf{e}\textbf{l}\textbf{l}\textbf{o} world

Italic does the same (\emph per character). The marks are applied one UTF-16 unit at a time (latexVisualDocument.ts:2865).

2. The same path breaks characters outside the basic plane. Bold on Hi 😀 you wraps each half of the emoji separately. The result contains unpaired UTF-16 surrogates, and a UTF-8 round-trip replaces them with replacement characters. We checked the translator's output; we did not run a full save to disk.

3. A paragraph appended after an \input chapter is written into the root. With

% main.tex                      % chapter.tex
Root intro.                     Chapter paragraph.

\input{chapter}
\end{document}

appending a paragraph after "Chapter paragraph." produces, in main.tex:

\input{chapter}
Appended paragraph.

The chapter file is unchanged, although it was the preferred destination. With \include the same edit is refused at the page boundary, which is the safe outcome.

4. Changing a figure's caption rewrites the figure. \centering is added and the indentation is removed; only the caption text should change.

5. Editing a listing moves its options into the code. Changing print(1) to print(2) in \begin{lstlisting}[language=Python] gives:

\begin{lstlisting}
[language=Python]
print(2)
\end{lstlisting}

6. Core maths commands add a package. Adding \vec{x}, \frac{a}{b}, \hat{x} or \mathcal{A} to a formula adds \usepackage{amsmath}. These are core LaTeX. In an included chapter that also makes an ordinary edit depend on a root change.

All six pass the round-trip comparison, because the rewritten source projects back to what the editor asked for. That is why the architecture note now asks for three checks (rule 2), and for an inserted block to name the file it belongs to (rule 7).

Tests

  • Running pnpm exec vp test run src/scient/latex in apps/web on our machine, 24 tests fail with your files at 6d5cb5e8c4: 17 in LatexVisualEditor.test.tsx, 3 in latexVisualDocument.test.ts, 3 in scientLatexSurfaceSeam.test.ts, and the one in LatexVisualEditor.browser.test.tsx. We have not run the whole suite, so please treat this as a pointer and check on your side.
  • CI has not run on the branch. The workflow runs are waiting for approval on our side; we will sort that out once the branch is realigned with main.

One item that is ours

The change to shared maths input (a new blank equation inserts nothing where it inserted {}) fails two Markdown tests in the branch, in editorAdapters.test.ts and mathInput.test.ts. Markdown on main is not affected. We will resolve it and propose the change to you.

Statement bodies

Item 2 of the review (theorem, proof and abstract bodies shown with the commands stripped, and locked) turned out to be the most visible gap when reading a real paper. We would like a first step earlier than planned: show a locked body with its maths rendered, read-only. Making bodies editable stays where it was.

Order we suggest

  1. Realign with main (as in the earlier comment), and the small fixes from the review.
  2. The contributor request: the capability table, now per operation, and the tests, now including the six cases above.
  3. The fixes: formatting and Unicode first, then insertion after an \input chapter, then listings, figure captions and package requirements. Each with its test.
  4. Statement bodies rendered read-only.

Saving, recovery and the PDF build scheduler stay with us, as before. The recovery repair mentioned in the note is in review on our side and we will propose it to you before it lands on the branch.

Thank you for the two fixes after the review; the labelled-equation approach is the pattern the note asks the other blocks to follow.

Reproduction file (10 tests; 9 fail at 6d5cb5e8c4, the \include one passes)
// Reproductions for the supplement to the #353 review. Place next to
// latexVisualDocument.ts (adjust the two import paths) and run with
// `vp test run <this file>`. Each test states the expected result: nine fail
// at 6d5cb5e8c4; the `\include` refusal test passes.
import { describe, expect, it } from "vite-plus/test";

import { assembleVisualProject, planVisualProjectEdit } from "../latex/latexProjectVisual";
import {
  applyLatexVisualDocumentChange,
  projectLatexVisualDocument,
} from "../latex/latexVisualDocument";

type Node = {
  type?: string;
  text?: string;
  marks?: { type: string }[];
  attrs?: Record<string, unknown>;
  content?: Node[];
};
const tex = (body: string, preamble = "") =>
  `\\documentclass{article}\n${preamble}\\begin{document}\n${body}\n\\end{document}`;
const find = (node: Node, test: (candidate: Node) => boolean): Node | null => {
  if (test(node)) return node;
  for (const child of node.content ?? []) {
    const hit = find(child, test);
    if (hit) return hit;
  }
  return null;
};
/** Project `source`, change the projected content, and return the new source. */
function edit(source: string, change: (content: Node) => void): string | null {
  const projection = projectLatexVisualDocument(source);
  const content = structuredClone(projection.content) as Node;
  change(content);
  return applyLatexVisualDocumentChange(source, projection, content as never)?.source ?? null;
}
const paragraph = (content: Node) => find(content, (node) => node.type === "paragraph")!;

describe("formatting", () => {
  it("wraps a run once", () => {
    const result = edit(tex("Hello world"), (content) => {
      paragraph(content).content = [
        { type: "text", text: "Hello", marks: [{ type: "bold" }] },
        { type: "text", text: " world" },
      ];
    });
    expect(result).toBe(tex("\\textbf{Hello} world"));
  });

  it("keeps a character outside the basic plane intact", () => {
    const result = edit(tex("Hi 😀 you"), (content) => {
      paragraph(content).content = [{ type: "text", text: "Hi 😀 you", marks: [{ type: "bold" }] }];
    })!;
    expect(new TextDecoder().decode(new TextEncoder().encode(result))).toBe(result);
    expect(result).toBe(tex("\\textbf{Hi 😀 you}"));
  });
});

describe("blocks", () => {
  it("changes only a figure's caption", () => {
    const figure = (caption: string) =>
      tex(
        `\\begin{figure}[ht]\n  \\includegraphics[width=0.5\\textwidth]{plot.pdf}\n  \\caption{${caption}}\n  \\label{fig:plot}\n\\end{figure}`,
        "\\usepackage{graphicx}\n",
      );
    const result = edit(figure("Old caption"), (content) => {
      find(content, (node) => node.attrs?.kind === "figure")!.attrs!.caption = "New caption";
    });
    expect(result).toBe(figure("New caption"));
  });

  it("keeps a listing's options in its opening line", () => {
    const listing = (code: string) =>
      tex(
        `\\begin{lstlisting}[language=Python]\n${code}\n\\end{lstlisting}`,
        "\\usepackage{listings}\n",
      );
    const result = edit(listing("print(1)"), (content) => {
      const node = find(content, (candidate) => candidate.attrs?.environment === "lstlisting")!;
      node.attrs!.body = String(node.attrs!.body).replace("print(1)", "print(2)");
    });
    expect(result).toBe(listing("print(2)"));
  });

  it.each(["\\vec{x}", "\\frac{a}{b}", "\\hat{x}", "\\mathcal{A}"])(
    "adds no package for %s",
    (formula) => {
      const result = edit(tex("Let $y$ be given."), (content) => {
        find(content, (node) => node.type === "latexInlineMath")!.attrs!.tex = formula;
      });
      expect(result).toBe(tex(`Let $${formula}$ be given.`));
    },
  );
});

describe("a paragraph appended at the end of a chapter", () => {
  const files = (command: string) =>
    new Map([
      [
        "main.tex",
        {
          contents: `\\documentclass{article}\n\\begin{document}\nRoot intro.\n\n\\${command}{chapter}\n\\end{document}\n`,
          revision: "r1",
          truncated: false,
        },
      ],
      ["chapter.tex", { contents: "Chapter paragraph.\n", revision: "c1", truncated: false }],
    ]);
  const append = (command: string) => {
    const document = assembleVisualProject("main.tex", files(command));
    const at = document.source.indexOf("Chapter paragraph.") + "Chapter paragraph.".length;
    const next = `${document.source.slice(0, at)}\n\nAppended paragraph.${document.source.slice(at)}`;
    // The chapter is passed as the preferred destination.
    return planVisualProjectEdit(document, next, files(command), "main.tex", "chapter.tex");
  };

  it("is written to the chapter or refused for \\input", () => {
    const plan = append("input");
    if (plan.error !== null) {
      expect(plan.changes.size).toBe(0);
    } else {
      expect([...plan.changes.keys()]).toEqual(["chapter.tex"]);
      expect(plan.changes.get("chapter.tex")).toBe("Chapter paragraph.\n\nAppended paragraph.\n");
    }
  });

  it("is refused for \\include (passes today)", () => {
    const plan = append("include");
    expect(plan.error).not.toBeNull();
    expect(plan.changes.size).toBe(0);
  });
});

@yaacovcorcos

Copy link
Copy Markdown
Contributor

@nattikahana, one more note, on a single topic: why so much of a real paper can't be edited in Visual, and what we think the fix is.

Reading a paper in the editor, most of what gets in the way is the same thing seen from different sides: a theorem or an abstract locks as soon as it contains a formula, a paragraph with Poincar\'e or a link becomes source, and one such item turns a whole list into source. We measured it construct by construct at 6d5cb5e8c4, and also found a few edits that rewrite source they didn't touch (changing one word in a list item writes the whole list again).

We wrote up the cause and a direction here: Editing inside blocks. In short:

  • Cause: the block ledger is top-level, rich blocks are atoms with string fields, and one unsupported piece rejects its container.
  • Direction: a recursive source ledger, real content inside containers, unsupported content as a small locked piece, and patches to the smallest safe range. It builds on what you already have: lists and quote are projected recursively today, tables already record cell ranges, and paragraphs already patch narrowly.
  • Steps: six, each with a bounded result. The first two are small enough to judge the approach: lists and quote keep their children's ranges (no change on screen), then one theorem body with real content.

It doesn't change the order in the earlier comments: realigning with main, the request, and the source-preservation fixes still come first. The new cases are added to the contributor request as tests.

This is a larger piece of work than anything else we have asked, so please read it as a direction to discuss, not a task list. We would like your view on the approach and on the split of work before you start; in particular, we agree the patch contract with you before step 0.

What is locked today, measured at 6d5cb5e8c4
Content Result
Paragraph with bold, italic, inline maths, ~, dashes, a custom macro in maths Editable
Heading, lists (nested, with maths), equations (labelled equation and align, cases, matrices), plain quote Editable
Paragraph with \cite, \ref, \eqref or a footnote Editable around a fixed piece
Abstract, theorem, proof, description list: plain text Fields editable
Abstract with italic or maths Locked
Theorem or proof with inline maths, display maths, a reference or bold Locked
Description list with maths Locked
Table: plain or bold cells, rules, p{…}, float with caption, tabularx, longtable Cells editable
Table with maths in a cell, or \multicolumn Locked
Figure with one image, caption and label Fields editable
Figure whose caption has maths Locked
Paragraph with an accent command, a % comment, \href, or an unknown command Source only
A list with one such item, or with \item[a)] Source only
Figure with two images or TikZ; center; minipage; \bibliography{…}; unknown environments Source only

@yaacovcorcos

Copy link
Copy Markdown
Contributor

@nattikahana, thank you for yesterday's three commits. We went through 9aa5337b61..8bbae54dc0 with the same translator checks as before, and it is a real step forward.

What we measured as fixed

  • Statement bodies are real text. In our theorem and proof fixtures, prose, maths, references and bold are editable, and an isolated word edit preserves the neighbouring source, the title and the label.
  • Formatting gives one wrapper per run, and characters outside the basic plane stay intact.
  • Accent commands in a paragraph or a list are editable.
  • Unsupported blocks show their exact source, with an in-place source editor.
  • A refused edit is no longer stored as recovered source. That change to the checkpoint timing is right, and we keep it.
  • Tests. Our runs of pnpm exec vp test run src/scient/latex src/scient/math/input in apps/web reported 26 failures at 6d5cb5e8c4 and 6 at 8bbae54dc0.

Three things to align

1. Automatic PDF builds. Thank you for wiring opening the PDF, Cmd+S and save-before-build; those can stay as the interim. A manual build can still leave out unapplied input or an unsaved included chapter; we will handle those checks in our save barrier. One part doesn't: the build 2.5 seconds after typing stops. The decided default is that typing doesn't build (rule 6 of the architecture note); that timer is the opt-in "as I type" choice, which needs the setting. Could you remove the timed build for now and keep the rest? The scheduler with the setting, the idle rule and the save barrier across included files is ours and will replace useLatexAutoBuild.

Small things we noticed while reading it:

  • a Rebuild clicked while a save is in progress is dropped;
  • the build key uses the open file's revision, so a change in an included chapter can be missed;
  • the labels read "Building PDF?" and "Exporting?", probably a lost ellipsis.

2. Recovery. The revision check on Restore helps, and these remain:

  • a typing snapshot from an older file is still put back automatically;
  • the editor is read-only while the prompt shows;
  • Restore can replace Source typing made at the same revision;
  • two windows can remove each other's record.

Our repair for these is ready on top of 8bbae54dc0: one line in the footer, writing continues, and the file changes only from a comparison. It replaces the alert and canRestoreVisualDraft, and keeps your post-acceptance checkpoint. We will propose it here before it lands on the branch. Please leave recovery as it is until then.

3. Shared files. Exposing flush is useful; it returns true when there is no session lease, which we will address in saving. Stale generated PDFs lost their reader notice, and the shortcuts dialog layout changed for Markdown too. Could generated-PDF readers outside LaTeX keep an equivalent stale-PDF indication? For further changes to shared files, please agree them with us first.

Edits that still rewrite source they didn't touch

Each reproduced at 8bbae54dc0. Please add exact-source regression tests for them, and state the limits per operation in the capability table, with the test that backs each:

Edit Result
One word in one list item, at top level or inside a theorem The whole list is written again: indentation removed, \textit becomes \emph
Theorem prose changed and a paragraph added after it in the same step The theorem is written again: [\textbf{Main result}] becomes [Main result], line breaks joined
A figure's caption \centering added, indentation removed
Code in lstlisting[language=Python] The option moves into the code
\vec, \frac, \hat, \mathcal added to a formula \usepackage{amsmath} added
A paragraph appended at the end of an \input chapter, with the chapter as the preferred destination The planner writes it to the root file

Step 0 of Editing inside blocks covers the first row: retained child ranges for lists and quote (a word edit inside a quote also still turns an untouched 3--5 into 3–5). The second row needs one more thing: a surviving theorem keeps its own syntax when the same step changes the number of top-level blocks.

We keep your statement-body work. The order we would suggest: agree the patch contract, then step 0, then bounded unsupported pieces inside statements (one % comment or unknown command still turns the whole theorem into source). The translator checks the intended edit, the unchanged source outside the allowed ranges and the protected syntax inside them, and refuses when a narrow patch fails.

Smaller items

  • Unapplied text in the block-source editor has no recovery when the file is closed; keeping it belongs to our recovery work. Its stored position also isn't mapped when content before it moves, which can hide the editor; let's agree that mapping with the adapter hook.
  • Field drafts stored for a statement's body, title or label before this change are no longer offered; that migration belongs to our recovery work.
  • The heading toolbar still acts while the editor is read-only.
  • Removing the document review dialog, with its duplicate-label and missing-reference checks, is fine by us.
  • The toolbar no longer offers the next-line description style; existing source that uses it is still recognised.

The two Markdown maths tests still fail; that fix is ours and is ready with the recovery repair.

Realigning with main is still the first thing we need from the earlier list.

@yaacovcorcos

Copy link
Copy Markdown
Contributor

The edit contract we said we would propose is written down: The edit contract.

In short: the planner hands the file's session a list of exact patches and the version it planned on; the session applies all of them or none and says why. The code and tests are on #415 (packages/scient-document/src/sourcePatch.ts, lease.applyEdit), so you can read and run it.

It is a proposal. Tell us what doesn't fit the planner and we change it before step 0. Two things are not in it yet and are listed at the end of the section: several files in one edit, and context revisions.

@yaacovcorcos

Copy link
Copy Markdown
Contributor

Thanks for the new commits, and for merging main in. We went through 58483b47ca, 8c6006af4f, f298e8ee02 and the two documentation commits, at a9403b27dc.

What we saw working

  • All of LatexVisualEditor.test.tsx passes now, including the block-source test that failed at 8bbae54dc0.
  • Typing no longer schedules builds. Opening a missing or stale PDF requests a build when prerequisites permit, and Ctrl/Cmd+S requests one while the PDF is visible. That matches the build policy.
  • The stale-PDF notice is back for generated PDFs outside LaTeX (showStaleNotice, on unless a surface turns it off). Thank you.
  • From reading the code, the changes to button, menu, dockChrome and the table-size menu are additive and leave Markdown's defaults as they were.

Still open from the last review

Our reproduction file gives the same results at this head:

  • a figure caption edit rewrites the whole figure;
  • lstlisting options move into the body;
  • \vec, \frac, \hat and \mathcal add amsmath;
  • a paragraph appended at the end of an \input chapter is planned into main.tex;
  • editing one list item normalises the list's indentation and changes an untouched 3--5 to 3–5; editing one word inside quote makes the same dash change.

The exact-source tests for these are not in yet.

Two further source-preservation cases, both reproduced

  1. A title with a comment or a line break. With \title{Energy % keep⏎estimate}, changing "Energy" to "Mass" gives \title{Mass estimate}: the comment and the line break are gone. (At 8bbae54dc0 the comment became printed text instead.)
  2. Inserting a block after a sized run. With {\small A 3--5} B, inserting a table before B writes {\small A 3–5}: the run that was not edited changes.

Both come under the same rule as before: source an edit is not about stays byte-identical, and where that can't be done the edit is refused.

Shared PDF reader

Moving navigation and search into scient/writing reads fine. Four tests in pdfReaderSeam.test.ts fail at this head; one of them already failed at 8bbae54dc0. Of the three new failures, the page-count test reads a CSS rule that moved to documentReaderControls.css, the responsive-controls test looks for markup that moved to DocumentReaderControls.tsx, and the producer-seam test matches "LaTeX" in the new comment. Could you update those tests together with the move?

Tests

pnpm exec vp test run src/scient/latex src/scient/math/input src/scient/pdf src/scient/writing src/scient/markdownEditor/ui in apps/web: 433 tests, 9 failing. Four are the reader tests above, two are the shared maths tests, and three are the browser layout tests. The Theorem and Remark layout tests time out; it looks as if they still click the entries directly while the entries now sit in a submenu.

From our side

  • The recovery repair and a small maths fix are being moved onto this head. We'll recheck the two maths tests once they are there.
  • The edit contract is waiting for your comments whenever you have them.

@yaacovcorcos

Copy link
Copy Markdown
Contributor

We pushed two commits to this branch, on top of 5fa34211eb. Please pull before you continue, so we don't diverge.

e9fd3665f3 — recovered work is never applied unasked

  • Unsaved work found when a document opens is offered as one line in the Visual footer. The file changes only after the writer opens the comparison and chooses Use recovered; Discard removes the copy. Nothing else removes or applies it.
  • It replaces the "Recovered unsaved source" alert block and canRestoreVisualDraft. Work that can't be converted stays readable and copyable; in a document made of several files it can be compared and copied, not applied.
  • In your new footer the line sits in the free space of contextControls, before LatexContextTools. It has a three-part form when the footer is wide, a one-action form below 700 px, and a dot when not even one word fits. The comparison opens above the footer, above an open search bar, and above the References panel.
  • Files: new visualRecovery.ts and LatexVisualRecovery.tsx with their tests; changes in LatexVisualEditor.tsx (startup, the revision a checkpoint is stamped with, the unsynced Discard, the footer slot), LatexProjectVisualEditor.tsx and ScientLatexSurface.tsx (when the stored copy may be retired), visualDrafts.ts, visualTyping.ts, the stylesheet and the two LaTeX docs.

916ad7c534 — a blank equation started in Markdown keeps its empty group

A small fix in the shared maths input. With it the two shared maths tests pass.

Checks at this head

vp test run --project unit src/scient/latex src/scient/math/input: 345 pass. In the wider set we ran before, the remaining failures are the four pdfReaderSeam.test.ts tests and the three browser layout tests from the last review. The change was reviewed in code and exercised in the app (opening, switching views, reloading, an outside edit during the comparison, rapid clicks, a long document, a multi-file document).

Three things we noticed on the way, all small

  1. The branch still has its own versions of the dev-launcher files, and with them a dev app built from this branch starts without a window on our machines. Could you take main's versions? They are apps/desktop/scripts/{dev-app-process,dev-electron,electron-launcher}.mjs and their tests, apps/desktop/src/app/{DesktopConfig,DesktopEnvironment}.ts, apps/desktop/src/backend/{DesktopBackendConfiguration,DesktopBackendManager}.ts, apps/server/src/bin.ts, scripts/local-dev-app.mjs and its test; apps/server/src/desktopDevelopmentBackendPidHandoff.ts and its test are not on main.
  2. Two of CI's checks would stop on files in this branch: apps/web/src/scient/latex/LatexTableInsertDialog.tsx is reported as unused, and apps/server/src/scient/latex/LatexProjectIndex.ts is not formatted.
  3. In Split at its narrowest, the References panel's Close and search controls run past the pane.

If you change the recovery code or the footer slot it sits in, a short note here is enough; we'll keep that part working with your changes.

@yaacovcorcos

Copy link
Copy Markdown
Contributor

@nattikahana — Yaacov would like to go over where the editing commands should live in both editors (Markdown and LaTeX Visual) before more features are added to the top bar.

The full list, command by command, with where each is today and a proposed home, is here:
https://github.com/ScientFactory/scient-desktop/blob/claude/editing-commands-placement-20261003/docs/design/editing-commands-placement.md
(draft PR #450)

The proposal is one shared bar, Undo Redo | Text ▾ | Lists ▾ | Insert ▾ | Math ▾ | Document ▾, with formatting (bold, italic, inline code…) moving into Text › Formatting. The decisions still open are at the end of the document.

yaacovcorcos added a commit to nattikahana/scient-desktop that referenced this pull request Oct 3, 2026
…head a9403b2) into the bars pilot

Conflicts resolved:
- LatexVisualEditor.tsx: imports unioned; the candidate's recovery bar and the
  collaborator's symbol picker both stay in the footer; the collaborator's
  writing-row menus are taken as they are, and the pilot's decisions are
  re-applied on top (style and list buttons show the current choice, icons per
  heading level, Markdown's names, no Indent/Outdent entries).
- scient-latex.css: both sides appended.
- FilePreviewPanel.tsx: the candidate's session-aware file status kept.
- ScientFileFreshnessControls.tsx: the collaborator's popover width variant.
- docs/user/latex.md: the collaborator's guide with the candidate's recovery wording.
- pnpm-lock.yaml: regenerated.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
yaacovcorcos added a commit to nattikahana/scient-desktop that referenced this pull request Oct 3, 2026
Brings claude/document-bars-pilot-main-20261003 (the reviewed bars pilot)
onto claude/pr353-main-sync-20261003 (PR ScientFactory#353 aligned with main, with
his newest work: references panel, macros, theorems, pagination).

Resolution: the editor follows the pilot's writing row exactly (Undo/Redo
· Text · Insert · Math · Lists · Document; formatting inside Text; Link in
Insert, Cmd+K kept), hosted header, shared footer and find & replace; his
features stay, with References under Document and his insert entries in
Insert. Menu labels keep the no-trailing-dots rule. Document settings
stays one card and gains his packages-and-macros summary at the bottom.
Recovered work sits directly in the shared footer.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@yaacovcorcos

Copy link
Copy Markdown
Contributor

@nattikahana This branch now holds everything in one place, as you asked. I pushed on top of your latest commit (ea6fd4f338 → f1b7baf86f) as a plain fast-forward: none of your commits were changed or removed.

Before you continue: pull first (git pull on codex/source-derived-writing-canvas). If you have local commits you haven't pushed, pull and merge once; tell us if anything conflicts. Then keep working and pushing here as usual. This PR stays the single branch for the Visual editor.

What was added on top of your work

  • Latest main (up to feat(timeline): roomier trace rows with short file names #459).
  • LaTeX saving through the shared document session (the same one Markdown uses): Source and Visual edit one working copy; saves are revision-checked; pending fields settle before save, build and export; recovered work is offered for comparison and never applied unasked. Your recovered-work repair was merged with this, keeping the stronger version where both existed.
  • References panel on the document session: a .bib file has one saver, shared by its tab and the References panel. A reference counts as saved only when it is in the saved file. A kept draft stays with its own bibliography. Unrelated duplicate or malformed entries don't block confirmation.
  • Your rich table cells connected to the shared writing pieces: Text → Formatting and Cmd+B/I/E work in cells and follow Scient's shortcut settings; clearing cells also clears their pending input; the footer counts a cell selection.
  • The document-bars redesign, decided with the owner (see docs/design/editing-commands-placement.md, which replaces docs(design): where every editing command lives (for discussion) #450):
    • one header row (status, search, view switch · sidebar, page, zoom · Rebuild);
    • writing row Undo/Redo · Text · Insert · Math · Lists · Document, with formatting inside Text and Link in Insert;
    • shared footer (object options · position and word count);
    • shared find and replace;
    • Document menu with Title & authors, Document settings (now one card, with your packages-and-macros summary), References, Find and replace, Export.
  • Also: rename any open file from its name in the header, and a Download menu (source, PDF, Word).

Your newest features are all kept: algorithms, TikZ, colour boxes, long tables, literal code, page layouts with running headers and footers, the editing target, compiled bibliographies, engine selection.

Three things for you to look at

  1. LatexBuildService.test.ts › "refuses a document that names an engine the lane cannot drive" fails on ea6fd4f338 itself. It still expects an XeLaTeX request to be refused, but your engine selection now compiles it. I updated the two latexEngineGate tests (wording only) and left this one for you, since rewriting it means deciding how the build pipeline should behave.
  2. Your two latexVisualReview tests that failed on ea6fd4f338 were outdated (abstract body, inline-math cells). They now follow your new model and still check preservation; please confirm that matches your intent.
  3. LatexRealEngine.test.ts needs the SyncTeX helper staged locally; it fails without it on every branch.

All of this was reviewed by Codex in several rounds before pushing. Web 10,321 tests, desktop and packages pass; server passes except items 1 and 3.

🤖 Generated with Claude Code

@nattikahana
nattikahana changed the base branch from main to codex/t3-sync-ca7df394ed-20261007 October 7, 2026 07:49
@yaacovcorcos
yaacovcorcos deleted the branch ScientFactory:main October 7, 2026 16:38
@yaacovcorcos yaacovcorcos reopened this Oct 7, 2026
@yaacovcorcos
yaacovcorcos changed the base branch from codex/t3-sync-ca7df394ed-20261007 to main October 7, 2026 19:59
yaacovcorcos added a commit that referenced this pull request Oct 9, 2026
…erging main

Merging main added main's copy of the header button's move and recovery
callbacks next to #353's; the unguarded copy let a new LaTeX document move
from the header. Keep the Markdown-only one and a single recovery step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
yaacovcorcos and others added 8 commits October 19, 2026 08:13
Selecting a formula, including one just inserted from the Math menu, prepares
its editor. Until it is ready, a document-wide listener swallowed printable
keys, so the first characters typed straight after inserting a formula were
lost ("x+1" became "+1"). Preparation got slower with the paging batch on a
cold start, which made this visible: the section-to-math test lost its first
key in about one run in three of its file, and never before the batch.

When the formula was selected without a click (inserted from a menu or reached
with the keyboard), characters typed while waiting are now kept in order and
typed into the field key by key once it is ready; MathLive's batch typedText
would process a backslash twice. A plain Backspace removes the last kept
character and never deletes anything in the formula. Composing keys are not
kept, and cancelled or timed-out admission discards what was kept. After a
click, keys are still not kept, as before: the click's caret and selection are
relayed to the field and kept text would race them.

Tests hold readiness and check that kept keys produce exactly what typing the
same keys into the ready editor produces (a command, brackets, scripts, spaces
and a corrected letter), that Backspaces delete nothing in the formula, that a
composing key and keys after a click leave the formula unchanged (Codex review
rounds 4 to 7). Known limit: Scient's environment completion (\beg to \begin{})
is not applied to kept keys.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
While an edit raced the page planner, a footnote's line could be placed on a
page the plan had not drawn a sheet for, and positioning its note read a
missing sheet and threw. The pass was abandoned with an uncaught error (about
one run in ten of the Hebrew editing test, intermittently). A note whose page
has no sheet now keeps its previous position until the next pass. The exact
cause of the missing sheet was not reproduced with instrumentation in place;
the Hebrew tests then passed in 20 of 20 runs without an error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Under a loaded full-suite run, reopening a document was checked three frames
after opening while its pages were still being placed (51 px; CI saw 48 px),
and typed prose beside a display took longer than the 1 s default poll to
publish. Reopening now checks where reading resumes once the pages are laid
out; under CPU load in isolation it lands within 2 px both before and after.
The prose poll waits up to 5 s. Test-only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… count off Linux

The reopening test scrolled to its reading position before the document's font
had loaded and before pages were laid out, so it remembered a position in an
unfinished layout; on CI the reopened document then landed 48 px away. It now
scrolls once the font and pages are in place, as a reader does.

Template 26 sets its Hebrew text in Arial, which macOS and Windows ship and
Linux runners do not: there a substitute with other metrics lays it out to 5
pages instead of the PDF's 4, and measuring whether Arial is installed is
unreliable (the Latin-only substitute reports it present). The PDF page count
is checked off Linux; the sheets must match the plan everywhere. Test-only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The document-closing test counted Chrome's benign "ResizeObserver loop
completed" notice, which Linux emits more often, as an error; it now ignores
only that notice. Test-only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Linux CI the test changed the inserted matrix while its editor was still
being admitted: the field was the hidden staged copy (admission pending, not
focused), and edits to it were dropped, so the source never showed the spaced
rows. Logged on Linux in draft PR ScientFactory#591. A user cannot reach the staged copy;
keys typed meanwhile are kept by the admission. The test now waits until the
field has focus before editing it. Test-only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A formula's rendering lands in its shadow root inside a preview task,
and the batch made those tasks user-visible priority. Nothing corrected
the reading anchor until the next ResizeObserver delivery, so a cold
open could paint one frame with the reading line moved by formulas
finishing above it (14-102 px), and skipped blocks with stale size
hints switched height in between.

Queue the existing one-per-task microtask correction from completed
preview and load events too, not only from resize delivery. Pending
preview events are ignored; completions in one task share a correction.

"formulas finishing above the reading area do not move it" failed alone
since the batch and passes again. Benchmarks show no cost (300 pages
and the thesis, interleaved runs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A correction measured against the anchor taken before a deliberate
scroll (a restored reading position, the caret followed) undid that
scroll whenever layout or a formula published before the next frame
took the new reading line. On Linux the reopen restore landed 47 px
off this way; the correction from the previous commit can run in that
window too.

The anchor records the scroll offset it was taken at. When a
correction finds the area scrolled since, the reading line is a new
one: drift of the old line is corrected only when reading moved down
(that content lies above the new line too); a move up corrects
nothing, since it cannot be told apart from a range clamped by content
shrinking above; and the new line is taken at once. Content changing
between the lines, or above both on a move up, in that same frame is
absorbed; it previously undid the whole scroll.

The new test covers a scroll then growth above, in the same task and
from the scroll event, and a clamp at the end; it fails without the
change (scroll undone) and with a rebase-only variant (clamp moves the
text 208 px). It waits for layout to settle and measures text rather
than the paper, shrinks the frame itself as the stage observer does,
and checks the clamp only when text is in view at the end: Linux CI's
fonts can end the document on a blank stretch of the last page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@yaacovcorcos
yaacovcorcos force-pushed the codex/source-derived-writing-canvas branch from e261c9d to 6d239c6 Compare October 19, 2026 05:16
@yaacovcorcos

Copy link
Copy Markdown
Contributor

@nattikahana this branch (codex/source-derived-writing-canvas) was rewritten today: e261c9d600 → 6d239c6425, 31 linear commits on main, so this PR can land itself. The description's top section explains what is in it. Nothing is lost: the old head is kept as the tag archive/pr353-history, and #589 is closed in favour of this PR.

Please don't pull or push this branch as it is in your local clone. A plain git pull would merge the old history back in, and a push from the old history would undo the rewrite. Instead:

git fetch origin                       # the remote for your fork
git switch codex/source-derived-writing-canvas
git branch backup/pr353-before-rewrite # keeps your local state, just in case
git reset --hard origin/codex/source-derived-writing-canvas

If you have commits that were never pushed, cherry-pick them onto the new branch afterwards (git cherry-pick <sha> from the backup branch), or tell us and we'll do it. The same applies to any Codex or agent worktree on this branch: reset it before it pushes again.

🤖 Generated with Claude Code

Yaacov and others added 2 commits October 20, 2026 11:11
Bring ScientFactory#353 up to current main, including the T3 alignments ScientFactory#593 and ScientFactory#607
(React Compiler migration, browser test project), ScientFactory#596, ScientFactory#601, ScientFactory#603-ScientFactory#606,
ScientFactory#608-ScientFactory#610 and ScientFactory#567.

Composition:
- Workspace writes: ScientFactory#353's mutation service keeps main's known-workspace
  boundary and Git-folder write denial. Every write path (write, create,
  binary write, delete, rename, retained replace/remove, inspect) is
  authorized first, and the mutation lock re-checks protected paths on
  each non-history resolution. Test layers provide KnownWorkspaces.
- Web: conflicted files follow main's React Compiler patterns (no manual
  memo, refs synced in layout effects, render-phase resets, effect
  events, try/finally in module helpers) with ScientFactory#353's behaviour on top,
  including ScientLatexSurface, the PDF reader, the file preview panel,
  save coordinator, workspace refresh, Pandoc tool and source PDF preview.
- Tests moved to main's browser project are ported (createRoot, async
  act); ScientFactory#353's deletions of the Markdown rename button and its seam test
  are kept.
- pnpm-lock.yaml regenerated with pnpm install.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop manual memoization and render-time ref access in the editing
target, table selection, source recovery, document compiler, reference
state and pinch-zoom hooks; port their tests' render captures. The PDF
reader's Save Copy keeps calling props.actions.saveCopy, which its seam
test checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Yaacov and others added 2 commits October 20, 2026 11:53
… React Compiler

Main lints every web file against the React Compiler rules: no manual
useMemo/useCallback, no ref access during render, no synchronous state
updates in effects. Bring the remaining LaTeX authoring files in line:

- Drop manual memoization from 22 controls and dialogs; keep a stable
  identity with useStableCallback only where an effect or child needs it.
- LatexVisualEditor: update handler refs after commit, track the rendered
  title and checkpoint as state, measure fit width from the DOM, and derive
  source notices from live editor state.
- LatexReferencesPanel: keep `apply` and `updateFile` stable so publishing
  the bibliography catalog cannot re-render the panel in a loop.

Also align two tests with main after the catch-up: the asset URL test no
longer expects `waiting: false` (main only sets it while refreshing), and
the workspace refresh test keeps the real atom library in its mock because
the refresh hook now reaches the app atom registry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The zoomed paper is scaled with a transform, so its resize observer never
sees a zoom change. The frame used to resize because its sync callback was
rebuilt for every zoom; as a stable callback it no longer is, which left
zooming out with blank scroll space below the document. Sync on zoom
explicitly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@yaacovcorcos
yaacovcorcos marked this pull request as ready for review October 20, 2026 09:05
Yaacov and others added 3 commits October 20, 2026 12:11
useStableCallback infers its argument tuple, so a parameter typed only by
its default value became unknown and failed the web typecheck.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Without useMemo the editor's command context changed identity on every
render. The layout effect that refreshes math reading context depended on
it, so it re-ran while a math field was being admitted and cancelled the
staged editor before <math-field> mounted: clicking an equation no longer
opened it. Refresh only when the preamble (mathSetupSource) changes, which
is everything the context and its macros derive from.

Also give the workspace mutation test the retention settings ScientFactory#353 made
required; main added the test without them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The refresh hook now reaches Markdown persistence, which imports
confirmProjectFileQueryData and setProjectFileQueryData. The test's
narrow mock lacked them, so the file failed to load.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Han column cut: CI now installs Playwright's system dependencies only
  when needed, so the CJK fallback font can differ. Forty short paragraphs
  could then all end exactly at column boundaries. One continuous Han
  paragraph forces a cut between Han characters on any font; the test
  still requires that cut and no drawn hyphen.
- Native word selection: wait until loading ends, fonts are ready, math
  previews have published and paged layout has settled before
  double-clicking, and click the word's own fragment rather than its union
  box, which a wrapped word can centre over unrelated text. All selection,
  mapping, scroll and handoff assertions are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@yaacovcorcos
yaacovcorcos added this pull request to the merge queue Oct 20, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 20, 2026
@yaacovcorcos
yaacovcorcos added this pull request to the merge queue Oct 20, 2026
Merged via the queue into ScientFactory:main with commit 8d7e8b9 Oct 20, 2026
48 checks passed
yaacovcorcos added a commit that referenced this pull request Oct 20, 2026
…ublish

Since #353 the applier writes only through native file publication and
refuses elsewhere, so on Linux CI both review-to-applier tests failed with
WorkspaceFileMutationUnavailableError. They now skip unless
nativePublicationHost(), as the applier's own suites do. The file-layer
agreement test treats an unavailable publication as "not refused": the Git
folder refusal comes before any write.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants