Commit Graph

25 Commits

Author SHA1 Message Date
Schwitzz 668470da2b fix(auth): unify Desktop and Web SQLite DB path and fix browser autofill crash on Input/AuthGate 2026-10-30 16:18:17 -05:00
Schwitzz 65d06e1681 fix: pin Writing toolbar and Entities header, fix Entities form alignment and red hint text, and make Settings category bar horizontal 2026-10-30 15:49:24 -05:00
Schwitzz 29dbc27c41 feat: add Web App Port management alongside API Server Port and fix square 512x512 app launcher icon 2026-10-30 14:23:12 -05:00
Schwitzz 1d209fedd2 feat: add tabbed Settings, 10 themes with custom accent/text color pickers, main project directory selector, and export filename/directory controls 2026-10-30 14:06:46 -05:00
Schwitzz 951aa81055 feat: peg AI inline suggestions to granular text spans, add autosave revision history with restore, fix focus mode, and make PS logo background transparent 2026-10-30 12:49:43 -05:00
Schwitzz 2940e3c9aa fix: preserve AI line highlight against ProseMirror DOMObserver and prevent crash on accepting suggestions 2026-10-30 12:20:13 -05:00
Schwitzz 271d1e2c79 feat: project dropdown in sidebar, resizable AI sidebar with snap, ctx & t/s telemetry, live line-number re-anchoring, and thinking indicator 2026-10-30 11:53:32 -05:00
Schwitzz b1c22229c5 feat: use enlarged single PS logo and add line-numbered AI suggestions with highlight/accept/deny/copy-to-note 2026-10-30 11:41:00 -05:00
Schwitzz 4add429469 feat: resolve all 22 items in Features and Bugs.txt and complete UI polish 2026-10-30 10:57:13 -05:00
Schwitzz a1b5587fcb server: reconcile ai_sessions persistence to Chunk 9 AISession shape
Reshape the ai_sessions table (migration 003) and mapping/routes to the
full AISession contract: title, kind, notes, created_at, last_message_at;
drop legacy role/model/settings. toAISession now maps the new columns and
routes create/patch/messages handlers use the new shape.
2026-10-29 15:03:32 -05:00
Schwitzz fc8eaa86c3 server: fix project_shares audit logging and add share transfer
- Fix PATCH /api/projects/:id/shares/:userId: query SELECT 1 instead of
  SELECT id (project_shares has a composite PK, no id column)
- Add audit logging to existing share grant/revoke routes
- Add POST /api/projects/:id/transfer for ownership handoff
- Remove inline admin user/audit routes from routes.ts (now in admin.ts)
- Remove unused requireAdmin import and userPatch schema from routes.ts
2026-10-29 10:44:58 -05:00
Schwitzz 1e16b0d5d2 CHUNK #9: AI session storage layer (AgentController, AIStore, builders)
- Add packages/ai: AgentController (streaming + budget), SuggestionBuilder,
  NoteBuilder, AIStore (Dexie/IndexedDB), SuggestionMutation
