Codebase map
Where to start. If you want the why behind the structure, read
architecture.md first; this document is the index.
File and line counts are left out on purpose: they drift with every commit.
When a size matters, measure it (git ls-files <dir> | wc -l, wc -l <file>).
1. Top-level directories
| Directory | What it is |
|---|---|
src/ |
The React 19 / TypeScript frontend. Split by domain: a few shell files at the root, the rest under app/, agent/, animated-icons/, assets/, build/, canvas/, components/, editor/{,latex,markdown,spreadsheet,board,insert,comments,presentation}/, history/, hooks/, lib/, onboarding/, overleaf/, papers/, pdf/, platform/, project/, settings/, styles/, telemetry/, terminal/, trellis/, and locales/. See §3. |
src-tauri/ |
The Rust host: the .rs sources under src-tauri/src/ — large areas are a short x.rs map beside an x/ directory of parts, command handlers are in ipc/ — plus tauri.conf.json, capabilities, icons, and LaTeX project templates. |
literature-worker/ |
The public literature proxy Worker (lattice-literature, OpenAlex and Crossref). Own package.json and vitest config; see public-literature-service.md. |
scripts/ |
Build and maintenance tooling: version bump, size budget, the pre-commit source file size check (check-file-size.mjs, run by .husky/pre-commit through lint-staged), i18n coverage, runtime staging and signing (Synara, Open Slide), third-party notices, perf fixtures, dev bootstrap (setup-dev.sh), Overleaf live verification. Shared helpers are in scripts/lib/ (util.mjs, codesign.mjs, runtime-prune.mjs). |
tools/ |
Development-only code outside the app bundle. tools/perf-bench/ is the interaction benchmark's page, which answers the app from the in-memory backend in tools/mock-backend/ (a stand-in for Tauri's internals, the open project, builds, text tasks, notes and the citation and label indexes); tools/web-demo/ is the website's interactive demo over the same backend (the tutorial with its PDF compiled ahead of time in content/, built by scripts/web-demo.mjs and published to the site's demo/ by .github/workflows/web-demo.yml); and tools/open-slide-runtime/ is the Open Slide presentation server staged into the app by scripts/prepare-presentation-runtime.mjs. Typechecked and linted with the app. |
design-system/ |
The design system's exported copy: tokens.css, tokens.json and DESIGN.md, generated from the Paper file "Lattice design system" by export.mjs and never edited by hand; screens/ draws that file's App screens page from the running app. See its README.md. |
docs/ |
This directory. See README.md. |
evals/ |
JSON fixtures for scripts/agent-quality-eval.mjs — pass/fail transcripts for the agent's research behaviour. |
research-env/ |
The research environment (Python, standard library only): verifiable LaTeX tasks for training and evaluating paper-writing agents, generated from seed papers (including src-tauri/templates/), packaged as Harbor tasks with a self-testing verifier. Not part of the app; the app binary's env mode (src-tauri/src/research_env.rs, with latex/pinned.rs for its build) runs the same tasks through Lattice's own tools. See its README.md. |
patches/ |
pnpm patches: @danfessler/trellis@0.2.0, @pdfslick/core@4.0.2, @pierre/trees@1.0.0-beta.6, @tiptap/react@3.33.0, pdfjs-dist@6.5.438 and prosemirror-view@1.42.6. |
public/ |
Static assets copied into the build: polyfills.js (the pre-module WebKit built-ins, minified on build and also imported by the PDF.js worker, src/pdf/pdf.worker.ts) and the pdf.js cmaps and standard fonts (public/pdfjs/ is regenerated by vite.config.ts on every build). |
.github/ |
Workflows (ci.yml, release.yml, release-cache.yml, bench-harness.yml, web-demo.yml, and flake-rerun.yml / flake-hunt.yml, which record CI flakes in the pinned Known flakes issue), the setup-pnpm composite action most of them share, plus the open-source contribution scaffolding: CODEOWNERS, SECURITY.md, the PR template and three issue templates. |
2. "I want to change X — where do I start?"
| You want to change… | Start here |
|---|---|
| The app shell, global state, IPC wiring | src/App.tsx (the hub — see §5), src/app/ (the hooks and surfaces extracted out of it: use-project-state.ts, use-open-documents.ts, use-build-pipeline.ts, use-synara-host.ts, app-*.tsx — see §6), src/main.tsx (root render, locale activation, error capture, the production surfaces it hands App: src/app/production-surfaces.ts), src/app-types.ts (the shared shapes), src/app-utils.ts, src/app/window-layout.ts, src/trellis/ (the panel workspace) |
| The LaTeX / CodeMirror source editor | src/editor/latex/latex-editor.ts (assembles the CM6 extension set from latex-completions.ts, latex-diagnostics.ts, latex-hover-cards.ts, latex-environments.ts, latex-edits.ts, latex-symbols.ts), src/editor/codemirror-host.tsx (the @uiw/react-codemirror wrapper), src/canvas/document-canvas.tsx (the split source/preview shell that mounts it) and its modules in src/canvas/ (use-source-session.ts and source-session.ts, which own the live view, its extensions' lifetime, the annotation note and the editor status; editor-extensions.ts, html-preview.tsx, paper-reader.tsx, …), src/editor/editor-languages.ts, src/editor/harper-spellcheck.ts (with grammar-settings-store.ts, the writer's dialect, grammar style and rule choices; Settings draws them in src/settings/grammar-settings.tsx), src/editor/latex/latex-outline.ts (the section tree and its numbers), src/editor/latex/latex-outline-rail.tsx (the outline rail on the source's right edge; its PDF pages come from synctex_heading_pages), src/editor/latex/latex-text.ts; Markdown source mode: src/canvas/markdown-source-toolbar.tsx shows the visual editor's selection toolbar (shared parts in chrome/selection-toolbar-parts.tsx and selection-toolbar-items.ts) and applies the Markdown syntax edits of src/editor/markdown/markdown-source-format.ts; LaTeX wrap edits live in src/canvas/source-selection-edits.ts; highlighting: latex-selection-toolbar.tsx opens highlight-palette.tsx (the six highlighters of highlighters.ts, renamed in place, and the custom picker src/components/ui/color-picker.tsx), latex-highlights.ts parses \colorbox spans and latex-highlight-paint.ts paints them in the source |
| The visual Markdown (TipTap) editor | Lattice's own engine in src/editor/markdown/engine/: lattice-visual-editor.tsx (the host component: publication, path swaps, eligibility), markdown-document.ts (the byte-preserving Markdown round trip), markdown-to-document.ts / document-to-markdown.ts, engine-schema.ts, source-map.ts (Markdown offsets ↔ ProseMirror positions), source-overlays.ts (presence, tracked-change and comment overlays), passive-view.tsx (large read-only documents), and chrome/ (slash menu, selection toolbar, links, find, block controls). The host contract is src/editor/markdown/visual-editor-props.ts; the frozen clean-room spec it was written from is visual-editor-spec.md. |
| The spreadsheet | src/editor/spreadsheet/spreadsheet-editor.tsx (the React host), src/editor/spreadsheet/spreadsheet-univer.ts (Univer 0.25 setup, theme and menus), src/editor/spreadsheet/spreadsheet-types.ts (the .lattice-sheet format), src/editor/spreadsheet/spreadsheet-operations.ts, src/editor/spreadsheet/spreadsheet-yjs.ts (the local Y.Doc model), src/editor/spreadsheet/spreadsheet-xlsx.ts + src-tauri/src/export.rs (import/export) |
| The whiteboard | src/editor/board/board-editor.tsx (tldraw 5), src/editor/board/board-store.ts (the standalone tldraw store: load, serialize, replace), src/editor/board/board-asset-urls.ts (tldraw's fonts, icons and translations bundled from @tldraw/assets instead of its CDN), src/agent/agent-canvas-tldraw-adapter.ts. Loaded only via loadBoardEditorModule() — never import tldraw eagerly. |
| The PDF viewer | src/pdf/pdf-viewer.tsx (the toolbar and surface; src/canvas/project-pdf-panel.tsx mounts it for the compiled project PDF, in the PDF panel beside the canvas), which composes src/pdf/use-pdf-document.ts (building, swapping and tearing down PDFSlick viewers; src/pdf/pdf-page-swap.ts shows a rebuild in the open viewer, redrawing only the pages src-tauri/src/pdf_pages.rs signs as changed), use-pdf-view.ts (saved view state, link history), use-pdf-zoom.ts, use-pdf-search.ts and pdf-source-targets.ts (SyncTeX/quote highlights); src/pdf/pdf-slick.ts owns the PDF.js/PDFSlick plumbing, and src/pdf/pdfjs-runtime.ts is the one PDF.js runtime setup (the worker URL, whose entry src/pdf/pdf.worker.ts installs public/polyfills.js before PDF.js) shared with reference previews; scripts/check-webkit-compat.mjs, run by pnpm build, fails when a built PDF.js chunk needs a built-in that macOS 14's WebKit lacks and nothing installs. src/pdf/project-pdf.ts reads project PDFs a byte range at a time (read_project_asset_range, or the browser host's /__lattice_asset) instead of whole in one IPC reply. Also src/pdf/pdf-viewer-utils.ts, src/pdf/pdf-bytes.ts, src-tauri/src/pdf_fonts.rs, public/pdfjs/ |
| Compiling LaTeX and surfacing errors | src-tauri/src/latex.rs + latex/ (latexmk orchestration in build.rs, log parsing in build_log.rs, the BibTeX/biber .blg in bib_log.rs, diagnostics' files placed in the project by diagnostic_paths.rs, the last good PDF and SyncTeX kept for an errored build in shown_pdf.rs, SyncTeX) → commands build_project / abort_build / clean_project in src-tauri/src/ipc/build.rs; src/build/compile-diagnostics.ts (log lines → CodeMirror lint diagnostics), src/build/compile-diagnostics-panel.tsx, src/build/compile-explain.ts (TeX's messages in plain words, with the failing line and a suggested fix). Missing-package flow: src-tauri/src/tex_setup.rs + tex_setup/ (incl. the privileged installer scripts) + src/build/tex-setup-wizard.tsx; environment health: src-tauri/src/doctor.rs. Export for arXiv: latex/figure_preview.rs (one figure typeset alone for the agent's preview_latex_figure, in a scratch folder), latex/arxiv.rs (the pack from the last build's .fls, the test build) and latex/arxiv/source.rs (comment stripping, \input merging) → check_arxiv_package / export_arxiv_package in src-tauri/src/ipc/workspace.rs; the sheet src/project/arxiv-export-dialog.tsx and its rows src/project/arxiv-export.ts |
| The file tree and project search | src/project/project-file-tree.tsx (the Project panel; wraps @pierre/trees) and its project-tree-* modules (files and Git decorations, clipboard, context menu, inline create, pointer drag, motion, the shadow-DOM CSS), src/project/paper-library.tsx, src/project/project-file-icons.ts; search backend src-tauri/src/fts.rs (SQLite FTS; project/search.rs falls back to a linear scan); UI src/editor/latex/search-panel.ts, src/project/project-find-dialog.tsx, src/project/project-replace-dialog.tsx, src/project/quick-open-dialog.tsx |
| Starting a project: blank, bundled and saved local templates | src-tauri/src/project/templates.rs (immutable UUID snapshots in app-data templates/, bounded copy, instantiate) → list_local_templates / inspect_template_source / save_local_template and the local:<UUID> branch of project creation in src-tauri/src/ipc/workspace.rs; project/create.rs (creation itself); UI src/project/local-templates.tsx inside the creation dialog in src/project/project-dialogs.tsx |
| The paper library, literature search, bibliography | src-tauri/src/papers.rs + papers/ (import, bundles, markdown conversion, citation; full-text search over the cited papers' cached text in papers/library_search.rs over the per-project tantivy index of papers/search_index.rs, under .research/cache/paper-index), citation_audit.rs + citation_audit/ (the bibliography audit), openalex.rs, alphaxiv.rs, literature.rs, citation_health.rs, firecrawl.rs; UI src/papers/literature-discovery-panel.tsx, the Papers panel src/project/paper-library.tsx (its rows paper-row.tsx, search paper-library-search.ts and hit lines paper-text-hits.tsx, keyboard paper-list-keys.ts, toolbar paper-list-toolbar.tsx), src/project/references-panel.tsx, src/papers/bib-entry.ts + src/papers/bib-entry-dialog.tsx, a .bib file's visual form (Preview: the reference list) in src/papers/bib-visual-editor.tsx (its rows bib-row.tsx, a reference's detail bib-entry-detail.tsx, its paper to open or download bib-entry-paper.ts, kept in step with the source in Split by use-bib-source-sync.ts; mounted by src/canvas/bib-preview.tsx) over the span-preserving parser and edits of src/papers/bib-document.ts, the linked library (a reference manager's .bib whose changes come in after review: src/papers/linked-library.ts compares and applies, src/papers/linked-library-sheet.tsx is the sheet, src-tauri/src/project/linked_bibliography.rs keeps the link), src/papers/arxiv-id.ts, src/papers/paper-link.ts, src/papers/venues.ts (generated by scripts/gen-venues.mjs, one venue per line) |
| Version history and diffs | Transactions: src-tauri/src/project/history.rs (apply_transaction, .research/history/); an editor save's merge against outside edits, kept in the file's own line breaks, in project/editor_save.rs + commands list_history / revert_transaction in ipc/history.rs; git: src-tauri/src/git.rs + git/parse.rs, and git/compare.rs (any two versions, or one and now). UI: src/history/history-drawer.tsx, src/history/versions-timeline.tsx, src/history/compare-versions.tsx (History → Compare…) with export-changes-dialog.tsx over src-tauri/src/latex/changes.rs (the latexdiff PDF with changes marked), src/history/file-diff-view.tsx and src/history/pierre-diff.ts (@pierre/diffs + shiki). Design rationale: project-history-architecture.md |
| Overleaf sync | src/app/use-overleaf-workspace.ts (the React-side workspace, extracted from App.tsx; the connection state it shares with use-overleaf-realtime.ts is the Zustand store in src/overleaf/overleaf-connection-store.ts), src-tauri/src/overleaf.rs + overleaf/ (session, linking, REST, review, three-way merge) and src-tauri/src/overleaf_rt.rs + overleaf_rt/ (a hand-written Socket.IO 0.9 client; read the module header of overleaf_rt.rs first); handlers in src-tauri/src/ipc/overleaf.rs and ipc/overleaf_realtime.rs. Frontend: everything else is in src/overleaf/ — use-overleaf-realtime.ts (its wire types and pure rules are overleaf-realtime-model.ts) and the other use-overleaf-* hooks, which all subscribe through overleaf-realtime-listen.ts (the typed event union, delivered only to this window and the subscriber's current project) and keep late REST reads out of the next project through use-overleaf-project-snapshot.ts; overleaf-connect.tsx (Settings → Overleaf) and overleaf-project-picker.tsx (Open from Overleaf: the project list, and a pasted project or share link, overleaf-pasted-link.tsx); ot.ts (the OT transform). Live verification: scripts/verify-overleaf.mjs |
| The AI agent integration | src-tauri/src/synara.rs (sidecar supervision), src/agent/synara-runtime.ts (iframe URL + inbound message parsers), src/agent/agent-host-context.ts (the context snapshot pushed to the agent), useSynaraHost in src/app/use-synara-host.ts (the postMessage bridge; the iframes mount in src/trellis/trellis-agent-surface.tsx and, for the Git drawer, src/app/git-workspace-frames.tsx), src/agent/agent-*.ts (canvas/spreadsheet/paper-library/presentation tool bridges, and agent-figure-tools.ts, which draws a LaTeX figure for the agent before it is inserted), the inline Proofread and Polish (src/agent/agent-proofread.ts for the prompts and the agent_text_task relay, src/agent/proofread-edits.ts for splitting a reply into edits and holding back unsafe ones against src/editor/latex/latex-protected.ts, src/canvas/proofread-layer.tsx and proofread-card.tsx for the card, which reads the edits in place through the reusable src/components/ui/inline-edits.tsx, reached through proofread-bridge.ts, src/editor/proofread-anchor.ts for its CodeMirror widget; src/editor/change-marks.ts marks an accepted suggestion's words, and an outside edit's lines noted by src/editor/landed-text.ts, for a moment), run_cli() in src-tauri/src/agent_literature.rs (the literature gateway the sidecar shells out to), scripts/prepare-synara-sidecar.mjs (staging + pruning). Runtime doc: synara-runtime.md |
| Settings | src/settings/settings-dialog.tsx (the dialog and its navigation), with panes in doctor-settings.tsx, literature-settings.tsx, synara-settings-pane.tsx, agent-environment-settings.tsx and acknowledgements-settings.tsx (credits: acknowledged-software.ts, plus every shipped package read from the bundled THIRD_PARTY_NOTICES.md by third-party-notices.ts), the shared rows in settings-controls.tsx, and the sidebar search in settings-search.tsx over the index in settings-search-index.ts; src/settings/app-settings.ts (localStorage-backed preferences and fixed font stacks), src/settings/use-appearance.ts (theme, tint, accent, code theme and the native window material, set_window_material in src-tauri/src/macos_window.rs), theme-settings.tsx and theme-customization.ts (the Theme group and its presets), code-theme-settings.tsx and code-themes.ts (the Code theme group and the source editors' palettes), src/telemetry/interface-sounds.ts. Per-project settings go through update_project_manifest into .research/project.json. |
| i18n | src/i18n.ts, lingui.config.ts, src/locales/{en,zh-CN}/messages.po, scripts/check-i18n-coverage.mjs. Use the useLingui macro. Run pnpm i18n:check — it extracts, fails on catalog drift (git diff --exit-code -- src/locales), compiles strictly, then runs the coverage guard, which fails when a zh-CN entry has no Chinese (unless listed in scripts/i18n-unlocalized-baseline.txt, reserved for product and format names) or when Oxlint stops enforcing the rule below. .oxlintrc.json runs lingui/no-unlocalized-strings as an error over all shipping src/ code; text that is not UI is exempted by that override's patterns or a commented eslint-disable-next-line at the site. The Rust host's errors stay English prose; src/platform/host-error-text.ts shows them in the interface language in toasts, InlineMessage and wherever hostErrorMessage(reason) replaces toMessage, from the per-area lists in src/platform/host-errors-*.ts (each entry is the Rust literal, pinned to its file by host-error-catalog.test.ts). |
| The design system | src/styles/foundations.css (the token scale), src/styles/theme.css (the palette), src/styles/tokens.test.ts + surfaces.test.ts (the enforcement), design-system/ (the copy exported from the Paper file) with src/styles/design-system-drift.test.ts (it must match the app), src/components/ui/ (the shadcn-style primitives), src/index.css (Tailwind v4 without preflight). Decisions: design-system.md |
| Bundle size / startup | vite.config.ts (shikiTrimPlugin, manualChunkName), scripts/app-size-report.mjs (the budgets), src/canvas/canvas-lazy-modules.ts (how to add a lazy chunk correctly). See architecture.md §4. |
| Symptom: an open deck reloads on a tab or workspace change | src/app/use-open-documents.ts (open identities), src/app/use-trellis-bridge.ts (surface/tool state), src/trellis/trellis-document-views.tsx and src/trellis/use-hibernation.ts (mount policy), src/canvas/document-canvas.tsx and OpenSlideDeckPool in src/canvas/open-slide-deck-pool.tsx (iframe lifetime). Compare active visibility with explicit-close/project-change teardown. |
| Symptom: Paper lacks source/split controls | useTrellisBridge's view modes in src/app/use-trellis-bridge.ts, tool selection in src/trellis/trellis-controller.ts, FileHeaderTools fit/action rendering in src/trellis/trellis-header-tools.tsx, chooseMode in src/app/use-open-documents.ts, src/canvas/use-markdown-mode-handoff.ts. Distinguish Paper content selection from display mode: a missing header action does not mean source editing was removed. |
| Symptom: Git panel startup or loading | useToolDrawers in src/app/use-tool-drawers.ts, src/app/git-workspace-frames.tsx, src/trellis/trellis-agent-surface.tsx, useSynaraHost in src/app/use-synara-host.ts; for upstream embed ownership read synara-runtime.md. |
| Symptom: a loading indicator (skeleton, spinner, "Loading…") | The shared reveal delay in src/components/ui/loading-reveal.ts and live-region announcement in src/components/ui/loading-status.tsx; the skeletons beside their views: src/pdf/pdf-loading.tsx, src/canvas/document-skeleton.tsx (a document opening: its source, its rendered page or both, also drawn by a Trellis tab on its way to becoming active in src/trellis/trellis-text-snapshot.tsx), src/app/git-workspace-skeleton.tsx, src/agent/agent-panel-skeleton.tsx, src/overleaf/overleaf-project-skeleton.tsx; the file-opening overlay in src/app/opening-overlay.tsx; lazily loaded tools' shells in src/app/tool-loading-shell.tsx. The one-indicator rule is in design-system.md. |
| The release | node scripts/bump-version.mjs patch — bumps package.json, src-tauri/tauri.conf.json, src-tauri/Cargo.toml and the research-writer entry in src-tauri/Cargo.lock in lockstep. It only edits files; it then prints the exact git commit / git tag / git push commands, and pushing the tag triggers .github/workflows/release.yml. Never edit the version numbers by hand. Full procedure: release-process.md |
3. Navigating src/ and src-tauri/src/
src/ was once one flat directory. It is now split by domain: only the
application shell stays at the root and everything else lives in a
directory named for what it does. The split landed in two batches —
overleaf/ + collab/ first, then everything else — and both were
pure git mv renames, so git log --follow still reaches the whole history of
every file.
Two rules govern the layout, and both are deliberate:
- Files were placed by their reference relationships, not their names. The
test for "shared" is not how many modules import something but whether its
importers span more than one domain. That is why
spreadsheet-types.tssits ineditor/spreadsheet/despite its many importers, whyreferences-panel.tsx(a symbol-reference panel, not a bibliography one) sits inproject/, and why the peer-colour palette moved out ofcollab/intocomponents/ui/. - Filenames keep their prefix —
src/overleaf/overleaf-presence.tsx, notsrc/overleaf/presence.tsx. Dropping the prefix would rewrite every intra-domain import and turn a reviewable pure-rename diff into a content diff. Prefix-stripping is a separate pass.
What stays at src/ root
The bar is "the application shell, or a contract every domain
depends on". There is no shared/ directory: a grab-bag named shared attracts
files, and a root you can read in one screen does not.
| File | Why it is not in a domain |
|---|---|
main.tsx |
The Vite entry. index.html names /src/main.tsx literally. |
App.tsx, App.css |
The root component and its stylesheet. App.css is the @import hub for src/styles/*.css. |
index.css |
Tailwind v4 entry; components.json names it. |
app-types.ts |
Imported from every domain — the shared domain model. |
app-utils.ts, app-utils.test.ts, app-utils-confirm.test.ts |
Imported from every domain. app-utils-confirm.test.ts covers the confirm-action registry that lives in app-utils.ts; it is separate because it needs a module-level @tauri-apps/plugin-dialog mock. |
i18n.ts, i18n.test.ts |
Locale bootstrap, called from main.tsx before render. |
vite-env.d.ts |
Ambient declarations. |
Domain directories
| Path | What it is |
|---|---|
src/components/ui/ |
Shared presentation primitives: the shadcn-style set (button, dialog, scroll-area, …) plus the app-level ones — motion.tsx/motion-values.ts, resizable-drawer.tsx, avatar-group.tsx, confirm-action-dialog.tsx, search-picker-dialog.tsx (the PickerDialog quick open and the command palette also use; picker-ranking.ts ranks their rows), keycaps.tsx and render-keycaps.tsx (a shortcut drawn as keycaps), chat-panel.tsx (the shared chat surface), collab-colors.ts, the fluid hover (use-fluid-hover.ts, fluid-hover-*), dialog exits (exit-picture.ts) and the drawn scrollbars (scrollbar-track.ts behind external-scrollbar.tsx and overlay-scrollbar.tsx). Deliberately one UI home, not a ui/ rival to components/. |
src/overleaf/ |
Overleaf sync: OT engine ot.ts, hooks use-overleaf-*, panels overleaf-*, overleaf-editor-extensions.ts (remote carets, tracked changes and margin review's line tint in CodeMirror), and margin review in the source (overleaf-margin-review.tsx, its per-window switch margin-review-mode.ts). Its React-side workspace is the odd one out — src/app/use-overleaf-workspace.ts — though the connection state it holds is overleaf-connection-store.ts here. |
src/editor/markdown/ |
Markdown support around the visual editor: the host contract (visual-editor-props.ts), headings and the heading rail, Markdown collaboration, link routing, the preview-sync policy, the workspace index, the project image host. The editor itself is Lattice's engine in engine/ beneath it. |
src/agent/ |
The Synara sidecar bridge: synara-runtime.ts (URL + message parsers), agent-host-context.ts, and the agent-* tool bridges (canvas, spreadsheet, paper library, composer files), which share validation and reply plumbing through agent-protocol.ts. |
src/project/ |
The sidebar panels: the file tree (project-file-tree.tsx + project-tree-*) and the paper library (paper-library.tsx, paper-library-search.ts, paper-library-view.ts for its filters, sorts and groups, paper-list-window.ts for drawing a long list near the view only, citation-health.ts); project-wide search and replace, quick-open, TODO scavenger, project dialogs, asset preview, the symbol-references panel, the manuscript checklist (manuscript-checklist.tsx, with the writer's own items in submission-items.tsx), and the figure inventory it opens (figure-inventory.ts parses usages, figure-inventory-dialog.tsx previews and applies replacements, figure-undo.ts keeps the project-scoped undo). |
src/editor/latex/ |
The CM6 LaTeX extension set and its parts (completions and their fallback vocabulary, diagnostics, hover cards, environments, edits, symbols), outline, selection toolbar, math regions/preview, project text helpers (latex-text.ts: labels, macros, KaTeX macro extraction, \appendix location), the search-panel relabelling. |
src/telemetry/ |
Everything the app says about itself: the log store and log panel, the toast stack (toast-stack.tsx, on Base UI), the updater, global error capture, the root error boundary and each panel's (panel-error-boundary.tsx), the save of the open edits they start on a crash (crash-save.ts), interface sounds. |
src/history/ |
Version history and diffs: the history drawer, the versions timeline, comparing two versions and the PDF with changes marked, file-diff-view.tsx and pierre-diff.ts (@pierre/diffs + shiki), conflict markers and the conflict resolver. |
src/editor/insert/ |
Putting things into a document: the figure and table generators, clipboard images. |
src/styles/ |
The CSS files (the token scale, palette, app shell, workspace) plus the two tests that enforce them (tokens, surfaces). |
src/build/ |
Compiling LaTeX and the tooling around it: log-line diagnostics, the TexLab language client and its diagnostics, the TeX-install wizard. |
src/papers/ |
The paper library and bibliography: literature discovery, bib-entry, venues.ts, arXiv ids, paper links, import progress, the reference check (bibliography-audit*). |
src/canvas/ |
The editing surface shell: document-canvas.tsx, the domain models App hands it (canvas-model.ts), and the modules it was broken into (the source editor's session and extensions, HTML preview, Paper reader, asset preview, Markdown split-scroll and mode handoff, split layout, zoom), the canvas toolbar, the document outline, the compiled project PDF's panel beside the canvas (project-pdf-panel.tsx), and the lazy-chunk registry (canvas-lazy-modules.ts). |
src/editor/spreadsheet/ |
The Univer-backed .lattice-sheet editor, its Univer setup, format types, operations, local Y.Doc model and XLSX import/export. |
src/app/ |
App orchestration: the hooks extracted from App.tsx (project state, lifecycle and library, the open documents, the project tree, build pipeline, Synara host, Overleaf workspace, editor comments, reference import, SyncTeX navigation and the LaTeX structure, …), the app-*.tsx surfaces App renders (title bar, dialogs, drawers), shared effect helpers, and the window/panel geometry App owns. Also the App integration suites, one per area (app-documents.test.tsx, app-agent.test.tsx, app-project.test.tsx, app-builds.test.tsx, app-overleaf.test.tsx, app-papers.test.tsx, app-welcome.test.tsx, the Settings focus suites), which render the real App through the inert mocks and helpers in app-test-utils.tsx and appHarness in app-test-mount.tsx, with the real surface their area needs (app-test-canvas.ts, app-test-pdf-panel.ts, app-test-settings.ts) and doubles for the rest; app-composition.test.tsx mounts every production surface for what crosses them. |
src/settings/ |
The settings dialog, its panes and its search, the localStorage-backed preference store, appearance and fixed fonts. |
src/live-preview/ |
The LuaLaTeX live preview (opt-in per project, Settings › Builds): use-live-preview.ts is the only part that loads with the app; the session that feeds rtex the project's edits (live-preview-session.ts), the idle-only schedule for autosave builds while it is live (quiet-builds.ts), the layer that paints its paragraphs and pages over the compiled PDF (live-preview-layer.tsx) and the rtex-* renderer adapted from the Realtime TeX VS Code plugin load once it is on. The host side is src-tauri/src/live_preview.rs. |
src/terminal/ |
The Terminal panel, a Trellis view at the foot of the workspace (⌃): terminal-layout.ts(its place and key) and the view glue interminal-view*.tsx load with the workspace; everything else loads when a Terminal panel first mounts (terminal-load.ts): the per-window session store (terminal-sessions.ts, so shells outlive the panel unmounting), the xterm.js screen (terminal-screen.ts, WebGL with a DOM fallback), its colours from the design system (terminal-theme.ts, terminal.css) and the panel (terminal-panel.tsx). The host side is src-tauri/src/terminal.rs`. |
src/pdf/ |
The pdf.js viewer (pdf-viewer.tsx over the use-pdf-* hooks and pdf-slick.ts) with SyncTeX, search and annotations, plus the range-read transport for project PDFs. |
src/platform/ |
Runtime bootstrap and the repo-level guards that pin it: the browser-host runtime and bridge, the build flavor (flavor.ts, Lattice Beta), perf-probe.ts, test-setup.ts, and tests for compiler/security configuration and the pre-module compatibility script in public/polyfills.js. |
src/editor/ |
Editor infrastructure shared by more than one editor kind: the hand-mounted CodeMirror host, language resolution, Harper spellcheck, paper drops and the drop caret that shows where they land, the go-to-line dialog, and dom-utils.ts (the element() helper for hand-built editor chrome). |
src/animated-icons/ |
The animated product icons (plus bakai/, hand-rebuilt glyphs). |
src/editor/comments/ |
Annotations (local editor comments): the data shape, the CodeMirror/TipTap integration with the gutter pins, the Annotations panel, the comment card and hover preview the document shows (comment-card.tsx; the source editor draws it into its hover tooltip through src/canvas/editor-comment-cards.tsx), the Revision rounds sheet, placing a PDF selection in the source, and the open-or-all visibility filter the Overleaf comments panel uses. The revision rounds are src/app/annotation-rounds.ts (one plain controller per window, which the panel, review card, PDF layer and sheet subscribe to once on screen), the agent's prompt and tools src/agent/agent-annotations.ts, the review card src/canvas/annotation-review.tsx, the composers src/canvas/annotation-composer.tsx and pdf-annotation-composer.tsx, and the PDF marks, their preview and card, and the Annotate offer src/pdf/pdf-annotations.tsx (loaded on the first selection or mark). Export review PDF, the copy of the compiled PDF a collaborator reads in Preview, Acrobat or Zotero: src/pdf/review-pdf.ts places each note on the PDF, src/pdf/review-pdf-sheet.tsx is the sheet (opened from the PDF's More menu or the Annotations panel), and src-tauri/src/review_pdf.rs writes them as standard PDF annotations. |
src/editor/board/ |
The tldraw whiteboard and its standalone store. Loaded only via loadBoardEditorModule(). |
src/editor/presentation/ |
The embedded Open Slide workspace and its loopback bridge. |
src/trellis/ |
The panel workspace (Trellis): the controller App drives, the workspace and its panel types, the title-bar panel controls, the panel header actions, per-project layout persistence with the named workspaces and the Writing and Reading presets, Trellis's translated labels, and its stylesheet. |
src/onboarding/ |
The guided tour of the Trellis workspace that the Guided tutorial entry starts once the sample project is open: its stops and what each waits for the writer to do (guided-tour-steps.ts), card placement, the lazily loaded tour surface and its stylesheet, and the useGuidedTour state App holds. |
src/components/ |
copy-button.tsx, icon-tip.tsx (outside ui/), chunk-gate.tsx (ChunkGate: a lazily loaded part of a window's first frame shows its own placeholder instead of a Suspense fallback), and lazy-mount.tsx (LazyMount: a lazily loaded dialog or drawer opens without React's 300 ms Suspense reveal delay). |
src/hooks/ |
Domain-free hooks: use-latest-ref.ts, use-composition-guard.ts (keys an input method is composing with never submit; Input, Textarea and SearchField route onKeyDown through it), use-non-passive-wheel.ts, use-dismiss-on-outside-pointer.ts (close a hand-placed popup on a press outside it), use-progressive-rows.ts (a long list draws its rows as they come near the view: the history timeline, Check references). |
src/lib/ |
utils.ts (the shadcn cn helper) and lazy-chunk.ts (chunk and lazyChunk: a shared dynamic import, and React.lazy over it that renders without suspending once the chunk is in). |
src/web-demo/ |
What the website's demo (tools/web-demo/) shows in place of the parts that need the Mac, the Agent, whiteboards and Open Slide: cards loaded through those surfaces' own lazy loaders only when LATTICE_WEB_DEMO=1 builds the demo, so the app build never contains them and the demo build never contains tldraw. |
src/locales/ |
Lingui catalogs: en and zh-CN .po sources (compiled .js are generated, not tracked). |
src/assets/ |
Fonts. |
Notes on the seams — the places where the domain boundary is a judgment call rather than a fact:
editor/has a shared level and four editor kinds.codemirror-host.tsxandeditor-languages.tsserve the plain-text editor for every language, so they are not undereditor/latex/;harper-spellcheck.tsis a CodeMirror lint source used by both the canvas and the LaTeX extension set.markdown-workspace-index.tscould beproject/. It is a project-wide index, but it exists to serve the Markdown editor (wiki links, mirrors), so it went with it.use-near-viewport.tsis a generic IntersectionObserver hook that would look at home insrc/hooks/— but its only importer is a Markdown-editor file, and the placement rule is reference-based, so it sits ineditor/markdown/.use-non-passive-wheel.ts, whose importers span the canvas and the PDF viewer, went tosrc/hooks/, as diduse-latest-ref.ts(App and its hooks, the canvas, the PDF viewer, the project tree, the updater and compile repair).interface-sounds.tsintelemetry/is the audio channel of the same notification layer asapp-notify.ts, not instrumentation. The directory is "what the app says about itself", which also covers the updater and its toast.- Yjs remains only for the spreadsheet:
editor/spreadsheet/spreadsheet-yjs.tsis its local Y.Doc model, not collaboration plumbing. reference-preview.tsis inproject/, notpdf/. It renders a project asset (image or first PDF page) read throughread_project_asset; it importspdf/pdf-viewer-utils.tsand the range transport inpdf/project-pdf.tsbut is not part of the viewer.
src-tauri/src/ modules
lib.rs declares every module. A large area is a short x.rs map beside an
x/ directory of parts; the map's module header is the place to start.
| Module | What it is |
|---|---|
lib.rs, main.rs |
The Tauri application shell: plugins, the window lifecycle, the child runtimes and the command table. main.rs only calls run. |
ipc.rs + ipc/ |
The Tauri command handlers, one module per area; each is a thin shell over a domain module. |
app_state.rs |
Which project each window shows, and the resources an open project owns. |
app_identity.rs |
The bundle identifier this build was configured with. |
models.rs |
The serialized shapes shared with the frontend, including coded messages the interface translates. |
util.rs |
Small text, hashing and filesystem helpers. |
http.rs |
Where every blocking reqwest client starts: deadlines, user agent, proxy. |
commands.rs + commands/ |
Finding and running external processes the way a GUI-launched app has to; commands/python_tools.rs pins the Python CLIs run through the app-managed uvx. |
wide_event.rs |
One wide, structured log event per operation (see logging.md). |
command_diagnostics.rs |
Puts the frontend's operation and request ids on a long command's wide event. |
diagnostic_logs.rs |
The support bundle: bounded, redacted tails of the app and sidecar logs. |
project.rs + project/ |
Everything Lattice does to a project folder: creation, manifest, paths, the transaction history, tree, search, archives. |
project_fs.rs |
Race-free mutations beneath an already selected project root. |
fs_watch.rs |
The project filesystem watcher (FSEvents plus a debounce thread). |
fts.rs |
The incremental SQLite FTS5 project search index. |
git.rs + git/ |
Git for the project folder: status and the version timeline. |
latex.rs + latex/ |
latexmk builds, build-log parsing, SyncTeX, prewarming, the pdfLaTeX preamble format cache (preamble_format.rs) and venue detection. |
texlab.rs |
A TexLab language server per project, for editor diagnostics, completion, hover and go-to-definition. |
texcount.rs |
Word counts for the root document, with an estimate when texcount is missing. |
format_latex.rs |
Formatting a LaTeX document with latexindent. |
harper.rs |
Harper grammar and spell linting, on the Rust side because its WASM Worker never starts under WKWebView; it also holds the academic profile's rule list. |
doctor.rs |
The TeX doctor: which tools a build needs are present, and whether the project's own requirements are met. |
tex_setup.rs + tex_setup/ |
One-click TeX and required-tool installs, including the privileged installer scripts. |
live_preview.rs + live_preview/ |
The LuaLaTeX live preview's host: downloading the pinned rtex release on demand, one supervised rtex serve per project, and the font, image and fallback-PDF files its renderer reads. |
terminal.rs + terminal/ |
The Terminal panel's shells: a login shell per tab on its own pseudo-terminal (pty.rs), its output streamed in batches with flow control (session.rs), ended with its tab, its window's project, the window or Lattice; caller.rs lets only a workspace window's own Lattice page reach them. Commands in ipc/terminal.rs. |
pdf_fonts.rs |
Inspecting embedded PDF fonts without poppler. |
pdf_pages.rs |
Per-page signatures of a PDF, so the preview redraws only the pages a rebuild changed. |
export.rs |
Saving an exported file (the compiled PDF, a workbook, an Agent visual) to the path the writer chose. |
review_pdf.rs |
The review copy of the compiled PDF: the writer's chosen annotations appended as standard Highlight and Text annotations in one incremental update. |
papers.rs + papers/ |
Reference import and the paper library: bundles, Markdown conversion, citation insertion, full-text search (search_index). |
paper_pdf_proxy.rs |
A loopback, token-guarded URL that streams a remote paper PDF to PDF.js. |
citation_audit.rs + citation_audit/ |
The bibliography audit: check .bib entries and inline reference lists against publication sources and apply reviewed fixes. |
citation_batch.rs |
Exact-ID Semantic Scholar hydration for the audit. |
citation_health.rs |
Cached Crossref notices (retractions, corrections) for the DOIs a project cites. |
literature.rs |
The Discover panel's search: alphaXiv full text first, OpenAlex second, deduplicated. |
literature_service.rs |
HTTP plumbing shared by the OpenAlex, Semantic Scholar and Crossref clients. |
literature_credentials.rs |
Personal literature-provider keys and the Crossref email, in one keychain item. |
openalex.rs |
The OpenAlex works search. |
alphaxiv.rs |
alphaXiv full-text search and per-paper overviews. |
firecrawl.rs |
Webpages cited into the bibliography, scraped into local Markdown. |
web_metadata.rs |
Deterministic webpage citation metadata. |
link_preview.rs |
Link previews (title, description, site name, favicon), fetched with each redirect re-checked. |
agent_literature.rs |
Literature operations the agent requests, through the same code as the app's own interface. |
overleaf.rs + overleaf/ |
The Overleaf sync bridge: session, linking, REST, review, the three-way merge. |
overleaf_rt.rs + overleaf_rt/ |
The hand-written Socket.IO 0.9 client for Overleaf's realtime channel. |
sidecar.rs |
The JavaScript runtime and process lifetime the Synara and Open Slide sidecars share. |
synara.rs + synara/ |
Supervisor for the bundled Synara agent sidecar; synara/ seeds its skill preferences and hands its provider CLIs the writer's proxy and provider settings (environment.rs: the login shell's, read by shell_env.rs, then the system proxy), and restarts the sidecar to pick up a change (restart.rs; Settings › Agent › Environment is src/settings/agent-environment-settings.tsx). |
process_inspector.rs + process_inspector.c |
An unprivileged ps replacement for the Synara sidecar. |
presentation.rs |
Supervisor for the embedded Open Slide presentation runtime. |
browser_host.rs + browser_host/ |
The loopback browser host that serves the app to a browser on a fixed local port. |
macos_window.rs |
macOS window chrome: backing colours, AppKit event monitors, the colour sampler, the import panel. |
traffic_lights.rs |
The traffic lights' compact-toolbar geometry and their alignment with the web titlebar. |
native_locale.rs |
The native surfaces (menu bar, AppKit and WebKit panels) that follow Lattice's interface language. |
save_before_close.rs |
Holding a window's close, quitting and restarting Lattice until each window's page has saved its open edits (or a few seconds pass). |
app_terminate.rs |
AppKit's terminate: (Dock ▸ Quit, logging out, shutting down) waiting for save_before_close. |
app_restart.rs |
Restarting into an update: which window installs it, and what a restart would stop. |
perf_lab.rs |
The real-app measurement lab's native half, built only with the perf-lab feature (see driving-the-app.md). |
test_support.rs |
Fixtures shared by the crate's unit tests. |
embedded_skills/ is not a module: it holds the agent skills synara.rs hands
the sidecar as its bundled skills directory.
4. Third-party code
No source tree under src/ is vendored from another project. Files adapted
from permissively licensed projects (a few UI primitives, icons, fonts, the
Open Slide theme skill, the live preview's display-list renderer) keep their attribution headers, and each is recorded in
THIRD_PARTY_NOTICES.md. The visual Markdown
editor was once built on vendored Open Knowledge code; it is now Lattice's own
engine (§2), and a repository guard test keeps that code from coming back.
5. The files that actually matter
| File | Role |
|---|---|
src/App.tsx |
The hub. Composes the src/app/ hooks and owns what still spans them: the wiring between them and the top-level layout. See §6. |
src/app/use-open-documents.ts |
The open documents: tabs, the file/Paper/asset in front and its buffers, every open and close, back/forward, autosave, disk sync and the layout restore. It composes the hooks that carry these out: the state in use-document-store.ts (held in the Zustand store of open-documents-store.ts), then use-document-save.ts, use-document-opens.ts, use-document-disk-sync.ts, use-document-autosave.ts, use-workspace-layout.ts and use-document-tree-changes.ts. Read its tests for the guarantees. |
src-tauri/src/project.rs + project/ |
Project validation, path safety, the transaction/history model, file classification, tree building, zip import/export. project.rs maps the submodules; most Rust areas depend on it. |
src-tauri/src/overleaf.rs + overleaf/ |
Overleaf session, linking and clone, REST, review, and the three-way merge against .research/overleaf-base/. |
src/canvas/document-canvas.tsx |
The editing surface: source pane, preview pane, the split layout, and the mount points for every lazy editor. |
src-tauri/src/overleaf_rt.rs + overleaf_rt/ |
A hand-written Socket.IO 0.9 client for Overleaf's realtime channel. The module header of overleaf_rt.rs is the protocol spec. |
src-tauri/src/lib.rs + ipc/ |
lib.rs wires plugins, the window lifecycle, the child runtimes and the command table; the thin handlers live in ipc/, one module per area. No domain logic in either. |
src/editor/markdown/engine/lattice-visual-editor.tsx |
The Lattice visual Markdown engine (TipTap 3 / ProseMirror): canonical publication, path swaps, round-trip eligibility. Parsing, serialization, source mapping, overlays and the passive viewport live in sibling modules under engine/. |
src-tauri/src/papers.rs + papers/ |
Paper import, materialization into .research/papers/, citation insertion, bibliography upgrade. |
src/editor/latex/latex-editor.ts |
The CodeMirror 6 extension set for LaTeX: keymaps, autocompletion, linter, and hover wiring over the latex-* modules beside it. (Vim/Emacs keymaps are loaded lazily one level up, by useOptionalKeymapExtensions in src/canvas/editor-extensions.ts.) |
src/editor/spreadsheet/spreadsheet-editor.tsx |
The Univer-backed .lattice-sheet editor (Univer setup is spreadsheet-univer.ts). |
src/overleaf/use-overleaf-realtime.ts |
The frontend half of the Overleaf realtime bridge: op queue, versions. The open document's comment anchors and tracked changes are use-open-doc-review.ts, the rules that move them overleaf-suggestions.ts. |
src/app/use-overleaf-workspace.ts |
The Overleaf workspace state extracted from App.tsx. |
Honourable mention: src-tauri/src/git.rs with git/parse.rs. The PDF viewer, the project
sidebar and the settings dialog, once among the largest files, are now split
into cohesive modules (see §2).
6. Known rough edges
Stated plainly so you are not surprised.
App.tsx is still the hub
It was over ten thousand lines before the simplification pass that split it up. Most
self-contained state now lives in hooks under src/app/, and App composes
them:
- Project and documents:
use-project-state.ts(project identity, transitions,captureProjectScope),use-project-lifecycle.ts(which project the window shows: startup routing, switching, new/open/recent/tutorial/ZIP, the browser handoff),use-project-library.ts(papers, citations, history, TODOs; the label and citation index's load state and its toast are inproject-index-load.ts),use-project-history-items.ts(the saved history and agent checkpoints as one newest-first list),use-open-documents.ts(the open documents: tabs, the file, Paper or asset in front, every buffer, opening and closing, back/forward, autosave, disk sync, and the layout restore it drives throughuse-file-view-states.tsand the pure plan inworkspace-restore.ts; saves run one at a time throughsave-activity.ts, and a Paper's reading files are inpaper-documents.ts),use-project-tree.ts(creating, deleting, renaming, moving and importing entries, Finder drops, clipboard figures),use-paper-actions.ts(downloading and opening a paper, cancelling downloads, reading a dropped paper, removing a cited entry),use-canvas-requests.ts,use-save-slot.ts(the save slot: saved, and reached Overleaf). - LaTeX:
use-latex-structure.ts(outline across included files, labels, macros,\graphicspath, TODOs, main-body pages),use-synctex-navigation.ts(source ↔ PDF jumps),use-source-navigation.ts(go to definition, find a label's or key's uses, rename, links into the project). - Build and agent:
use-build-pipeline.ts(withbuild-outcome.ts,build-progress.ts,compiled-sources.tsandadopt-root-document.tssplit out of it),use-build-error-fix.ts(a failed build's suggested fix, applied in the project it was asked in),use-tex-setup.ts,use-agent-checkpoints.ts,use-agent-context.ts,use-synara-host.ts,use-agent-spreadsheet-resolver.ts,use-comment-author.ts(who signs comments). - Overleaf and Open Slide:
use-overleaf-workspace.tsandopen-slide-writes.ts(Open Slide writes). - Workspace and commands:
use-trellis-bridge.ts(the Trellis bridge, panel menus and doc tools),use-tool-drawers.ts(the one owner for opening and closing the tool drawers — history, Git, TODOs, checklist, comments, literature, terms — and their per-kind side effects),use-command-table.ts(the command table itself) andapp-command-overlays.tsx(the palette and the shortcut sheet it opens),use-app-commands.ts(the command type, the palette'srunCommandand the window's one shortcut listener, ⌘/Ctrl keys and F8 alike; a Synara frame hands Lattice's keys back to it throughsrc/agent/synara-host-shortcuts.ts, and ⌘W closes a document tab throughsrc/trellis/close-document-tab.ts),palette-sections.ts(the sections the palette and the shortcut sheet group commands under),command-palette.tsx(the ⌘K palette: commands, and for a typed query the project's files, Papers and Settings rows),command-palette-leading.ts(the recent and per-surface commands an empty palette query leads with),key-combos.ts(how every keymap's notation is read and drawn),shortcut-groups.tsand the lazyshortcut-sheet.tsx(the ⌘? sheet, built from the command table,src/trellis/trellis-keymap.ts,src/editor/editor-keymap.ts,src/editor/latex/latex-shortcuts.tsandsrc/editor/markdown/engine/engine-shortcuts.ts, which also bind those keys), and focus mode'sfocus-mode-bar.tsxanduse-focus-mode-escape.ts(the layout side is a Trellis layout state intrellis-layout.ts). - Surfaces:
app-titlebar.tsx,use-canvas-toolbar.tsx,app-history-drawers.tsx,app-search-dialogs.tsx,app-project-dialogs.tsx,app-overleaf-drawer.tsx,app-overleaf-dialogs.tsx,app-submission-dialogs.tsx,app-settings-dialog.tsxanduse-settings-window.ts(Settings, and whether it is open and on which page),app-editor-panels.tsx,use-panel-portals.tsx(what App renders into the Trellis panels: file tree, Papers, agent, action rows; the lazy panels areapp-lazy-panels.tsx),app-overlays.tsx, each handed the hook object it renders rather than dozens of loose props. The workspace itself is the Trellis panel layout insrc/trellis/. The document canvas, the compiled-project PDF panel (both built byuse-document-surfaces.tsx) and Settings are App'ssurfacesprop (app-surfaces.ts), whichmain.tsxfills fromproduction-surfaces.ts: App imports only their types, so a test that mounts App with doubles does not depend on them. - Shared plumbing:
effect-helpers.ts(disposeWhenSettled,subscribeTauriEvent,whenIdle,frameCoalescer,onLayoutChange, timers,useRefState,useStableHandlers). - Action helpers:
*-action.ts(removing a cited entry, renaming a symbol, adding a spelling word, ignoring a grammar rule or sentence) — thetry/catchbodies of App and its hooks, kept outside them so the React Compiler can compile them.
What remains in App is the wiring between those hooks (what a project entry
resets and scans, which build follows a save) and the top-level layout. Every way of changing what is open goes through the documents store's
commands (openFile, openPaper, openAsset, open, close, save, …);
its test file is the place to read what they guarantee.
One-shot requests to the canvas (jump to a line, restore a view, insert a
citation or figure, rename or wrap an environment) travel as a single
CanvasRequests bundle and are settled by id.
If you add a feature here, put its state in a hook under src/app/ and keep
App.tsx as the composition point. src/platform/react-compiler-guard.test.ts
caps React Compiler bailouts per file, App.tsx and the extracted hooks included.
document-canvas.tsx is still large
It was once several thousand lines. The editor extension set, HTML preview, Paper reader, asset
preview, Markdown split-scroll and mode handoff, split layout and zoom
controls now live beside it in src/canvas/. src/canvas/canvas-lazy-modules.ts
still exists so that document-canvas.tsx exports components only: one
shared function export there would invalidate the whole module on every edit
under Fast Refresh.
The lint debt is concentrated
pnpm lint runs Oxlint with the --max-warnings cap set in the lint script
in package.json. The cap is a ratchet: lower
it when you remove warnings, never raise it.
The warnings are almost entirely react-hooks-js/* diagnostics that
the **/*.{ts,tsx} override in .oxlintrc.json deliberately downgrades to warnings
(exhaustive-deps, refs, set-state-in-effect, immutability), and
react-refresh/only-export-components. A few files carry most of them,
document-canvas.tsx and use-overleaf-workspace.ts above all; run
pnpm lint for today's list.
Test files that do not name what they test
Four files that misreported their subject were renamed in August 2026, and the
gap the worst of them hid is now closed: navigator.test.ts tested the tree's
Git decorations, so the large navigator.tsx looked covered while
having no test at all. It is now project-tree-files.test.ts (the decorations
live in project-tree-files.ts), and project-file-tree.test.tsx,
paper-library.test.tsx and navigator-drag.test.ts cover the sidebar itself.
The others became collab-session-peers.test.ts (since removed with Lattice Shares) and app-utils-confirm.test.ts;
latex-outline-breadcrumb.test.ts was merged into latex-outline.test.ts.
Some test files still have no same-named source module, and those are fine —
they assert across several modules or on files rather than testing one export:
src/platform/polyfills.test.ts,
src/platform/react-compiler-guard.test.ts,
src/platform/tauri-security-config.test.ts,
src/components/ui/motion-controls.test.tsx,
src/pdf/pdf-viewer-component.test.tsx. Most of these are
intentional (a scenario suite, not a unit suite) — but you cannot infer what a
test covers from its filename here. Open it.
The guards that assert on files rather than exercise a module —
react-compiler-guard, tauri-security-config, polyfills, clean-room-guard — live in
src/platform/ because what they pin is the build/runtime platform, not any
feature. A test may read repository files as text only if
src/platform/source-text-test-guard.test.ts allowlists it with a reason;
everywhere else, test the behaviour.
Other things worth knowing
src/has no@types/nodeby design — it is browser-only by convention.src/styles/editor-workspace.cssis the largest stylesheet;src/App.cssis a short import manifest, so don't look for CSS there.