diff --git a/canvas/src/components/Canvas.tsx b/canvas/src/components/Canvas.tsx index 16335608..c194e08f 100644 --- a/canvas/src/components/Canvas.tsx +++ b/canvas/src/components/Canvas.tsx @@ -117,6 +117,12 @@ function CanvasInner() { } }, [pendingDelete, setPendingDelete, removeNode]); + // Cascade guard: include child count in the warning message when the workspace + // has children, so the user understands the blast radius before clicking Delete All. + const cascadeMessage = pendingDelete?.hasChildren + ? `⚠️ Deleting "${pendingDelete.name}" will permanently delete all child workspaces and their data. This cannot be undone.` + : null; + const onNodeDragStop: OnNodeDrag> = useCallback( (_event, node) => { const { dragOverNodeId, nodes: allNodes } = useCanvasStore.getState(); @@ -381,9 +387,11 @@ function CanvasInner() { {/* Confirmation dialog for workspace delete — driven by store */} setPendingDelete(null)} diff --git a/canvas/src/components/ContextMenu.tsx b/canvas/src/components/ContextMenu.tsx index 34c17b01..3b54cc17 100644 --- a/canvas/src/components/ContextMenu.tsx +++ b/canvas/src/components/ContextMenu.tsx @@ -164,7 +164,7 @@ export function ContextMenu() { // it survives ContextMenu unmount. Closing the menu here avoids the // prior race where the portal dialog's Confirm click was treated as // "outside" by the menu's outside-click handler. - setPendingDelete({ id: contextMenu.nodeId, name: contextMenu.nodeData.name }); + setPendingDelete({ id: contextMenu.nodeId, name: contextMenu.nodeData.name, hasChildren }); closeContextMenu(); }, [contextMenu, setPendingDelete, closeContextMenu]); diff --git a/canvas/src/store/canvas.ts b/canvas/src/store/canvas.ts index 7c7cc314..692b5bdb 100644 --- a/canvas/src/store/canvas.ts +++ b/canvas/src/store/canvas.ts @@ -72,8 +72,8 @@ interface CanvasState { // handler: clicking Confirm registered as "outside", closed the menu, and // unmounted the dialog before its onClick fired. Hoisting the state fixes // that — see fix/context-menu-delete-race. - pendingDelete: { id: string; name: string } | null; - setPendingDelete: (v: { id: string; name: string } | null) => void; + pendingDelete: { id: string; name: string; hasChildren: boolean } | null; + setPendingDelete: (v: { id: string; name: string; hasChildren: boolean } | null) => void; searchOpen: boolean; setSearchOpen: (open: boolean) => void; viewport: { x: number; y: number; zoom: number };