- Fix createUlid to use monotonicFactory for reliable listSessions sort tiebreaker
- Extend outline system prompt with setting + characters context
- Add dexie dependency to packages/ai
- 188 tests passing (core 47, ui 5, editor 30, ai 71, exporters 22, web 13)
2026-10-29 07:31:25 -05:00
Schwitzz 4c4b83bb68 feat(ai): add packages/ai provider layer (Chunk #8)
Self-contained SSE-based provider layer for OpenAI-compatible endpoints
(Ollama/llama.cpp/LM Studio/vLLM), Anthropic, Gemini, and OpenRouter.

- budget.ts: re-export core token-budget engine (countTokens,
  allocateBudget, DEFAULT_BUDGET)
- client.ts: streamCompletion/collectStream/complete with budget
  enforcement via enforceBudget + measureBudget
- discover.ts: auto-detect local providers by probing common /v1/models
- adapters.ts: per-provider param mapping + streaming adapters + factory
- thinking.ts: thinking/reasoning-param normalization per provider
- http.ts: SSE parsing + 429 retry
- errors.ts: BudgetExceededError

35 unit/integration tests pass; typecheck and lint clean.
2026-10-28 18:59:54 -05:00
Schwitzz 5c28a94813 feat(editor): spell & grammar checking (Chunk #7)
Offline spellcheck via hunspell compiled to WASM (runs fully in-browser),
plus optional self-hostable LanguageTool grammar/style checking.

- hunspell-wasm spell engine with bundled en_US dictionary (base64-decoded
  browser-safe via atob + TextDecoder)
- LanguageTool engine (self-hostable, docker compose snippet under
  docker/languagetool/)
- single decoration pipeline: spell + grammar hits merge into one squiggle
  layer (red=spelling, blue=grammar) with a side panel to jump/apply/ignore
  and add-to-dictionary
- useCheckService hook wired into WritingView; check.css loaded in main.tsx
- spell + grammar unit tests (30 editor tests green)

PLAN.md and CHUNK#7.md updated to reflect completion.
2026-10-28 17:13:23 -05:00
Schwitzz 836d82113b fix(editor): use browser-safe base64 decode for en_US dictionary
The bundled en_US Hunspell dictionary was decoded with Node's Buffer,
which is undefined in the browser and crashed React on app load
(Blank UI). Switch to atob() + TextDecoder, available in both the
browser and Node 18+, so the dictionary imports cleanly in Vite.

Fixes the blank UI reported after dev server startup.
2026-10-27 14:32:08 -05:00
Schwitzz d310635b65 feat(editor): add spell & grammar check engines with decorations
Wire real spell-checking into the editor via hunspell-wasm (bundled en_US
dictionary) and an optional LanguageTool grammar client.

- spell/: HunspellSpellEngine wrapper, pure spell logic, base64 en_US dict
- grammar/: LanguageToolEngine grammar client and pure grammar logic
- decorations/: collectBlock text assembly, offset-mapped CheckService,
  buildCheckDecorations (flat DecorationSet, inline spell/grammar marks)
- export engines and types from editor index; add hunspell-wasm dep

Verified: typecheck (6 pkgs), tests (all pass), lint (6 pkgs), and web
build all green.
2026-10-27 12:52:29 -05:00
Schwitzz 2a522977e9 feat(ui): incorporate PubScript logo as favicon & Topbar mark
Recreate the gold-rimmed 'PS' typewriter-tile emblem as scalable SVGs:
- public/logo.svg — dark-ground emblem used as the app favicon
- public/logo-mark.svg — transparent mark placed in the Topbar beside
  the page title, with a subtle hover scale

Add <link rel=icon> to index.html and a .ps-topbar-logo rule in
AppShell.css.
2026-10-26 23:53:43 -05:00
Schwitzz 978858362d feat(export): Chunk #6 Export Pipeline — 7 exporters, ExportDialog & tests
Add the @pubscript/exporters package exporting a flattened ProseMirror
project to Markdown, HTML, EPUB, DOCX, ODT and PDF, plus a deterministic
generatedAt timestamp threaded through every format (ZIP entry dates,
PDF /ID, DOCX dc:created/modified, EPUB id, HTML/XML footers) so golden
files are byte-stable.

Wire an ExportDialog into the web app (Topbar trigger, App route) with
format selection, theme pick, and download helpers.

Add round-trip and golden-file determinism tests (22) covering
non-empty output, byte-identical re-exports, Markdown round-trip through
the editor, and EPUB/ODT ZIP + mimetype + XML validation.
2026-10-26 23:30:47 -05:00
Schwitzz 45f5c505c9 feat(plot): Chunk #5 story whiteboard, timeline & plot beats
Story whiteboard built on React Flow v11: free-form canvas of typed
nodes (character, world, chapter, note, image, group) with draggable
leads-to/contrasts/causes edges, cross-link selectors (chapter/entity),
linked-node badges, and an "Add beats to board" import from the plot.

Plot beats feature set:
- Status/locking (draft/revised/locked) with a progress bar
- Beat templates (three-act, save-the-cat, seven-point, custom)
- Timeline route with search/chapter/character filters
- Entity cross-links (entityIds) on beats and board nodes
- getBoard added to the StorageAdapter contract (local + server) and
  REST endpoints; loadBoard now restores persisted board state

Storage & server:
- PlotBeat type (status, entityIds) across core types, Dexie schema,
  and server mapping/routes/migration (002_plot_beats.sql)
- SQLite-safe column names (beat_order, entity_ids)
- Server seed data for plot beats

UI: accent Button variant, Badge variants, plot/timeline/whiteboard
stylesheets. Tests: templates module, plot-beat store round-trip,
board getBoard. typecheck, lint, test, and build all green.
2026-10-26 19:00:07 -05:00
Schwitzz 301e842c5c feat(entity): Chunk #4 remaining items — consistency UI, context rail, palette search, expanded seed & DATA_MODEL
- Consistency tab in EntitiesView surfacing findInconsistencies() results
- ContextRail + MentionPopover wired into WritingView for entity context/hover
- CommandPalette indexes entities and chapters for cross-entity search
- link field type for entity references in character fields
- findDeadCharacterMentions consistency check + tests
- Expand seed with a location, note, and a natural Elias Vell mention so the
  dead-character-after-death case is actually exercised
- Author docs/DATA_MODEL.md documenting core schemas, types, storage contract
- Rename CharacterMention id attr -> entityId to match core backlinks/consistency
2026-10-26 12:02:03 -05:00
Schwitzz 4efa71515e feat(entity): Chunk #4 data layer + entity UI (characters & world-building)
Data layer:
- Fuzzy scorer (packages/core/src/fuzzy.ts) with index recovery + unit tests
- Backlink computation (packages/core/src/backlinks.ts) incl. entity-level backlinks
- Consistency checker (packages/core/src/consistency.ts) incl. reciprocity/scope + unit tests
- StorageAdapter contract extended with listEntities/searchEntities/saveEntity/deleteEntity;
  implemented in Local (Dexie) and Server adapters; contract tests cover entity CRUD + ?q= search
- WorldItem.fields now supports list values (keyPeople, rivals); expanded RelationshipKind
- @mention autocomplete extension + picker in the editor package

Entity UI (apps/web/src/features/entities/):
- Generic field renderer (EntityFields) driven by ENTITY_FIELDS/FieldDef
- Tabbed directory with fuzzy search + entity cards (EntitiesView)
- Create/edit/delete drawer (EntityEditor) reusing shared fields
- Relationship editor + force-directed SVG relationship map
- Entity engine stylesheet; wired into nav

Store: project-store entity methods (load/create/update/delete) over the StorageAdapter contract.

CI-green: typecheck, lint, 65 tests, and web build all pass.
2026-10-25 22:42:55 -05:00
Schwitzz 015e18ff96 fix(web): pass full ProseMirror doc to editor, not bare array
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.
2026-10-25 16:01:14 -05:00
Schwitzz bb68d9d5d2 CHUNK #3: Writing Surface — editor wiring, storage view, polish
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.)
2026-10-25 13:57:07 -05:00
Schwitzz c95f5e485a editor: fix circular import that dropped PubScript nodes from schema
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).
2026-10-25 02:37:05 -05:00
Schwitzz 1c9b531996 Implement CHUNK #2: server layer, core schemas/adapters, contract tests, seed
Complete the server-backed storage layer for PubScript so the app can run
against a real Fastify + SQLite backend instead of only local IndexedDB.

