mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-09-11 13:28:57 +00:00
cd5c5702d4
Lead ruling on PR #1241, and the right call. The extraction had silently moved the add-relationship search onto the warm local path, and `localIndex` strips `content` by design — so a user who links an item by a phrase they remember from its BODY lost that, with no signal anything had changed. Consistency with the other pickers does not buy back a capability under CONVE-139. `source` is now an explicit MODEL choice, not a performance one: 'index' (default) — `localSearch` over title / ref / tags / parent / field values, no network call, server only as a cold fallback. Right for a RELATION field, where you are choosing a row from a known collection and know what it is called. U2 onward take this. 'server' — always `/search`, whose FTS also indexes body content. Right for the Relationships tab, where you are finding an item you remember rather than one you can name, and what it did before this component existed. ItemDetail passes it. An empty-query LISTING stays on the index for both: it is not a search, `/search` cannot answer one (it requires a `q`), and the rows are local either way. Only QUERIES follow `source`. Two supporting changes fall out of it rather than being bolted on: **`rawResults` + a derived `results`.** The exclusion filter is now part of the derivation, so a late `excludeIds` — `ItemDetail` loads `itemLinks` asynchronously — re-filters on its own. Without that, honouring a late exclusion on the server-backed caller would have meant re-issuing the request, which is the rate-limiter pressure the debounce exists to avoid. The refresh effect no longer needs `excludeIds` as a dependency at all, and server-sourced QUERIES are explicitly not re-run on an index delta. **The highlight is an ID, not an index.** `activeId` is state; `activeIndex` derives from it. Identity survives the list changing underneath — a delta, a late exclusion — where an index silently moves the highlight onto whatever slid into that position. This deletes the hand-rolled preserve/restore that lived in the effect, so no future site that changes the list has to remember to do it. Pins, per the ruling: the server caller queries `/search` with a hydrated index and never touches `localSearch`; the control leg asserts the default source on the same warm index never reaches the network; and a source-level test asserts ItemDetail's call site still carries `source="server"` — a regression invisible from the component's own tests, which is why it is asserted at the call site. Verified in a real browser against a locally built binary with a marker string present ONLY in an item's body and never in its title, so the local index cannot answer it: the Relationships picker finds it, arrows to it, and creates the link. Mutation matrix 20 of 20 killed, including the three new ones — ignore `source` (3 failed), re-query on a delta (1 failed), drop `source="server"` at the call site (1 failed). Gates: `npm run check` 1092 files 0 errors, 6 pre-existing warnings; full web suite 121 files / 2041 tests green.
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