mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-09-24 11:26:34 +00:00
350e8ef576
* feat(web): saved view defaults applied on collection-page entry (TASK-1366) Phase 3d of the local-first read model (PLAN-1343 / DOC-1342): per the design note's recommendation, persist the user's preferred saved view per (workspace, collection) in localStorage and re-apply it on mount. No schema change in v1; cross-device sync can come later as a server-side `is_default` column on saved views. Behavior - localStorage key: `pad-default-view:<wsSlug>:<collSlug>` → view id. - On collection-page mount, after `savedViews` loads, look up the default and call `applyViewConfig` automatically. - URL-driven state wins: if the user arrived via a shared link with `?q=...` or `?status=...`, the default is skipped so the link's intent isn't hijacked. - "Make default" / "Default ★" toggle next to the saved-views bar flips the persistence state for the currently active view. The active default also renders a small pin icon on its tab so users can see which view is current at a glance. - `deleteView` clears a dangling localStorage pointer when the default view is the one being removed. - localStorage failures (private mode, quota) degrade silently — the toggle still works for the session. Out of scope (deferred to a future PR) - Cross-device sync via a server-side `is_default` column. - Sharing defaults across workspace members. - Auto-save view config changes back to the underlying saved view. Parent: PLAN-1343. Completes Phase 3 of DOC-1342. * fix(web): gate default-view apply on metaLoading per Codex review (round 1) Codex round 1 P1 #1: `loadCollection` flips `metaLoading=true` at entry, assigns `savedViews` mid-flight, then calls `loadUrlFilters()` synchronously near the end, and only flips `metaLoading=false` in the `finally` block. My default-view effect tracked `savedViews` and ran as soon as it changed — so a shared link like `?q=foo` could land in the local state AFTER the savedViews assignment but BEFORE `loadUrlFilters` populated `searchQuery`. The effect saw an empty searchQuery, thought there were no URL overrides, and applied the default — clobbering the incoming URL. Codex round 1 P1 #2: on client-side navigation across collections, `defaultViewApplied` got reset by the route-change effect but `savedViews` still held the PREVIOUS collection's views until the new fetch resolved. The default-apply effect would run with the wrong list, fail to find the new collection's default view id in the stale list, and ERASE the localStorage pointer — wiping the default on every cross-collection navigation. Gate the effect on `!metaLoading`. `metaLoading=false` only fires after BOTH the new `savedViews` is assigned AND `loadUrlFilters()` has run, so both races are eliminated. * fix(web): URL-override check reads page.url directly per Codex review (round 2) Codex round 2 P1: `?view=board` URLs are explicit user intent but my urlOverrides check only looked at `searchQuery` and `activeFilters`, so view-only URLs would be overwritten by the default-view apply. Codex round 2 P2: `loadUrlFilters` doesn't clear absent params, so parsed `searchQuery` / `activeFilters` can carry leftover values from the previous route on cross-collection navigation. A clean URL on the new route would then look "overridden" via stale parsed state, and the default would be incorrectly skipped. Both fixed by reading `page.url.searchParams.size` directly. The collection page only writes user-driven params (view, q, field filters), so any non-empty searchParams signals explicit intent.
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