mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-10-26 14:30:13 +00:00
3aebbb58f0
No P1s. Two P2s and a P3, all addressed. THE RELOAD IS EXCEPTION-SAFE NOW. `clearPersistentIdentityState()` ran outside any try/finally, so a throw on the way through would have skipped `location.reload()` entirely — leaving the tab mounted with the previous user's page under the new identity, which is the state this whole fix exists to prevent, reached by the fix itself failing quietly. Latent today (the only uncaught call is a `Map.clear()`), and the ordering is now structural rather than incidental. A TEST THAT REPORTED PASSED WHILE THE PAGE CRASHED. The starred-page leg used `mockReturnValueOnce` with no default, so a later call resolved `undefined`, `items.map` threw, and vitest showed a green test with an unhandled error beside it — the assertion was reading a page that had crashed rather than a fence that had refused. Every mock carries a default now, `vi.resetModules()` is gone (it handed the test a different authStore instance than the component closed over), and there is a counterfactual leg where the identity holds still and the data DOES render. AND THE SAME LEG'S MUTANT SURVIVES, which is in the file rather than left for the next reader to discover. Removing the page's identity fence leaves it green: the page's own `$effect` re-runs during the transition and advances `loadSeq`, so the NAVIGATION fence refuses A's settle first and the identity fence never decides anything there. The leg stands as an end-state regression fence — A's items must not appear — and the identity fence's coverage of the pre-reload window is an argument, not a measurement. Recorded because a surviving mutant filed as a passing test is exactly how this file already shipped one assertion pointed the wrong way. P3: a trailing blank line `git diff --check` flagged. Full web suite 2457 passed, no unhandled errors; svelte-check 0 errors; `git diff --check` clean. Claude-Session: https://claude.ai/code/session_0115vpkjH5mmFdjRdRmZRuZt
Pad Web UI
SvelteKit 2 + Svelte 5 frontend for Pad, compiled to static files and embedded into the Go binary.
Development
npm install
npm run dev # Dev server at localhost:5173 (proxies API to localhost:7777)
npm run build # Production build to build/
npm run check # Type checking with svelte-check
When developing, run the Go backend separately with make dev from the project root.
Building for Production
Do not build in isolation. Always use make build from the project root — this builds the web frontend, then compiles the Go binary with the build output embedded via //go:embed.
Stack
- Svelte 5 with runes (
$state,$derived,$effect) - SvelteKit 2 with
adapter-static(SPA mode) - Tiptap block editor with markdown round-trip
- svelte-dnd-action for drag-and-drop in board/list views
- SSE for real-time updates
- TypeScript throughout
Structure
src/
routes/ SvelteKit pages
+layout.svelte App shell (sidebar + main)
+page.svelte Landing/redirect
[workspace]/
+page.svelte Dashboard (collections, phases, activity)
+layout.svelte SSE connection per workspace
[collection]/
+page.svelte Collection view (board/list)
[collection]/[item]/
+page.svelte Item detail + editor
conventions/ Purpose-built conventions page
playbooks/ Purpose-built playbooks page
settings/ Workspace settings
lib/
api/client.ts HTTP API client
components/
layout/ Sidebar, navigation
editor/ Tiptap editor, raw markdown editor
fields/ FieldEditor, relation picker
items/ ItemCard, ItemDetail
collections/ BoardView, ListView
common/ StatusBadge, badges, modals
search/ CommandPalette
stores/ Svelte 5 reactive stores
workspace.svelte.ts Workspace state
collections.svelte.ts Collection + item state
ui.svelte.ts Sidebar, mobile state
types/index.ts TypeScript types and constants
app.css Global styles and design tokens