Skip to content

feat: Board Pane Reorder UI#329

Merged
sahil-noon merged 4 commits into
mainfrom
260708-rmiq-board-pane-reorder
Jul 8, 2026
Merged

feat: Board Pane Reorder UI#329
sahil-noon merged 4 commits into
mainfrom
260708-rmiq-board-pane-reorder

Conversation

@sahil-noon

Copy link
Copy Markdown
Collaborator

Meta

Change ID Type Confidence Plan Review
rmiq feat 4.9/5.0 15/15 tasks, 21/21 acceptance ✓ ✓ 3 cycles
Impact +/− Net
raw +2083 / −56 +2027
true +1681 / −51 +1630
└ impl +806 / −51 +755
└ tests +875 / −0 +875

excludes fab/, docs/ · generated by fab-kit v2.13.5

Pipeline: intake ✓ → apply ✓ → review ✓ → hydrate ✓ → ship → review-pr

Summary

Panes on /board/$name rendered in server orderKey order with no UI to change it, even though the backend reorder endpoint, sorted fetch, and SSE broadcast were already shipped and unused. This wires that dark plumbing to the desktop board row: drag-and-drop via the pane header plus Cmd+K Move Focused Pane Left/Right.

Changes

  • Drag-and-drop reorder in the desktop board row — header-only drag handle, custom application/x-board-pane-reorder MIME, derive-over-store optimistic preview (ref-based, no watcher effect, no drag-end snap-back), single reorderPin POST per drop with before/after neighbor windowIds, cross-server board support.
  • Command palette: Move Focused Pane Left / Right — boundary-gated (hidden at edges, no wraparound), optimistic focus-follow via setFocusedIndex.
  • Tests — unit tests for neighbor computation and palette boundary gating; Playwright e2e for drag reorder and palette move with companion .spec.md.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR wires the already-shipped board reorder backend surface into the /board/:name desktop UI, adding drag-and-drop pane reordering and command-palette parity, plus backend support for cross-server neighbour resolution and comprehensive test coverage.

Changes:

  • Add desktop board pane drag-and-drop reorder (header-only drag source, pane-root drop target) with optimistic preview and rollback/cancel handling.
  • Add command palette actions to move the focused pane left/right with boundary gating and focus tracking by server:windowId key.
  • Add backend cross-server neighbour lookup for reorder and add unit/e2e test coverage across frontend and backend.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
fab/changes/260708-rmiq-board-pane-reorder/plan.md Implementation plan and acceptance criteria for board pane reorder UI.
fab/changes/260708-rmiq-board-pane-reorder/intake.md Intake document describing scope and approach.
fab/changes/260708-rmiq-board-pane-reorder/.status.yaml Change status and metrics for the fab pipeline.
fab/changes/260708-rmiq-board-pane-reorder/.history.jsonl Fab pipeline stage history for the change.
docs/memory/run-kit/ui-patterns.md Documented the board pane reorder UI pattern and palette parity.
docs/memory/run-kit/index.md Updated memory index entry description to include board pane reorder pattern.
docs/memory/run-kit/architecture.md Documented backend cross-server neighbour resolution behavior.
app/frontend/tests/e2e/board-reorder.spec.ts Playwright e2e validating reorder endpoint + GET ordering + SSE broadcast.
app/frontend/tests/e2e/board-reorder.spec.md Companion documentation describing what the e2e spec proves and why.
app/frontend/src/lib/board-reorder.ts Pure helper functions for neighbour computation and focus reconciliation gates.
app/frontend/src/lib/board-reorder.test.ts Vitest coverage for neighbour computation, focus-by-key reconcile, focus gating.
app/frontend/src/hooks/use-pin-actions.ts Changed reorder action to rethrow after toast so callers can rollback optimistic UI.
app/frontend/src/hooks/use-board-pane-reorder.ts New DnD hook for pane reorder with optimistic override + cancel/reject rollback.
app/frontend/src/hooks/use-board-pane-reorder.test.ts Vitest coverage for DnD MIME guard, preview splice, cancel/reject rollback, reconcile.
app/frontend/src/components/board/board-pane.tsx Wired drag/drop props into pane root/header and added drag-source dimming.
app/frontend/src/components/board/board-page.tsx Integrated hook, added palette actions, and implemented focus-by-key tracking + gate.
app/frontend/src/components/board/board-header.tsx Made header draggable (optional) and protected unpin button interactions.
app/backend/api/router.go Updated reorder neighbour lookup to aggregate across servers + added warning on skip.
app/backend/api/router_test.go Added tests for cross-server neighbour resolution and negative/boundary cases.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +216 to +229
const authoritative = entries.map((en) => paneKey(en.server, en.windowId));
const fromIdx = authoritative.indexOf(dragKey);
const landedIdx = override.indexOf(dragKey);
if (fromIdx === -1 || landedIdx === -1) return;
// Derive neighbours directly from the final override order (correct by
// construction — the override already reflects the drop position).
const before = landedIdx > 0 ? override[landedIdx - 1] : null;
const after = landedIdx < override.length - 1 ? override[landedIdx + 1] : null;
// No-op guard: if the pane did not actually move (neighbours unchanged
// from its authoritative slot), skip the POST.
const authBefore = fromIdx > 0 ? authoritative[fromIdx - 1] : null;
const authAfter =
fromIdx < authoritative.length - 1 ? authoritative[fromIdx + 1] : null;
if (before === authBefore && after === authAfter) return; // no-op: no POST

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — onDrop now reconciles the raw override against the current authoritative keys (drop absent keys, append newly-present entries — the same reconciliation the render does) before deriving before/after, so a mid-drag pin/unpin or remote reorder can no longer POST a stale neighbour windowId. Added a regression test covering a neighbour removed mid-drag. (ee53be8)

