B3 — Planner board: multi-select stack-drag #411

Closed
opened 2026-04-27 00:02:16 +00:00 by claude-desktop · 0 comments
Collaborator

As an operator,
I want to select multiple cards and drag them as one stack onto an agent column,
so that triaging a batch of freshly-broken-down stories is one gesture instead of N.

Today drag is single-card only. The dominant operator chore is moving 3-10 newly-created stories from Triage onto agent columns one at a time. Linear and Shortcut both ship multi-select stack-drag; this story brings the same pattern.

Acceptance criteria

Selection

  • cmd-click (or ctrl-click on Linux) toggles a card's selected state.
  • shift-click extends a contiguous range from the most recent anchor within the same column; cross-column shift-click resets the anchor instead.
  • Selected cards render with a 2 px border-accent ring and a checkmark in the top-right corner.
  • Selection is column-scoped — selecting a card in dev clears any selection in other columns. (Cross-column selection has no useful drag target.)
  • Selection state lives in component state, not URL — selections do not survive reload (matches Linear/Shortcut).

Drag

  • Drag of any selected card drags all selected cards.
  • Cursor shows a stacked-cards visual (3 layered card outlines with offset) and a count badge (3) in the top-right corner of the stack.
  • Drag of a non-selected card while selection exists drags only the dragged card (does not implicitly select).

Drop + assignment fan-out

  • Drop on an agent column fires assignCard for each selected card via Promise.allSettled.
  • Per-card optimistic update (each card moves immediately).
  • Per-card rollback on error (only the failed card returns to source); show a toast Failed to assign N of M cards: <reason> aggregating failures.
  • Drop on Unassigned (B1) fan-outs unassign calls with the same semantics; if any selected card has a running task, the confirm dialog enumerates them: Cancel 3 running tasks and unassign?.

Keyboard

  • Esc clears selection.
  • cmd-a (or ctrl-a) selects all visible cards in the focused column. Suppressed if the focus is in a text input / textarea.

Tests

  • board.test.tsx: cmd-click toggles selection.
  • board.test.tsx: shift-click extends range within column, resets across columns.
  • board.test.tsx: drag of selected card → 3 assignCard calls + 3 cache patches.
  • board.test.tsx: one assign call rejects → only the failed card rolls back, toast shows Failed to assign 1 of 3 cards.
  • board.test.tsx: Esc clears, cmd-a selects all in focused column.

Out of scope

  • Bulk-edit side panel (Shortcut-style right-rail). Drag is the only bulk op for now.
  • Cross-column multi-select.
  • Multi-select restage (B2's drag becomes multi-aware automatically once this lands; no extra acceptance criteria here).

References

  • Spec: docs/specs/board-rework.md §5 B3.
  • B1 (drop-to-unassign) — confirm-dialog copy adapts to N cards once both ship.
  • B4 (keyboard nav) — Esc and cmd-a overlap with B4's keymap; coordinate so they share the same key handler.
  • Linear's stack-drag visual is the reference (count badge in top-right of stacked cards).

Dependencies

  • Land after B4 — both stories touch board-level keyboard handlers; B4 establishes the focus/keymap infra and B3 reuses it.

Suggested first commit

feat(board): multi-select cards with cmd/shift-click + stack-drag

**As an** operator, **I want** to select multiple cards and drag them as one stack onto an agent column, **so that** triaging a batch of freshly-broken-down stories is one gesture instead of N. Today drag is single-card only. The dominant operator chore is moving 3-10 newly-created stories from Triage onto agent columns one at a time. Linear and Shortcut both ship multi-select stack-drag; this story brings the same pattern. ## Acceptance criteria ### Selection - [ ] `cmd-click` (or `ctrl-click` on Linux) toggles a card's selected state. - [ ] `shift-click` extends a contiguous range from the most recent anchor within the same column; cross-column shift-click resets the anchor instead. - [ ] Selected cards render with a 2 px `border-accent` ring and a checkmark in the top-right corner. - [ ] Selection is column-scoped — selecting a card in `dev` clears any selection in other columns. (Cross-column selection has no useful drag target.) - [ ] Selection state lives in component state, not URL — selections do not survive reload (matches Linear/Shortcut). ### Drag - [ ] Drag of any selected card drags **all** selected cards. - [ ] Cursor shows a stacked-cards visual (3 layered card outlines with offset) and a count badge (`3`) in the top-right corner of the stack. - [ ] Drag of a non-selected card while selection exists drags only the dragged card (does not implicitly select). ### Drop + assignment fan-out - [ ] Drop on an agent column fires `assignCard` for each selected card via `Promise.allSettled`. - [ ] Per-card optimistic update (each card moves immediately). - [ ] Per-card rollback on error (only the failed card returns to source); show a toast `Failed to assign N of M cards: <reason>` aggregating failures. - [ ] Drop on Unassigned (B1) fan-outs unassign calls with the same semantics; if any selected card has a running task, the confirm dialog enumerates them: `Cancel 3 running tasks and unassign?`. ### Keyboard - [ ] `Esc` clears selection. - [ ] `cmd-a` (or `ctrl-a`) selects all visible cards in the focused column. Suppressed if the focus is in a text input / textarea. ### Tests - [ ] `board.test.tsx`: cmd-click toggles selection. - [ ] `board.test.tsx`: shift-click extends range within column, resets across columns. - [ ] `board.test.tsx`: drag of selected card → 3 `assignCard` calls + 3 cache patches. - [ ] `board.test.tsx`: one assign call rejects → only the failed card rolls back, toast shows `Failed to assign 1 of 3 cards`. - [ ] `board.test.tsx`: `Esc` clears, `cmd-a` selects all in focused column. ## Out of scope - Bulk-edit side panel (Shortcut-style right-rail). Drag is the only bulk op for now. - Cross-column multi-select. - Multi-select restage (B2's drag becomes multi-aware automatically once this lands; no extra acceptance criteria here). ## References - Spec: `docs/specs/board-rework.md` §5 B3. - B1 (drop-to-unassign) — confirm-dialog copy adapts to N cards once both ship. - B4 (keyboard nav) — `Esc` and `cmd-a` overlap with B4's keymap; coordinate so they share the same key handler. - Linear's stack-drag visual is the reference (count badge in top-right of stacked cards). ## Dependencies - Land **after B4** — both stories touch board-level keyboard handlers; B4 establishes the focus/keymap infra and B3 reuses it. ## Suggested first commit `feat(board): multi-select cards with cmd/shift-click + stack-drag`
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
charles/claude-hooks#411
No description provided.