# Codebase map
Where to start. If you want the *why* behind the structure, read
[`architecture.md`](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
| wc -l`, `wc -l `).
---
## 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`](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`](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`](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:` 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`](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`](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`](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](architecture.md#4-performance-and-bundle-constraints). |
| **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`](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`](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`](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.ts` sits
in `editor/spreadsheet/` despite its many importers, why `references-panel.tsx` (a
*symbol*-reference panel, not a bibliography one) sits in `project/`, and why
the peer-colour palette moved out of `collab/` into `components/ui/`.
- **Filenames keep their prefix** — `src/overleaf/overleaf-presence.tsx`, not
`src/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 in `terminal-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.tsx`
and `editor-languages.ts` serve the plain-text editor for every language, so
they are not under `editor/latex/`; `harper-spellcheck.ts` is a CodeMirror
lint source used by both the canvas and the LaTeX extension set.
- **`markdown-workspace-index.ts` could be `project/`.** 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.ts` is a generic IntersectionObserver hook** that would
look at home in `src/hooks/` — but its only importer is a Markdown-editor
file, and the placement rule is reference-based, so it sits in
`editor/markdown/`. `use-non-passive-wheel.ts`, whose importers span the
canvas and the PDF viewer, went to `src/hooks/`, as did `use-latest-ref.ts`
(App and its hooks, the canvas, the PDF viewer, the project tree, the updater
and compile repair).
- **`interface-sounds.ts` in `telemetry/`** is the audio channel of the same
notification layer as `app-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.ts`
is its local Y.Doc model, not collaboration plumbing.
- **`reference-preview.ts` is in `project/`, not `pdf/`.** It renders a project
*asset* (image or first PDF page) read through `read_project_asset`; it
imports `pdf/pdf-viewer-utils.ts` and the range transport in
`pdf/project-pdf.ts` but 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`](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`](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`](../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 in `project-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 through `use-file-view-states.ts` and the pure plan in `workspace-restore.ts`; saves run one at a time through `save-activity.ts`, and a Paper's reading files are in `paper-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` (with `build-outcome.ts`, `build-progress.ts`, `compiled-sources.ts` and `adopt-root-document.ts` split 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.ts` and `open-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) and `app-command-overlays.tsx` (the palette and the shortcut sheet it opens), `use-app-commands.ts` (the command type, the palette's `runCommand` and the window's one shortcut listener, ⌘/Ctrl keys and F8 alike; a Synara frame hands Lattice's keys back to it through `src/agent/synara-host-shortcuts.ts`, and ⌘W closes a document tab through `src/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.ts` and the lazy `shortcut-sheet.tsx` (the ⌘? sheet, built from the command table, `src/trellis/trellis-keymap.ts`, `src/editor/editor-keymap.ts`, `src/editor/latex/latex-shortcuts.ts` and `src/editor/markdown/engine/engine-shortcuts.ts`, which also bind those keys), and focus mode's `focus-mode-bar.tsx` and `use-focus-mode-escape.ts` (the layout side is a Trellis layout state in `trellis-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.tsx` and `use-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 are `app-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 in `src/trellis/`. The document canvas, the compiled-project PDF panel (both built by `use-document-surfaces.tsx`) and Settings are App's `surfaces` prop (`app-surfaces.ts`), which `main.tsx` fills from `production-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) — the `try/catch` bodies 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/node` by design** — it is browser-only by convention.
- **`src/styles/editor-workspace.css` is the largest stylesheet**;
`src/App.css` is a short import manifest, so don't look for CSS there.