({
functions,
onOpenFunction,
onCreateFunction,
canvasTabs,
onCanvasTabChange,
onCanvasTabClose,
onCanvasTabReorder,
onTestNode,
onDebugStep,
}: BuilderLayoutProps)
| 56 | |
| 57 | export const BuilderLayout = ({ |
| 58 | functions, |
| 59 | onOpenFunction, |
| 60 | onCreateFunction, |
| 61 | canvasTabs, |
| 62 | onCanvasTabChange, |
| 63 | onCanvasTabClose, |
| 64 | onCanvasTabReorder, |
| 65 | onDebugStep, |
| 66 | }: BuilderLayoutProps) => { |
| 67 | const activeCanvasId = useEditorStore((s) => s.activeCanvasId); |
| 68 | const builderMode = useEditorStore((s) => s.builderMode); |
| 69 | const readOnly = isReadOnly(builderMode); |
| 70 | const isDebugMode = builderMode.type === "debug"; |
| 71 | |
| 72 | // NodeRegistry (static) + dynamic function nodes — derived, not embedder-provided. |
| 73 | const { nodeDefinitions, getNodeDefinition, getAllCategories } = useNodeDefinitions(); |
| 74 | |
| 75 | const graph = useGraph(activeCanvasId, readOnly); |
| 76 | const { undo, redo, takeCheckpoint, canUndo, canRedo } = useCanvasHistory(activeCanvasId); |
| 77 | |
| 78 | // Selection (project-wide in editorStore, mirrored to canvas store for RF visual) |
| 79 | const selection = useEditorStore((s) => s.selection); |
| 80 | const selectGraph = useEditorStore((s) => s.selectGraph); |
| 81 | const syncSelectionFromRF = useEditorStore((s) => s.syncSelectionFromRF); |
| 82 | const clearSelection = useEditorStore((s) => s.clearSelection); |
| 83 | |
| 84 | // Sidebar tab state + mode auto-switch. |
| 85 | const activeSidebarTab = useEditorStore((s) => s.activeSidebarTab); |
| 86 | const setActiveSidebarTab = useEditorStore((s) => s.setActiveSidebarTab); |
| 87 | useEffect(() => { |
| 88 | const isDebugTab = activeSidebarTab === "debug-context"; |
| 89 | if (isDebugMode && !isDebugTab) { |
| 90 | setActiveSidebarTab("debug-context"); |
| 91 | } else if (!isDebugMode && isDebugTab) { |
| 92 | setActiveSidebarTab("nodes"); |
| 93 | } |
| 94 | }, [isDebugMode, activeSidebarTab, setActiveSidebarTab]); |
| 95 | |
| 96 | // Selection-drag flag (used by RightConfigPanel to suppress during drag). |
| 97 | const [selectionDrag, setSelectionDrag] = useState(false); |
| 98 | |
| 99 | // ViewportCenter ref (populated by ReactFlow inside CanvasEditor, consumed |
| 100 | // here for sidebar's click-to-add path). |
| 101 | const viewportCenterRef = useRef<(() => { x: number; y: number }) | null>(null); |
| 102 | |
| 103 | // ── Handlers ────────────────────────────────────────────────────────────── |
| 104 | |
| 105 | const selectNodeById: (id: string) => void = useCallback( |
| 106 | (nodeId: string) => selectGraph([nodeId], []), |
| 107 | [selectGraph], |
| 108 | ); |
| 109 | |
| 110 | const selectEdgeById = useCallback((edgeId: string) => selectGraph([], [edgeId]), [selectGraph]); |
| 111 | |
| 112 | const handleAddNode = useCallback( |
| 113 | (nodeDef: NodeDefinition, position?: { x: number; y: number }) => { |
| 114 | const pos = position ?? viewportCenterRef.current?.(); |
| 115 | const id = graph.addNode(nodeDef, pos); |
nothing calls this directly
no test coverage detected