From d5dfa4da3018cee81c18d17693d1b702cbfa98f1 Mon Sep 17 00:00:00 2001 From: abccodes Date: Tue, 23 Jun 2026 01:22:36 -0700 Subject: [PATCH] [aidan] chore/dashboard: drop dead configure/missed-run cards, refetch on reconnect --- backend/apps/dashboards/models.py | 1 - .../Dashboard/canvas/DashboardCanvas.tsx | 4 - .../Dashboard/canvas/DashboardCardLayer.tsx | 6 - .../Dashboard/canvas/DashboardOverlays.tsx | 3 - .../app/pages/Dashboard/controls/Minimap.tsx | 19 +-- .../Dashboard/geometry/dashboardTethers.ts | 56 +------ .../hooks/lifecycle/useDashboardLifecycle.ts | 28 +--- .../hooks/state/useDashboardController.ts | 6 +- .../hooks/state/useDashboardSelectors.ts | 2 - .../Dashboard/hooks/state/useLayoutSave.ts | 7 +- .../src/shared/state/dashboardLayoutSlice.ts | 151 +----------------- 11 files changed, 24 insertions(+), 259 deletions(-) diff --git a/backend/apps/dashboards/models.py b/backend/apps/dashboards/models.py index bf307bd2..2b203965 100644 --- a/backend/apps/dashboards/models.py +++ b/backend/apps/dashboards/models.py @@ -58,7 +58,6 @@ class DashboardLayout(BaseModel): view_cards: dict[str, ViewCardPosition] = Field(default_factory=dict) browser_cards: dict[str, BrowserCardPosition] = Field(default_factory=dict) workflow_cards: dict = Field(default_factory=dict) - configure_panels: dict = Field(default_factory=dict) workflows_hub: Optional[dict] = None notes: dict[str, NotePosition] = Field(default_factory=dict) expanded_session_ids: list[str] = Field(default_factory=list) diff --git a/frontend/src/app/pages/Dashboard/canvas/DashboardCanvas.tsx b/frontend/src/app/pages/Dashboard/canvas/DashboardCanvas.tsx index 06821c35..9f80028f 100644 --- a/frontend/src/app/pages/Dashboard/canvas/DashboardCanvas.tsx +++ b/frontend/src/app/pages/Dashboard/canvas/DashboardCanvas.tsx @@ -14,7 +14,6 @@ import type { NotePosition, WorkflowCardPosition, WorkflowsHubPosition, - ConfigurePanelPosition, } from '@/shared/state/dashboardLayoutSlice'; import type { Output } from '@/shared/state/outputsSlice'; import type { CardType, useDashboardSelection } from '../hooks/state/useDashboardSelection'; @@ -43,7 +42,6 @@ interface DashboardCanvasProps { notes: Record; workflowCards: Record; workflowsHub: WorkflowsHubPosition | null; - configurePanels: Record; outputs: Record; glowingAgentCards: Record; expandedSessionIds: string[]; @@ -106,7 +104,6 @@ const DashboardCanvas: React.FC = ({ notes, workflowCards, workflowsHub, - configurePanels, outputs, glowingAgentCards, expandedSessionIds, @@ -251,7 +248,6 @@ const DashboardCanvas: React.FC = ({ notes={notes} workflowCards={workflowCards} workflowsHub={workflowsHub} - configurePanels={configurePanels} outputs={outputs} glowingAgentCards={glowingAgentCards} expandedSessionIds={expandedSessionIds} diff --git a/frontend/src/app/pages/Dashboard/canvas/DashboardCardLayer.tsx b/frontend/src/app/pages/Dashboard/canvas/DashboardCardLayer.tsx index 9fca7751..5b019efd 100644 --- a/frontend/src/app/pages/Dashboard/canvas/DashboardCardLayer.tsx +++ b/frontend/src/app/pages/Dashboard/canvas/DashboardCardLayer.tsx @@ -16,7 +16,6 @@ import { type NotePosition, type WorkflowCardPosition, type WorkflowsHubPosition, - type ConfigurePanelPosition, } from '@/shared/state/dashboardLayoutSlice'; import { useAppSelector, useAppDispatch } from '@/shared/hooks'; import { closeWorkflowMonitor } from '@/shared/state/dashboardLayoutSlice'; @@ -36,7 +35,6 @@ interface DashboardCardLayerProps { notes: Record; workflowCards: Record; workflowsHub: WorkflowsHubPosition | null; - configurePanels: Record; outputs: Record; glowingAgentCards: Record; expandedSessionIds: string[]; @@ -73,7 +71,6 @@ const DashboardCardLayer: React.FC = ({ notes, workflowCards, workflowsHub, - configurePanels, outputs, glowingAgentCards, expandedSessionIds, @@ -101,9 +98,6 @@ const DashboardCardLayer: React.FC = ({ onBranch, onMeasuredHeight, }) => { - // Ephemeral singleton, not part of the saved layout, so read it straight - // from the store rather than threading it through the selector chain. - const missedRunsCard = useAppSelector((s) => s.dashboardLayout.missedRunsCard); const dispatch = useAppDispatch(); const monitorCard = useAppSelector((s) => s.dashboardLayout.workflowsMonitorCard); const monitorWorkflowId = useAppSelector((s) => s.dashboardLayout.workflowsMonitorId); diff --git a/frontend/src/app/pages/Dashboard/canvas/DashboardOverlays.tsx b/frontend/src/app/pages/Dashboard/canvas/DashboardOverlays.tsx index 88585d29..963d23a7 100644 --- a/frontend/src/app/pages/Dashboard/canvas/DashboardOverlays.tsx +++ b/frontend/src/app/pages/Dashboard/canvas/DashboardOverlays.tsx @@ -1,6 +1,5 @@ import React, { type RefObject } from 'react'; import Box from '@mui/material/Box'; -import { useAppSelector } from '@/shared/hooks'; import DashboardToolbar from '../DashboardToolbar'; import CanvasControls from '../controls/CanvasControls'; import CardSearchPalette from '../controls/CardSearchPalette'; @@ -82,7 +81,6 @@ const DashboardOverlays: React.FC = ({ toolbarPrefill, toolbarPrefillMode, }) => { - const missedRunsCard = useAppSelector((s) => s.dashboardLayout.missedRunsCard); return ( <> {/* Floating bottom toolbar */} @@ -133,7 +131,6 @@ const DashboardOverlays: React.FC = ({ browserCards, workflowCards, workflowsHub, - missedRunsCard, }} onMinimapPan={(px, py) => canvas.actions.setState({ panX: px, panY: py, zoom: canvas.zoom })} /> diff --git a/frontend/src/app/pages/Dashboard/controls/Minimap.tsx b/frontend/src/app/pages/Dashboard/controls/Minimap.tsx index 3b2b64eb..4bdee3b1 100644 --- a/frontend/src/app/pages/Dashboard/controls/Minimap.tsx +++ b/frontend/src/app/pages/Dashboard/controls/Minimap.tsx @@ -1,6 +1,6 @@ import React, { useRef, useCallback, useMemo } from 'react'; import { useClaudeTokens } from '@/shared/styles/ThemeContext'; -import type { CardPosition, ViewCardPosition, BrowserCardPosition, WorkflowCardPosition, WorkflowsHubPosition, MissedRunsCardPosition } from '@/shared/state/dashboardLayoutSlice'; +import type { CardPosition, ViewCardPosition, BrowserCardPosition, WorkflowCardPosition, WorkflowsHubPosition } from '@/shared/state/dashboardLayoutSlice'; const MINIMAP_W = 200; const MINIMAP_H = 140; @@ -16,7 +16,6 @@ export interface MinimapProps { browserCards: Record; workflowCards: Record; workflowsHub: WorkflowsHubPosition | null; - missedRunsCard: MissedRunsCardPosition | null; onPan: (panX: number, panY: number) => void; } @@ -25,12 +24,12 @@ interface CardRect { y: number; width: number; height: number; - type: 'agent' | 'view' | 'browser' | 'workflow' | 'workflows-hub' | 'missed-runs'; + type: 'agent' | 'view' | 'browser' | 'workflow' | 'workflows-hub'; } const Minimap: React.FC = ({ panX, panY, zoom, viewportRef, - cards, viewCards, browserCards, workflowCards, workflowsHub, missedRunsCard, + cards, viewCards, browserCards, workflowCards, workflowsHub, onPan, }) => { const c = useClaudeTokens(); @@ -60,17 +59,8 @@ const Minimap: React.FC = ({ type: 'workflows-hub', }); } - if (missedRunsCard) { - result.push({ - x: missedRunsCard.x, - y: missedRunsCard.y, - width: missedRunsCard.width, - height: missedRunsCard.height, - type: 'missed-runs', - }); - } return result; - }, [cards, viewCards, browserCards, workflowCards, workflowsHub, missedRunsCard]); + }, [cards, viewCards, browserCards, workflowCards, workflowsHub]); const layout = useMemo(() => { const vp = viewportRef.current; @@ -159,7 +149,6 @@ const Minimap: React.FC = ({ case 'browser': return c.status.success; case 'workflow': return c.status.warning; case 'workflows-hub': return c.status.warning; - case 'missed-runs': return c.status.warning; } }; diff --git a/frontend/src/app/pages/Dashboard/geometry/dashboardTethers.ts b/frontend/src/app/pages/Dashboard/geometry/dashboardTethers.ts index a0e00f25..42d3aa40 100644 --- a/frontend/src/app/pages/Dashboard/geometry/dashboardTethers.ts +++ b/frontend/src/app/pages/Dashboard/geometry/dashboardTethers.ts @@ -1,5 +1,5 @@ import { useMemo, type RefObject } from 'react'; -import type { CardPosition, BrowserCardPosition, WorkflowCardPosition, ConfigurePanelPosition, WorkflowsHubPosition } from '@/shared/state/dashboardLayoutSlice'; +import type { CardPosition, BrowserCardPosition, WorkflowCardPosition, WorkflowsHubPosition } from '@/shared/state/dashboardLayoutSlice'; import type { Workflow, OpenCard } from '@/shared/state/workflowsSlice'; import { EXPANDED_CARD_MIN_H, GRID_GAP } from '@/shared/state/dashboardLayoutSlice'; import type { AgentSession } from '@/shared/state/agentsSlice'; @@ -82,7 +82,6 @@ interface UseTethersArgs { workflowCards: Record; workflowItems: Record; workflowOpenCards: Record; - configurePanels: Record; expandedSessionIds: string[]; liveDragInfo: LiveDragInfo | null; measuredHeightsRef: RefObject>; @@ -101,7 +100,6 @@ export function useTethers({ workflowCards, workflowItems, workflowOpenCards, - configurePanels, expandedSessionIds, liveDragInfo, measuredHeightsRef, @@ -390,54 +388,6 @@ export function useTethers({ }); } - // Configure-panel tethers: anchor each open configure panel to its workflow card. - const configureTethers: Tether[] = []; - for (const p of Object.values(configurePanels)) { - const wc = workflowCards[p.workflow_id]; - if (!wc) continue; - let srcX = wc.x, srcY = wc.y; - let dstX = p.x, dstY = p.y; - if (liveDragInfo) { - if (liveDragInfo.cardId === p.workflow_id) { srcX += liveDragInfo.dx; srcY += liveDragInfo.dy; } - } - const wcH = wfHeight(wc); - const srcCx = srcX + wc.width / 2; - const dstCx = dstX + p.width / 2; - const srcAnchors: Anchor[] = [ - { x: srcX + wc.width, y: srcY + wcH * 0.5, side: 'right' }, - { x: srcX, y: srcY + wcH * 0.5, side: 'left' }, - { x: srcCx, y: srcY, side: 'top' }, - { x: srcCx, y: srcY + wcH, side: 'bottom' }, - ]; - const dstAnchors: Anchor[] = [ - { x: dstX, y: dstY + p.height * 0.5, side: 'left' }, - { x: dstX + p.width, y: dstY + p.height * 0.5, side: 'right' }, - { x: dstCx, y: dstY, side: 'top' }, - { x: dstCx, y: dstY + p.height, side: 'bottom' }, - ]; - let bestSrc = srcAnchors[0], bestDst = dstAnchors[0]; - let bestDist = Infinity; - for (const sa of srcAnchors) { - for (const da of dstAnchors) { - const d = Math.hypot(sa.x - da.x, sa.y - da.y); - if (d < bestDist) { bestDist = d; bestSrc = sa; bestDst = da; } - } - } - const x1 = bestSrc.x, y1 = bestSrc.y; - const x2 = bestDst.x, y2 = bestDst.y; - const pathD = elbowPath(x1, y1, x2, y2); - const midX = x1 + (x2 - x1) / 2; - const midY = y1 + (y2 - y1) / 2; - configureTethers.push({ - key: `configure-${p.workflow_id}`, - path: pathD, - labelX: midX, - labelY: midY, - label: 'Configure', - fading: false, - }); - } - // Run Monitor tether: the Workflows window to its spawned live-run card. // Same border-anchor + elbow math as the sidecar "Watching" arrow. const monitorTethers: Tether[] = []; @@ -470,9 +420,9 @@ export function useTethers({ }); } - return [...agentTethers, ...browserTethers, ...workflowTethers, ...configureTethers, ...monitorTethers]; + return [...agentTethers, ...browserTethers, ...workflowTethers, ...monitorTethers]; // measuredHeightsTick re-runs the memo once ResizeObserver reports a new // height after a collapse (the ref read is invisible to the dep checker). // eslint-disable-next-line react-hooks/exhaustive-deps - }, [glowingAgentCards, glowingBrowserCards, cards, browserCards, workflowCards, workflowItems, workflowOpenCards, configurePanels, expandedSessionIds, liveDragInfo, measuredHeightsTick, sessionList, workflowsHub, workflowsMonitorCard, workflowsMonitorLabel]); + }, [glowingAgentCards, glowingBrowserCards, cards, browserCards, workflowCards, workflowItems, workflowOpenCards, expandedSessionIds, liveDragInfo, measuredHeightsTick, sessionList, workflowsHub, workflowsMonitorCard, workflowsMonitorLabel]); } diff --git a/frontend/src/app/pages/Dashboard/hooks/lifecycle/useDashboardLifecycle.ts b/frontend/src/app/pages/Dashboard/hooks/lifecycle/useDashboardLifecycle.ts index 68af7c92..cde74658 100644 --- a/frontend/src/app/pages/Dashboard/hooks/lifecycle/useDashboardLifecycle.ts +++ b/frontend/src/app/pages/Dashboard/hooks/lifecycle/useDashboardLifecycle.ts @@ -17,12 +17,11 @@ import { clearPendingFocusBrowserId, clearPendingFocusWorkflowId, clearPendingFocusWorkflowsHub, - clearPendingFocusMissedRuns, type ViewCardPosition, } from '@/shared/state/dashboardLayoutSlice'; import { fetchOutputs, type Output } from '@/shared/state/outputsSlice'; import { generateDashboardName } from '@/shared/state/dashboardsSlice'; -import { fetchWorkflows } from '@/shared/state/workflowsSlice'; +import { fetchWorkflows, fetchAllRuns, fetchActiveRuns } from '@/shared/state/workflowsSlice'; import { fetchMissedRuns } from '@/shared/state/missedRunsSlice'; import { dashboardWs } from '@/shared/ws/WebSocketManager'; import { initBrowserCommandHandler } from '@/shared/browserCommandHandler'; @@ -70,7 +69,6 @@ export function useDashboardLifecycle({ const pendingFocusAgentId = useAppSelector((state) => state.tempState.pendingFocusAgentId); const pendingFocusBrowserId = useAppSelector((state) => state.dashboardLayout.pendingFocusBrowserId); const pendingFocusWorkflowId = useAppSelector((state) => state.dashboardLayout.pendingFocusWorkflowId); - const pendingFocusMissedRuns = useAppSelector((state) => state.dashboardLayout.pendingFocusMissedRuns); const pendingFocusWorkflowsHub = useAppSelector((state) => state.dashboardLayout.pendingFocusWorkflowsHub); // Once per app launch: if scheduled fires elapsed while we were closed, fetch @@ -110,6 +108,12 @@ export function useDashboardLifecycle({ const unsubReconnect = dashboardWs.on('dashboard:reconnected', () => { dispatch(fetchSessions({ dashboardId })); dispatch(fetchLayout({ dashboardId, isReconnect: true })); + // workflow:run/updated/deleted are global broadcasts that skip the replay + // log, so a socket gap drops them: refetch to heal stale "running" cards, + // ghost workflows, and missed run history on reconnect. + dispatch(fetchWorkflows(dashboardId)); + dispatch(fetchAllRuns(200)); + dispatch(fetchActiveRuns()); }); // DEFERRABLE: history list (for the search palette) and outputs // (for the apps panel) aren't on the first-paint path. Same for the @@ -264,24 +268,6 @@ export function useDashboardLifecycle({ }, 200); }, [isActive, pendingFocusWorkflowId, layoutInitialized, dispatch, canvasActions, handleHighlightCard]); - // Same pan/highlight choreography when the missed-runs card opens from its toast. - useEffect(() => { - if (!isActive) return; - if (!pendingFocusMissedRuns || !layoutInitialized) return; - dispatch(clearPendingFocusMissedRuns()); - setTimeout(() => { - const card = store.getState().dashboardLayout.missedRunsCard; - if (card) { - canvasActions.fitToCards( - [{ x: card.x, y: card.y, width: card.width, height: card.height }], - 1.15, - true, - ); - handleHighlightCard('missed-runs'); - } - }, 200); - }, [isActive, pendingFocusMissedRuns, layoutInitialized, dispatch, canvasActions, handleHighlightCard]); - // Pan/zoom to Workflows Hub on Expand; chained rAFs ensure fit runs after the hub div lands at its new coords. useEffect(() => { if (!isActive) return; diff --git a/frontend/src/app/pages/Dashboard/hooks/state/useDashboardController.ts b/frontend/src/app/pages/Dashboard/hooks/state/useDashboardController.ts index 89f0757d..fc181d9f 100644 --- a/frontend/src/app/pages/Dashboard/hooks/state/useDashboardController.ts +++ b/frontend/src/app/pages/Dashboard/hooks/state/useDashboardController.ts @@ -32,7 +32,7 @@ export function useDashboardController(dashboardId: string, isActive: boolean) { const isElementSelectMode = elementSelectionCtx?.selectMode ?? false; const { dashboardName, sessions, expandedSessionIds, cards, viewCards, browserCards, - workflowCards, workflowItems, workflowOpenCards, configurePanels, workflowsHub, + workflowCards, workflowItems, workflowOpenCards, workflowsHub, pendingFocusWorkflowId, pendingFocusWorkflowsHub, notes, pendingFocusNoteId, layoutInitialized, persistedExpandedSessionIds, zoomSensitivity, newAgentShortcut, browserHomepage, expandNewChats, @@ -177,7 +177,6 @@ export function useDashboardController(dashboardId: string, isActive: boolean) { viewCards, browserCards, workflowCards, - configurePanels, workflowsHub, notes, expandedSessionIds, @@ -296,7 +295,6 @@ export function useDashboardController(dashboardId: string, isActive: boolean) { workflowCards, workflowItems, workflowOpenCards, - configurePanels, expandedSessionIds, liveDragInfo, measuredHeightsRef, @@ -310,7 +308,7 @@ export function useDashboardController(dashboardId: string, isActive: boolean) { return { c, dashboardId, dashboardName, canvas, selection, sessions, sessionList, cards, viewCards, browserCards, notes, outputs, glowingAgentCards, - workflowCards, workflowsHub, configurePanels, + workflowCards, workflowsHub, expandedSessionIds, tethers, highlightedCardId, autoFocusSessionId, focusedCardId, pendingFocusNoteId, multiDragDelta, shakeDirection, neighborDirections, toolbarOpen, searchPaletteOpen, newAgentBounce, diff --git a/frontend/src/app/pages/Dashboard/hooks/state/useDashboardSelectors.ts b/frontend/src/app/pages/Dashboard/hooks/state/useDashboardSelectors.ts index 94f6561a..7114eb7d 100644 --- a/frontend/src/app/pages/Dashboard/hooks/state/useDashboardSelectors.ts +++ b/frontend/src/app/pages/Dashboard/hooks/state/useDashboardSelectors.ts @@ -25,7 +25,6 @@ export function useDashboardSelectors(dashboardId: string) { return out; }, [allBrowserCards, dashboardId]); const workflowCards = useAppSelector((state) => state.dashboardLayout.workflowCards); - const configurePanels = useAppSelector((state) => state.dashboardLayout.configurePanels); const workflowsHub = useAppSelector((state) => state.dashboardLayout.workflowsHub); const pendingFocusWorkflowId = useAppSelector((state) => state.dashboardLayout.pendingFocusWorkflowId); const pendingFocusWorkflowsHub = useAppSelector((state) => state.dashboardLayout.pendingFocusWorkflowsHub); @@ -55,7 +54,6 @@ export function useDashboardSelectors(dashboardId: string) { workflowCards, workflowItems, workflowOpenCards, - configurePanels, workflowsHub, pendingFocusWorkflowId, pendingFocusWorkflowsHub, diff --git a/frontend/src/app/pages/Dashboard/hooks/state/useLayoutSave.ts b/frontend/src/app/pages/Dashboard/hooks/state/useLayoutSave.ts index f06c37d1..d3f1812c 100644 --- a/frontend/src/app/pages/Dashboard/hooks/state/useLayoutSave.ts +++ b/frontend/src/app/pages/Dashboard/hooks/state/useLayoutSave.ts @@ -7,7 +7,6 @@ import { type BrowserCardPosition, type NotePosition, type WorkflowCardPosition, - type ConfigurePanelPosition, type WorkflowsHubPosition, } from '@/shared/state/dashboardLayoutSlice'; @@ -19,7 +18,6 @@ interface UseLayoutSaveArgs { viewCards: Record; browserCards: Record; workflowCards: Record; - configurePanels: Record; workflowsHub: WorkflowsHubPosition | null; notes: Record; expandedSessionIds: string[]; @@ -38,7 +36,6 @@ export function useLayoutSave({ viewCards, browserCards, workflowCards, - configurePanels, workflowsHub, notes, expandedSessionIds, @@ -56,7 +53,7 @@ export function useLayoutSave({ skipInitialSave.current = false; return; } - const payload = { dashboardId, cards, viewCards, browserCards, workflowCards, configurePanels, workflowsHub, notes, expandedSessionIds }; + const payload = { dashboardId, cards, viewCards, browserCards, workflowCards, workflowsHub, notes, expandedSessionIds }; pendingSaveRef.current = payload; if (saveTimerRef.current) clearTimeout(saveTimerRef.current); saveTimerRef.current = setTimeout(() => { @@ -65,7 +62,7 @@ export function useLayoutSave({ saveTimerRef.current = null; captureNow(); }, 500); - }, [isActive, cards, viewCards, browserCards, workflowCards, configurePanels, workflowsHub, notes, expandedSessionIds, layoutInitialized, dashboardId, dispatch, captureNow]); + }, [isActive, cards, viewCards, browserCards, workflowCards, workflowsHub, notes, expandedSessionIds, layoutInitialized, dashboardId, dispatch, captureNow]); useEffect(() => { return () => { diff --git a/frontend/src/shared/state/dashboardLayoutSlice.ts b/frontend/src/shared/state/dashboardLayoutSlice.ts index f5817532..a86c2d9b 100644 --- a/frontend/src/shared/state/dashboardLayoutSlice.ts +++ b/frontend/src/shared/state/dashboardLayoutSlice.ts @@ -24,14 +24,12 @@ export const DEFAULT_WORKFLOW_CARD_H = 520; // comfortable size automatically. export const DEFAULT_WORKFLOWS_HUB_W = DEFAULT_BROWSER_CARD_W; export const DEFAULT_WORKFLOWS_HUB_H = DEFAULT_BROWSER_CARD_H; -export const DEFAULT_MISSED_RUNS_W = 460; -export const DEFAULT_MISSED_RUNS_H = 420; export const EXPANDED_CARD_MIN_H = 620; export const GRID_GAP = 24; const GRID_ORIGIN = { x: 40, y: 100 }; const GRID_COLS_FALLBACK = 4; -export type CardType = 'agent' | 'view' | 'browser' | 'note' | 'workflow' | 'workflows-hub' | 'workflows-monitor' | 'missed_runs'; +export type CardType = 'agent' | 'view' | 'browser' | 'note' | 'workflow' | 'workflows-hub' | 'workflows-monitor'; export interface CardPosition { session_id: string; @@ -93,16 +91,6 @@ export interface WorkflowsHubPosition { zOrder: number; } -// Ephemeral launch-time card listing scheduled fires missed while the app was -// closed. Singleton like workflowsHub, but deliberately NOT persisted to the -// saved layout: the launch hook decides each session whether to show it. -export interface MissedRunsCardPosition { - x: number; - y: number; - width: number; - height: number; - zOrder: number; -} export type NoteColor = 'yellow' | 'pink' | 'blue' | 'green' | 'purple' | 'gray'; @@ -120,22 +108,12 @@ export interface NotePosition { export const DEFAULT_NOTE_W = 240; export const DEFAULT_NOTE_H = 200; -export interface ConfigurePanelPosition { - workflow_id: string; - x: number; - y: number; - width: number; - height: number; -} - export interface DashboardLayoutState { cards: Record; viewCards: Record; browserCards: Record; workflowCards: Record; - configurePanels: Record; workflowsHub: WorkflowsHubPosition | null; - missedRunsCard: MissedRunsCardPosition | null; notes: Record; closedCardPositions: Record; glowingBrowserCards: Record; @@ -154,8 +132,6 @@ export interface DashboardLayoutState { /** Transient: id of the view card the user has clicked into; preload stops forwarding canvas gestures while set. */ activeViewCardId: string | null; pendingFocusWorkflowId: string | null; - /** Transient: signals Dashboard to pan/zoom to the missed-runs card on open. */ - pendingFocusMissedRuns: boolean; /** Transient: signals Dashboard to pan/zoom to the singleton Workflows Hub on open. */ pendingFocusWorkflowsHub: boolean; /** Transient deep-link target: the Workflows card jumps to this workflow's detail on open, then clears it. */ @@ -183,9 +159,7 @@ const initialState: DashboardLayoutState = { viewCards: {}, browserCards: {}, workflowCards: {}, - configurePanels: {}, workflowsHub: null, - missedRunsCard: null, notes: {}, closedCardPositions: {}, glowingBrowserCards: {}, @@ -200,7 +174,6 @@ const initialState: DashboardLayoutState = { endingBrowserCards: {}, activeViewCardId: null, pendingFocusWorkflowId: null, - pendingFocusMissedRuns: false, pendingFocusWorkflowsHub: false, workflowsAppTarget: null, workflowsMonitorId: null, @@ -214,7 +187,6 @@ interface LayoutPayload { viewCards: Record; browserCards: Record; workflowCards: Record; - configurePanels: Record; workflowsHub: WorkflowsHubPosition | null; notes: Record; expandedSessionIds: string[]; @@ -253,7 +225,6 @@ export const fetchLayout = createAsyncThunk( viewCards: (layout.view_cards ?? {}) as Record, browserCards: browserCards as Record, workflowCards: (layout.workflow_cards ?? {}) as Record, - configurePanels: (layout.configure_panels ?? {}) as Record, workflowsHub: (layout.workflows_hub ?? null) as WorkflowsHubPosition | null, notes: (layout.notes ?? {}) as Record, expandedSessionIds: (layout.expanded_session_ids ?? []) as string[], @@ -277,7 +248,6 @@ export const saveLayout = createAsyncThunk( view_cards: payload.viewCards, browser_cards: payload.browserCards, workflow_cards: payload.workflowCards, - configure_panels: payload.configurePanels, workflows_hub: payload.workflowsHub, notes: payload.notes, expanded_session_ids: payload.expandedSessionIds, @@ -321,9 +291,6 @@ function collectOccupiedRects( if (state.workflowsHub) { rects.push({ x: state.workflowsHub.x, y: state.workflowsHub.y, w: state.workflowsHub.width, h: state.workflowsHub.height }); } - if (state.missedRunsCard) { - rects.push({ x: state.missedRunsCard.x, y: state.missedRunsCard.y, w: state.missedRunsCard.width, h: state.missedRunsCard.height }); - } for (const n of Object.values(state.notes)) { rects.push({ x: n.x, y: n.y, w: n.width, h: n.height }); } @@ -520,14 +487,12 @@ const dashboardLayoutSlice = createSlice({ for (const n of Object.values(state.notes)) tally(n.zOrder); if (state.workflowsHub) tally(state.workflowsHub.zOrder); if (state.workflowsMonitorCard) tally(state.workflowsMonitorCard.zOrder); - if (state.missedRunsCard) tally(state.missedRunsCard.zOrder); if (type === 'agent') currentZ = state.cards[id]?.zOrder ?? 0; else if (type === 'view') currentZ = state.viewCards[id]?.zOrder ?? 0; else if (type === 'note') currentZ = state.notes[id]?.zOrder ?? 0; else if (type === 'workflow') currentZ = state.workflowCards[id]?.zOrder ?? 0; else if (type === 'workflows-hub') currentZ = state.workflowsHub?.zOrder ?? 0; else if (type === 'workflows-monitor') currentZ = state.workflowsMonitorCard?.zOrder ?? 0; - else if (type === 'missed_runs') currentZ = state.missedRunsCard?.zOrder ?? 0; else currentZ = state.browserCards[id]?.zOrder ?? 0; if (currentZ >= maxZ) return; // Already on top: no-op. @@ -548,8 +513,6 @@ const dashboardLayoutSlice = createSlice({ if (state.workflowsHub) state.workflowsHub.zOrder = z; } else if (type === 'workflows-monitor') { if (state.workflowsMonitorCard) state.workflowsMonitorCard.zOrder = z; - } else if (type === 'missed_runs') { - if (state.missedRunsCard) state.missedRunsCard.zOrder = z; } else { const card = state.browserCards[id]; if (card) card.zOrder = z; @@ -607,7 +570,8 @@ const dashboardLayoutSlice = createSlice({ const bCards = Object.values(state.browserCards); const wCards = Object.values(state.workflowCards); const hub = state.workflowsHub; - const total = agentCards.length + viewCards.length + bCards.length + wCards.length + (hub ? 1 : 0); + const mon = state.workflowsMonitorCard; + const total = agentCards.length + viewCards.length + bCards.length + wCards.length + (hub ? 1 : 0) + (mon ? 1 : 0); if (total === 0) return; const allItems = [ @@ -616,6 +580,7 @@ const dashboardLayoutSlice = createSlice({ ...bCards.map((c) => ({ kind: 'browser' as const, id: c.browser_id, x: c.x, y: c.y, storedW: c.width, storedH: c.height })), ...wCards.map((c) => ({ kind: 'workflow' as const, id: c.workflow_id, x: c.x, y: c.y, storedW: c.width, storedH: c.height })), ...(hub ? [{ kind: 'workflows-hub' as const, id: 'workflows-hub', x: hub.x, y: hub.y, storedW: hub.width, storedH: hub.height }] : []), + ...(mon ? [{ kind: 'workflows-monitor' as const, id: 'workflows-monitor', x: mon.x, y: mon.y, storedW: mon.width, storedH: mon.height }] : []), ]; allItems.sort((a, b) => a.y - b.y || a.x - b.x); @@ -645,6 +610,8 @@ const dashboardLayoutSlice = createSlice({ if (card) { card.x = pos.x; card.y = pos.y; } } else if (item.kind === 'workflows-hub') { if (state.workflowsHub) { state.workflowsHub.x = pos.x; state.workflowsHub.y = pos.y; } + } else if (item.kind === 'workflows-monitor') { + if (state.workflowsMonitorCard) { state.workflowsMonitorCard.x = pos.x; state.workflowsMonitorCard.y = pos.y; } } else { const card = state.browserCards[item.id]; if (card) { card.x = pos.x; card.y = pos.y; } @@ -888,65 +855,9 @@ const dashboardLayoutSlice = createSlice({ if (!card) return; delete state.workflowCards[oldId]; state.workflowCards[newId] = { ...card, workflow_id: newId }; - // Carry any open Action-Library panel along with the rekey so the - // popout doesn't disappear when a draft is saved. - const panel = state.configurePanels[oldId]; - if (panel) { - delete state.configurePanels[oldId]; - state.configurePanels[newId] = { ...panel, workflow_id: newId }; - } if (state.pendingFocusWorkflowId === oldId) state.pendingFocusWorkflowId = newId; }, - openConfigurePanel( - state, - action: PayloadAction<{ workflowId: string }>, - ) { - const { workflowId } = action.payload; - // Anchor the panel just to the right of the workflow card. - const wfCard = state.workflowCards[workflowId]; - const baseX = wfCard ? wfCard.x + wfCard.width + GRID_GAP * 6 : 600; - const baseY = wfCard ? wfCard.y : 200; - const existing = state.configurePanels[workflowId]; - if (existing) { - existing.x = baseX; - existing.y = baseY; - return; - } - state.configurePanels[workflowId] = { - workflow_id: workflowId, - x: baseX, - y: baseY, - width: 580, - height: 600, - }; - }, - - setConfigurePanelPosition( - state, - action: PayloadAction<{ workflowId: string; x: number; y: number }>, - ) { - const { workflowId, x, y } = action.payload; - const p = state.configurePanels[workflowId]; - if (p) { p.x = x; p.y = y; } - }, - - setConfigurePanelSize( - state, - action: PayloadAction<{ workflowId: string; width: number; height: number }>, - ) { - const { workflowId, width, height } = action.payload; - const p = state.configurePanels[workflowId]; - if (p) { - p.width = Math.max(360, width); - p.height = Math.max(280, height); - } - }, - - closeConfigurePanel(state, action: PayloadAction) { - delete state.configurePanels[action.payload]; - }, - clearPendingFocusWorkflowId(state) { state.pendingFocusWorkflowId = null; }, @@ -973,37 +884,6 @@ const dashboardLayoutSlice = createSlice({ state.pendingFocusWorkflowsHub = false; }, - openMissedRunsCard(state, action: PayloadAction<{ expandedSessionIds?: string[] } | undefined>) { - state.pendingFocusMissedRuns = true; - if (state.missedRunsCard) { - state.missedRunsCard.zOrder = state.nextZOrder++; - return; - } - const rects = collectOccupiedRects(state, action.payload?.expandedSessionIds); - const pos = findOpenGridCell(rects, DEFAULT_MISSED_RUNS_W, DEFAULT_MISSED_RUNS_H); - state.missedRunsCard = { - x: pos.x, - y: pos.y, - width: DEFAULT_MISSED_RUNS_W, - height: DEFAULT_MISSED_RUNS_H, - zOrder: state.nextZOrder++, - }; - }, - - clearPendingFocusMissedRuns(state) { - state.pendingFocusMissedRuns = false; - }, - - closeMissedRunsCard(state) { - state.missedRunsCard = null; - }, - - setMissedRunsCardPosition(state, action: PayloadAction<{ x: number; y: number }>) { - if (!state.missedRunsCard) return; - state.missedRunsCard.x = action.payload.x; - state.missedRunsCard.y = action.payload.y; - }, - closeWorkflowsHub(state) { state.workflowsHub = null; }, @@ -1281,11 +1161,6 @@ const dashboardLayoutSlice = createSlice({ state.workflowsHub.x += dx; state.workflowsHub.y += dy; } - } else if (item.type === 'missed_runs') { - if (state.missedRunsCard) { - state.missedRunsCard.x += dx; - state.missedRunsCard.y += dy; - } } else { const card = state.browserCards[item.id]; if (card) { @@ -1414,9 +1289,7 @@ const dashboardLayoutSlice = createSlice({ state.viewCards = {}; state.browserCards = {}; state.workflowCards = {}; - state.configurePanels = {}; state.workflowsHub = null; - state.missedRunsCard = null; state.notes = {}; state.closedCardPositions = {}; state.glowingBrowserCards = {}; @@ -1428,7 +1301,6 @@ const dashboardLayoutSlice = createSlice({ state.suspendedBrowserCards = {}; state.endingBrowserCards = {}; state.pendingFocusWorkflowId = null; - state.pendingFocusMissedRuns = false; }, }, @@ -1456,7 +1328,6 @@ const dashboardLayoutSlice = createSlice({ card.dashboard_id = ownerDashboardId; } state.workflowCards = action.payload.workflowCards || {}; - state.configurePanels = action.payload.configurePanels || {}; state.workflowsHub = action.payload.workflowsHub || null; state.notes = action.payload.notes || {}; // Cards boot parked (no guest process, title placeholder); the suspend @@ -1475,7 +1346,6 @@ const dashboardLayoutSlice = createSlice({ if (!card.dashboard_id) card.dashboard_id = ownerDashboardId; } addMissingCards(state.workflowCards, action.payload.workflowCards || {}, occupied); - addMissingCards(state.configurePanels, action.payload.configurePanels || {}, occupied); if (!state.workflowsHub && action.payload.workflowsHub) state.workflowsHub = action.payload.workflowsHub; addMissingCards(state.notes, action.payload.notes || {}, occupied); } @@ -1520,7 +1390,6 @@ const dashboardLayoutSlice = createSlice({ .addCase(deleteWorkflowFulfilledAction, (state, action) => { const id = action.payload; if (id && state.workflowCards[id]) delete state.workflowCards[id]; - if (id && state.configurePanels[id]) delete state.configurePanels[id]; }) .addCase(launchAndSendFirstMessage.fulfilled, (state, action) => { const { draftId, session } = action.payload; @@ -1579,10 +1448,6 @@ export const { setWorkflowCardSize, removeWorkflowCard, rekeyWorkflowCard, - openConfigurePanel, - closeConfigurePanel, - setConfigurePanelPosition, - setConfigurePanelSize, clearPendingFocusWorkflowId, openWorkflowsHub, closeWorkflowsHub, @@ -1597,10 +1462,6 @@ export const { setWorkflowsHubPosition, setWorkflowsHubSize, clearPendingFocusWorkflowsHub, - openMissedRunsCard, - clearPendingFocusMissedRuns, - closeMissedRunsCard, - setMissedRunsCardPosition, addNote, setNotePosition, setNoteSize,