mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-09-11 21:39:01 +00:00
00d529d325
* fix(editor): add NodeView update() hook to AttachmentChip (TASK-1251) Audit confirmed the same gap pattern as BUG-1246 (mermaid) / TASK-1250 (attachment-image): closure-captured uuid + filename, no update() hook, so any attr change forces NodeView destroy+recreate. No in-tab attr-change source today (no rotate/crop on chips), but the upcoming Yjs collab work makes peer-driven uuid/filename swaps the common case — destroying the NodeView on every peer keystroke of a rename would jump the cursor and flicker the chip. Refactor closure state to be mutable, add update() that: - Returns false on type mismatch (defensive — should never fire for an attachmentChip). - On uuid change: refresh href + data-attachment-id, reset MIME + size pending re-probe, re-fire the HEAD metadata fetch (with in-flight guard so a stale probe doesn't trample fresher state). - On filename change: refresh data-filename, download attribute, visible name, and (only when MIME unknown) re-derive the filename-extension fallback icon. Click handler now reads currentUuid (mutable) so a peer-swapped chip target opens the new attachment, not the original. Parent: PLAN-1248. Phase 0 — Production prerequisites complete. * fix(editor): always refresh chip icon on rename per Codex review (round 1) P3: refreshIcon() falls back to the filename-extension heuristic whenever iconForMime() returns empty — both MIME-unknown and MIME-known-but-unmapped (e.g. application/octet-stream) hit the same fallback path. The previous `if (!currentMime) refreshIcon()` was too narrow; renaming foo.csv → foo.pdf after a generic MIME resolved would leave the stale .csv icon until the NodeView was recreated. Drop the conditional. refreshIcon() is idempotent for MIMEs that DO map to a specific icon (just rewrites the same emoji), so calling it on every filename change is correct and trivially cheap.
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
activity/ ActivityFeed
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