WritingView passed chapter.doc.content (a bare ProseMirror node array)
as the editor content, which made EditorState.create build a doc node
with an undefined type, throwing 'Cannot read properties of undefined
(reading schema)' and unmounting the write view. Pass the full doc
object ({ type: 'doc', content }) on init and on setContent sync.
Add editor regression tests covering schema instantiation, node
registration, and doc-content round-trip.
Complete the Writing Surface chunk: wire the web writing view to the
offline-first storage adapter, finish the editor package exports, and
apply Step 4 polish (SVG monogram logo, real project in the sidebar,
font settings → CSS variables).
Editor package
- Re-export EditorContent + Editor type from @pubscript/editor so the
web app never imports TipTap directly.
Web writing view (apps/web)
- project-store (zustand) over storageLocal: project, chapters, active
chapter, autosave via createAutosave, idle detection via
createIdleDetector.
- Client-side seed mirroring the server seed ("The Glass Harbor").
- WritingView component: editor + autosave label + word/scene count +
chapter nav; wire the /write route inside AppShell.
- Import editor.css in main.tsx.
Core storage contract
- Extend StorageAdapter with createBook/listBooks/listChapters;
implement in local + server adapters; update contract test mock.
Polish
- Sidebar: SVG monogram logo + live project title/word count instead
of the placeholder "The Silent Cartographer".
- Fonts: applyFontSettings() in the theme engine writes --ps-font-*
variables; .ps-content reads --ps-font-size/leading/measure; settings
store applies fontSize on boot and change.
Docs
- PLAN.md: CHUNK #3 → in progress.
- CHUNK#3_PROGRESS.md: steps 1–3 done, step 4 polish in progress.
Verified: full workspace typecheck clean, lint clean, core 24 / ui 5 /
editor 7 tests pass, web build clean. (server vitest hits EADDRINUSE on
8080 — pre-existing Docker WordPress port conflict, unrelated.)
The entry point defined and exported start() but never called it, so
running 'node --import tsx src/index.ts' imported the module and exited
0 without ever calling app.listen. Add a top-level start().catch() so the
server binds and logs its listen addresses.
The TipTap node definitions imported their node-name constants from
schema.ts, while schema.ts imported the nodes to build the schema. This
circular dependency evaluated the nodes before the constants were
assigned, leaving sceneBreak/footnote/footnoteArea/characterMention with
name 'undefined' — so they never registered in PubScriptSchema. As a
result <hr class="scene-break"> parsed as StarterKit's horizontalRule
instead of a scene break.
- Move node-name constants, groups, MentionAttrs, and colorAttribute into
a new constants.ts module that both schema.ts and the node files import
from, breaking the cycle.
- Remove the redundant separate History extension (already in StarterKit),
which also silenced a TipTap 'Duplicate extension names: undoRedo' warn.
- Drop unused TipTap extension and prosemirror-* deps (all access goes
through @tiptap/pm).