Comment on lines +1 to +17
/**
* Pure order-computation helpers for board pane reorder — shared by the
* drag-and-drop hook (`use-board-pane-reorder.ts`) and the command-palette
* Move Focused Pane Left/Right actions (`board-page.tsx`).
*
* The board reorder endpoint (`POST /api/boards/{name}/reorder`) uses fractional
* indexing: it mints an orderKey strictly between the moved pane's new
* `before` and `after` neighbours (`ComputeOrderKey` in
* `app/backend/internal/tmux/board.go`). So a move is expressed as ONE POST
* carrying the new neighbours' windowIds — `before` is the windowId that ends
* up immediately BEFORE the moved pane (smaller orderKey), `after` is the one
* immediately AFTER (larger orderKey); each is `null` at the respective edge.
*
* Extracting the neighbour arithmetic here (mirroring `palette-move.ts`) keeps
* it unit-testable without mounting the board, and lets DnD and the palette
* share one implementation.
*/

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — updated the module docstring to state the DnD hook deliberately does NOT import this module (it derives neighbours inline from its final optimistic override), matching computeMoveNeighbors' existing note. (ee53be8)

Comment on lines +9 to +13
// These helpers back both the board pane drag-and-drop hook
// (use-board-pane-reorder) and the command-palette Move Focused Pane
// Left/Right actions. Covering the insert-before neighbour arithmetic +
// boundary gating here proves the reorder POST's before/after are correct
// without mounting the board (mirrors palette-move.test.ts).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — corrected the test-file comment to say these helpers back only the command-palette Move Left/Right actions, noting the DnD hook derives neighbours inline and does not import this module. (ee53be8)

@sahil-noon sahil-noon marked this pull request as ready for review July 8, 2026 08:20
sahil87 added 4 commits July 8, 2026 13:53
Wires the already-shipped backend reorder endpoint (orderKey sort,
reorderPin, board-changed SSE) to the desktop board row: header-only
drag handle with a derive-over-store optimistic preview, and
Cmd+K Move Focused Pane Left/Right with boundary gating and focus-follow.
@sahil87 sahil87 force-pushed the 260708-rmiq-board-pane-reorder branch from 4fa4969 to 77a50de Compare July 8, 2026 08:58
@sahil-noon sahil-noon merged commit 6085b3b into main Jul 8, 2026
5 checks passed
@sahil-noon sahil-noon deleted the 260708-rmiq-board-pane-reorder branch July 8, 2026 09:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants