mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-09-24 11:26:34 +00:00
99e8802095
* feat(board): wire bulk lane actions via the bulk endpoint (TASK-1672)
Extract LaneActionsMenu.svelte — a drill-down kebab menu — and populate
it with the full mutation set, each operating on the lane's CURRENTLY-
FILTERED items via api.items.bulk:
- Archive all (count + "(filtered)" in the confirm)
- Move all to ▸ (other status lanes; status-grouped boards only)
- Tag all ▸ (free input + workspace tag suggestions)
- Untag all ▸ (tags present on the lane's items)
- Set priority ▸ (the priority field's options)
- Assign all ▸ (workspace members by name)
Page handlers funnel through a shared runBulk() that calls the bulk
endpoint once, deltaSyncs, and toasts the updated/failed counts; it
returns the affected ids for TASK-1674's undo. BoardView threads the
lane's items + member/tag data + callbacks into the menu; the old
inline menu markup/styles (TASK-1671) move into the component. All
actions canEdit-gated. Drill-down clicks stopPropagation (the Svelte 5
same-click detach fix). Move scope = status, Assign = members only.
* fix(board): chunk bulk lane actions at the 1000-id server cap per Codex review (round 1)
The bulk endpoint rejects >1000 ids; the old per-item archive loop had
no ceiling, so a >1000-item filtered lane would fail every action with
"too many items". runBulk now chunks ids at 1000 (one bulk call per
chunk — a few SSE events instead of thousands) and aggregates the
updated/failed counts across chunks.
* fix(board): finalize partial bulk results when a chunk throws per Codex review (round 2)
A thrown chunk returned early, skipping deltaSync and the count toast —
leaving the UI stale after earlier chunks had already mutated items
server-side, behind a generic error. Move the try/catch inside the loop
(break on throw), then always deltaSync (when anything succeeded) and
toast the partial result: 'Verb N, M failed' on partial success, the
server error message only when nothing succeeded.
* fix(board): gate bulk lane actions on owner/editor role per Codex review (round 3)
canEditThisCollection is grant-aware (true for collection edit grants),
but the bulk endpoint requires workspace owner/editor (TASK-1668), so a
grant-only editor saw the lane bulk actions and got a 403 per click.
Gate the bulk callbacks (archive-all/move/tag/untag/set-priority/assign,
in both Board and List) on a new canBulkEdit = owner|editor role. The
single `+` create stays grant-aware (canEditThisCollection). LaneActions-
Menu's action callbacks are now optional and each entry hides when its
callback is absent, so a grant-editor sees only "Add item here".
* fix(board): drive lane-action visibility off callbacks, not canEdit per Codex review (round 4)
The kebab wrapper was still inside {#if canEdit} (= canEditThisCollection),
so an owner/editor without a collection edit grant (canBulkEdit true,
canEdit false) couldn't open the menu at all. Show the `+` when
onCreateInColumn is wired and the kebab when any menu action is wired
(hasMenuActions) — each callback already encodes its own permission.
Same fix for ListView's archive-group button (gate on onArchiveGroup
alone, not canEdit && onArchiveGroup).
* fix(board): lane-aware kebab visibility per Codex review (round 5)
The kebab showed whenever any bulk callback existed, but the bulk menu
entries only render for a non-empty lane — so a role-only bulk editor
(no onCreateInColumn) saw a ⋯ that opened an empty panel on empty
schema lanes. Show the kebab only when onCreateInColumn is wired OR the
lane has items AND a bulk action is wired.
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