Server (@pubscript/server):
- db.ts: better-sqlite3 schema + migrations (users, shares, projects, books,
  chapters, entities, boards, ai_sessions, audit); WAL mode; idempotent
  admin bootstrap via PUBSCRIPT_ADMIN_EMAIL/PASSWORD.
- auth.ts: argon2 hashing, JWT-ish session tokens, guards, auth routes.
- routes.ts: REST CRUD for projects/books/chapters/entities/boards/ai_sessions
  with multi-tenant isolation and optimistic concurrency (baseRev).
- ws.ts: Fastify websocket hub for autosave + presence events.
- mapping.ts: row <-> core type conversion.

Core (@pubscript/core):
- Zod domain schemas (projects, books, chapters, entities, boards, ai_sessions)
  exported from index.
- storage-local.ts (IndexedDB/Dexie) and storage-server.ts (REST) adapters
  implementing the StorageAdapter contract.

Tests & tooling:
- storage.contract.test.ts: parameterized over both adapters; wired as
  `pnpm contract` and added to CI.
- seed.ts (`pnpm --filter server seed`): creates demo project "The Glass Harbor"
  — 3 chapters of prose, 4 linked characters, 1 world location, 6-node board.
- Dockerfile + compose wiring for the server runtime.

Acceptance: seed project opens with 3 chapters, 4 linked characters, 1 board.
2026-10-24 18:31:20 -05:00