diff --git a/canvas/src/components/SidePanel.tsx b/canvas/src/components/SidePanel.tsx index c8b6456e..46322fea 100644 --- a/canvas/src/components/SidePanel.tsx +++ b/canvas/src/components/SidePanel.tsx @@ -46,11 +46,15 @@ export function SidePanel() { const panelTab = useCanvasStore((s) => s.panelTab); const setPanelTab = useCanvasStore((s) => s.setPanelTab); const selectNode = useCanvasStore((s) => s.selectNode); + const setSidePanelWidth = useCanvasStore((s) => s.setSidePanelWidth); const node = useCanvasStore((s) => s.nodes.find((n) => n.id === s.selectedNodeId) ); - // Resizable panel width — persisted across node selections via localStorage + // Resizable panel width — persisted across node selections via localStorage. + // Also published to the canvas store on every change so the centered + // Toolbar can re-centre itself on the remaining canvas area (avoids the + // Audit / Search / Settings buttons hiding under the panel). const [width, setWidth] = useState(() => { if (typeof window === "undefined") return SIDEPANEL_DEFAULT_WIDTH; const saved = localStorage.getItem(SIDEPANEL_WIDTH_KEY); @@ -59,6 +63,9 @@ export function SidePanel() { ? parsed : SIDEPANEL_DEFAULT_WIDTH; }); + useEffect(() => { + setSidePanelWidth(width); + }, [width, setSidePanelWidth]); const widthRef = useRef(width); // tracks live drag value for the mouseup handler const dragging = useRef(false); const startX = useRef(0); diff --git a/canvas/src/components/Toolbar.tsx b/canvas/src/components/Toolbar.tsx index f994c75b..19cd04d2 100644 --- a/canvas/src/components/Toolbar.tsx +++ b/canvas/src/components/Toolbar.tsx @@ -16,6 +16,17 @@ export function Toolbar() { const setShowA2AEdges = useCanvasStore((s) => s.setShowA2AEdges); const selectedNodeId = useCanvasStore((s) => s.selectedNodeId); const setPanelTab = useCanvasStore((s) => s.setPanelTab); + const sidePanelWidth = useCanvasStore((s) => s.sidePanelWidth); + + // Toolbar is fixed + centred on the viewport. When a workspace is + // selected the SidePanel (z-50, fixed right-0) opens and covers the + // right edge of the viewport — without this adjustment, the right + // half of the Toolbar (Audit / Search / Help / Settings) hides + // behind the panel. Shifting the toolbar LEFT by half the panel + // width re-centres it on the remaining canvas area. + const toolbarOffsetStyle = selectedNodeId + ? { marginLeft: `-${sidePanelWidth / 2}px` } + : undefined; const [stopping, setStopping] = useState(false); const [restartingAll, setRestartingAll] = useState(false); @@ -116,7 +127,10 @@ export function Toolbar() { }, []); return ( -
+
{/* Logo / Title */}
Molecule AI diff --git a/canvas/src/components/__tests__/SidePanel.tabs.test.tsx b/canvas/src/components/__tests__/SidePanel.tabs.test.tsx index ae16e094..f1181ba1 100644 --- a/canvas/src/components/__tests__/SidePanel.tabs.test.tsx +++ b/canvas/src/components/__tests__/SidePanel.tabs.test.tsx @@ -36,6 +36,10 @@ const mockStoreState = { panelTab: "chat", setPanelTab: mockSetPanelTab, selectNode: vi.fn(), + // Consumed by SidePanel's useEffect — publishes the drag-resized + // width to the store so Toolbar can re-centre itself on the + // remaining canvas area when the panel is open. + setSidePanelWidth: vi.fn(), nodes: [ { id: "ws-1", diff --git a/canvas/src/store/canvas.ts b/canvas/src/store/canvas.ts index 2b8a9ecf..e6f6f28a 100644 --- a/canvas/src/store/canvas.ts +++ b/canvas/src/store/canvas.ts @@ -51,6 +51,13 @@ interface CanvasState { panelTab: PanelTab; dragOverNodeId: string | null; contextMenu: ContextMenuState | null; + // Live width of the SidePanel in pixels. Only meaningful when + // selectedNodeId is non-null (panel visible). The Toolbar reads this + // to stay centred on the remaining canvas area instead of the full + // viewport, so the "Audit" / "Search" / "Settings" buttons don't get + // hidden behind the panel when a workspace is selected. + sidePanelWidth: number; + setSidePanelWidth: (w: number) => void; hydrate: (workspaces: WorkspaceData[]) => void; applyEvent: (msg: WSMessage) => void; onNodesChange: (changes: NodeChange>[]) => void; @@ -115,6 +122,8 @@ export const useCanvasStore = create((set, get) => ({ panelTab: "chat", dragOverNodeId: null, contextMenu: null, + sidePanelWidth: 480, // matches SIDEPANEL_DEFAULT_WIDTH in SidePanel.tsx + setSidePanelWidth: (w) => set({ sidePanelWidth: w }), // Batch selection selectedNodeIds: new Set(), toggleNodeSelection: (id) => {