diff --git a/CHUNK#3_PROGRESS.md b/CHUNK#3_PROGRESS.md new file mode 100644 index 0000000..feb7bff --- /dev/null +++ b/CHUNK#3_PROGRESS.md @@ -0,0 +1,31 @@ +# CHUNK #3 β€” Editor & Writing View + +**Status:** In progress. Steps 1–3 committed. Step 4 (polish) in progress. + +## Progress +- [x] Step 1: Editor package (TipTap 3 schema, wrapper, markdown, key bindings) β€” committed +- [x] Step 2: Writing view in apps/web wired to storage adapter + - [x] Add @pubscript/editor to web deps + - [x] project-store (zustand) over storageLocal + - [x] client-side seed for local adapter + - [x] WritingView component (editor + autosave + word/scene count + chapter nav) + - [x] wire Write route +- [x] Step 3: Performance (autosave via createAutosave, idle detection via createIdleDetector) +- [ ] Step 4: Polish (editor.css import βœ…, fonts 🟑, SVG monogram logo βœ…) + - [x] editor.css import in main.tsx + - [x] SVG monogram logo in Sidebar + - [x] Sidebar reflects the real seeded project ("The Glass Harbor") + live word count + - [ ] font family/size/leading/measure controls wired to CSS variables (size only so far) + +## Key facts +- Editor package exports: usePubScriptEditor, createEditorInstance, emptyDecorationSource, throttle, createIdleDetector, serializeToMarkdown, parseMarkdown, node types + constants. +- PubScriptEditor React component re-exports EditorContent + Editor type from @pubscript/editor (web never imports TipTap directly). +- storageLocal (offline-first, IndexedDB) chosen for web wiring (server routes require auth). +- Demo prose lives in server/src/seed.ts ("The Glass Harbor", 3 chapters, 4 chars, 1 world, 1 board). +- Web app uses @pubscript/core/local (storageLocal) β€” no server needed to run offline. +- Server runs on PORT (default 8080; set PORT=8081 when 8080 is taken by Docker WordPress). + +## Remaining (finish Step 4) +- Wire font family/leading/measure settings to CSS variables (fontSize already wired via applyFontSettings). +- Add a settings UI control for font size (or confirm existing one applies). +- Final polish pass: focus mode visual, empty-state copy, a11y labels. diff --git a/PLAN.md b/PLAN.md index ec0a1f1..93fb61b 100644 --- a/PLAN.md +++ b/PLAN.md @@ -269,7 +269,7 @@ Each chunk is a 1–3 day work order in `CHUNK#n.md`, written so a human *or* a |---|---|---|---|---| | 1 | **Scaffold & Foundations** | 1 | βœ… Done | Monorepo, strict TS, tooling, app shell, theme system, CI, settings store | | 2 | **Data Model, Storage & Auth** | 3 | ⬜ Not started | Full domain types, StorageAdapter, Local + Server adapters, Fastify auth/multi-tenant, autosave, contract tests | -| 3 | **Writing Surface** | 3 | ⬜ Not started | TipTap editor, document tree, fonts, focus mode, Markdown I/O, performance | +| 3 | **Writing Surface** | 3 | 🟑 In progress | TipTap editor βœ…, document tree βœ…, focus mode βœ…, Markdown I/O βœ…, autosave βœ…, fonts/logo polish 🟑 | | 4 | **Characters & World-Building** | 2 | ⬜ Not started | Entity schema, character sheets, world items, search, mentions, backlinks | | 5 | **Plot & Whiteboard** | 3 | ⬜ Not started | Beat board + templates, timeline, React Flow whiteboard (200-node perf) | | 6 | **Export Pipeline** | 3 | ⬜ Not started | MD/HTML/EPUB/DOCX/ODT + PDF, export dialog, CI validation | diff --git a/apps/web/package.json b/apps/web/package.json index 0a4a138..cddcf21 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -13,6 +13,7 @@ }, "dependencies": { "@pubscript/core": "workspace:*", + "@pubscript/editor": "workspace:*", "@pubscript/ui": "workspace:*", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index f34f316..393803a 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -2,7 +2,7 @@ import { useState, useEffect } from "react"; import { Routes, Route } from "react-router-dom"; import { Sidebar, Topbar, CommandPalette } from "./components/index.js"; import { ToastViewport } from "@pubscript/ui"; -import { Write } from "./routes/Write.js"; +import Write from "./routes/Write.js"; import { Characters } from "./routes/Characters.js"; import { Plot } from "./routes/Plot.js"; import { Whiteboard } from "./routes/Whiteboard.js"; diff --git a/apps/web/src/components/Sidebar.tsx b/apps/web/src/components/Sidebar.tsx index 0ad9836..ce6fad9 100644 --- a/apps/web/src/components/Sidebar.tsx +++ b/apps/web/src/components/Sidebar.tsx @@ -1,4 +1,5 @@ import { NavLink } from "react-router-dom"; +import { useProjectStore } from "../store/project-store.js"; export interface NavItem { to: string; @@ -11,19 +12,51 @@ export interface SidebarProps { nav: NavItem[]; } +/** A minimal view of a chapter document for word counting. */ +interface ChapterDoc { + doc: { content?: Array<{ text?: string }> }; +} + +/** Total word count across every chapter document in the active project. */ +function totalWords(chapters: ChapterDoc[]): number { + let total = 0; + for (const chapter of chapters) { + for (const node of chapter.doc.content ?? []) { + if (node.text) total += node.text.trim().split(/\s+/).filter(Boolean).length; + } + } + return total; +} + export function Sidebar({ nav }: SidebarProps) { + const project = useProjectStore((s) => s.project); + const chapters = useProjectStore((s) => s.chapters); + const title = project?.title ?? "Untitled project"; + const wordCount = totalWords(chapters); + return (