Files
PubScript/CHUNK#3.md
T

63 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CHUNK #3 — Writing Surface
**Duration:** ~3 days · **Prereqs:** CHUNK #2 (storage spine, autosave) · **Follows:** CHUNK #2 · **Unblocks:** CHUNK #6 (export), #7 (spellcheck), #9 (AI suggestions render over this editor)
**Purpose:** Build the actual writing experience: the TipTap 3 editor with the full node schema, the document tree, the font system, focus/typewriter modes, live stats, and Markdown import/export — performing well enough that a 100k-word project opens in under 2 seconds and typing never lags.
---
## Steps
### 1. Editor package (`packages/editor`)
1. `packages/editor/src/schema.ts` — TipTap extensions, all registered once:
- Base: `Document`, `Paragraph`, `Text`, `Heading` (1–3), `Blockquote`, `HorizontalRule`, `HardBreak`, `Bold`, `Italic`, `Strike`, `Code`, `CodeBlock`.
- **`SceneBreak`** (node) — a centered `* * *` divider, draggable, with a NodeView; splits scenes for stats and AI targeting.
- **`Footnote`** (inline + a `FootnoteArea` block at doc end) — v1 simple: number, reference, definition.
- **`CharacterMention`** (inline atom) — rendered as a styled chip linking to a character entity (inserted by autocomplete in Chunk 4; the node type lands here).
- Markdown I/O: `prosemirror-markdown` with rules for scene breaks (`***`), mentions (`@Name`), footnotes.
2. `packages/editor/src/Editor.tsx` — the headless wrapper:
- `createEditor({ doc, onUpdate, decorations })` returning `{ view, state, dispatch, applyTransaction }`.
- **Decoration pipeline:** `useDecorations(view, sources[])` — merges decorations from spellcheck (Chunk 7), AI suggestions (Chunk 9), and mentions into one `DecorationSet`, memoized, recomputed only on relevant transactions. This is the seam later chunks plug into — build it now, even with zero sources.
- `applyEdit(range, before, after)` — the transaction used by "Accept suggestion".
3. `packages/editor/src/perf.ts` — `measureTypingLatency()` (rAF-based) + a dev overlay showing ms/keystroke.
### 2. Writing view (`apps/web/src/features/write/`)
1. `WritingView.tsx` — layout: document tree (left, from Chunk 2), editor (center), context rail (right: current chapter's linked characters/notes, collapsible).
2. **Toolbar** — floating (appears on selection) + docked (top): headings, bold/italic/strike, blockquote, scene break, footnote, mention, undo/redo. Slash menu (`/`) for block insertion.
3. **Keyboard map** (`packages/editor/src/keys.ts`): `Ctrl/Cmd+B/I/K`, `Ctrl+/` scene break, `Alt+↑/↓` chapter nav, `F2` focus mode.
4. **Focus mode** — dims everything but the current paragraph band; **Typewriter mode** — auto-scrolls the active line to a fixed vertical position. Both toggle from toolbar + settings.
5. **Stats bar** (footer): word count (chapter/book/project), session words, reading time; per-chapter counts in the tree.
6. **Font system** (`packages/ui/src/fonts.ts`): bundle via `@fontsource` — **EB Garamond**, **Source Serif 4**, **Inter**, **Space Grotesk**, **JetBrains Mono** (+ weights). Settings controls: family, size (14–22 px), leading (1.3–2.0), measure (50–90 ch), margins. Apply via CSS variables on the editor root; persist in settings store.
7. **Markdown I/O:** `exportMarkdown(doc)` / `importMarkdown(text)` in `packages/editor/src/markdown.ts`. Import dialog in the tree ("Import .md…") with a preview + conflict check; per-chapter and whole-project export (feeds Chunk 6).
### 3. Performance work
1. **Lazy chapters:** only the open chapter's doc is in the editor; switching chapters swaps state without unmounting the view.
2. **Virtualize the tree** for books with 500+ chapters (`@tanstack/react-virtual`).
3. Add a Playwright perf test (`apps/web/e2e/perf.spec.ts`): seed a 100k-word doc (script generates 40k paragraphs), assert: open < 2 s; 20 keystrokes median latency < 16 ms (using the dev overlay's API); tree scroll at 60 fps.
### 4. Polish
1. Placeholder states (empty book, empty chapter), save indicator already wired (Chunk 2).
2. Accessibility: visible focus rings, `aria` on toolbar/tree, full keyboard operation of the tree.
3. `docs/EDITOR.md` — node reference (for humans) + "how to add a node type" (for agents).
---
## Acceptance criteria (all must Pass)
- [ ] Type a full chapter: no dropped keystrokes; latency overlay < 16 ms median on a 100k-doc.
- [ ] 100k-word seeded project opens < 2 s (Playwright perf test in CI).
- [ ] Scene breaks insert via `***` and slash menu; stats split by scene.
- [ ] Footnotes render and export to Markdown correctly.
- [ ] Font/size/leading/measure changes apply live and persist across reload.
- [ ] Focus + typewriter modes work and are keyboard-toggleable.
- [ ] Import a 50-chapter `.md` file → tree + docs correct; re-export round-trips (diff of re-imported Markdown is empty modulo whitespace).
- [ ] Decoration pipeline: register a fake decoration source → renders; (Chunk 9 will replace it with real AI suggestions.)
- [ ] A11y pass: toolbar fully keyboard-operable; contrast ≥ 4.5:1 in all four themes.
## Definition of done
An author can sit down and *write* — fast, beautiful, with the story structure around them — and the editor exposes a stable decoration/edit API that spellcheck and AI plug into without touching core code.
## Risks & fallbacks
- **TipTap v3 API churn** → pin the exact v3 release; wrap all v3-specific calls in `packages/editor` so the app never imports TipTap directly.
- **Markdown round-trip loss** (footnotes/mentions) → keep a canonical ProseMirror-JSON store; Markdown is an *exchange* format, not the source of truth (document this in `docs/EDITOR.md`).
- **Perf test flakiness in CI** → run perf specs on a dedicated runner with 3 runs, take the median; fail only on >2× regression.