From 2d09a783d5bb50e77cd8ce119365f39b2ac33a1a Mon Sep 17 00:00:00 2001 From: Daniel Kutyla Date: Thu, 19 Mar 2026 12:11:24 +0100 Subject: [PATCH] fix(shell): persist drag-and-drop pane reorder across navigation The frontend emitted state:reorder-panes on drop but the server had no handler, so dashboardState.panes was never reordered. When the user navigated away and returned, the snapshot restored the original order. Add a server-side socket handler that validates the incoming order array, reorders dashboardState.panes to match, and broadcasts the new order to other connected clients. --- src/routers/shell/shell-socket.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/routers/shell/shell-socket.ts b/src/routers/shell/shell-socket.ts index b086bde..6973c8d 100644 --- a/src/routers/shell/shell-socket.ts +++ b/src/routers/shell/shell-socket.ts @@ -307,6 +307,30 @@ export function initShell(nsp: Namespace): void { socket.broadcast.emit('state:active-pane', { paneId }); }); + // ── Drag-to-reorder persistence ────────────────────────────────────────── + socket.on('state:reorder-panes', ({ order }: { order: string[] }) => { + if (!Array.isArray(order) || order.length === 0) return; + // Validate every ID exists in dashboardState + const paneIds = new Set(dashboardState.panes.map((p: PaneInfo) => p.id)); + if (!order.every(id => typeof id === 'string' && paneIds.has(id))) return; + + // Rebuild panes array in the requested order, appending any panes not in the order list + const byId = new Map(dashboardState.panes.map((p: PaneInfo) => [p.id, p])); + const reordered: PaneInfo[] = []; + for (const id of order) { + const pane = byId.get(id); + if (pane) reordered.push(pane); + } + // Append any panes that weren't in the order array (e.g. from another project) + for (const p of dashboardState.panes) { + if (!order.includes(p.id)) reordered.push(p); + } + dashboardState.panes = reordered; + + // Broadcast to other clients so they sync + socket.broadcast.emit('state:panes-reordered', { order: reordered.map((p: PaneInfo) => p.id) }); + }); + // ── Disconnect ──────────────────────────────────────────────────────────── socket.on('disconnect', () => { console.log(`[shell:disconnect] ${socket.id}`);