'use client'; import * as React from 'react'; import { graphToFlow, type FlowNode, type FlowEdge } from '../serialization'; import { autoLayout, type LayoutDirection } from '../layout/auto-layout'; import type { WorkflowGraph, NodeTypeDef } from '../types'; import type { WorkflowNodeData } from '../WorkflowNode'; export interface UseCanvasGraphOptions { /** Node-type catalog used to enrich nodes with category + port metadata. */ nodeTypes?: NodeTypeDef[]; /** Layout direction for nodes without an explicit position. Default 'LR'. */ direction?: LayoutDirection; /** Inline-rename callback wired onto every node's data. */ onRename?: (nodeId: string, name: string) => void; } /** Index a node-type catalog by slug for O(1) lookup. */ function indexNodeTypes( nodeTypes: NodeTypeDef[] | undefined ): Record { const index: Record = {}; for (const nt of nodeTypes ?? []) index[nt.slug] = nt; return index; } /** * Derive laid-out xyflow nodes/edges from a {@link WorkflowGraph}, enriching * each node with its category, output ports/labels (from the node-type * catalog), and the rename callback. Auto-layout fills positions for nodes * that don't carry an explicit one (explicit positions are preserved). */ export function useCanvasGraph( graph: WorkflowGraph, options: UseCanvasGraphOptions = {} ): { nodes: FlowNode[]; edges: FlowEdge[] } { const { nodeTypes, direction = 'LR', onRename } = options; return React.useMemo(() => { const typeIndex = indexNodeTypes(nodeTypes); const { nodes, edges } = graphToFlow(graph); const enriched: FlowNode[] = nodes.map((n) => { const wf = n.data.node; const def = typeIndex[wf.type]; const category = def?.category ?? wf.type.split('.')[0]; const outputCount = def?.outputCount ?? 1; const data: WorkflowNodeData = { node: wf, category, outputCount, outputLabels: def?.outputLabels, onRename, status: 'idle', }; return { ...n, type: 'workflowNode', data }; }); const typedEdges: FlowEdge[] = edges.map((e) => ({ ...e, type: 'workflowEdge', })); const laid = autoLayout(enriched, typedEdges, { direction, preservePositions: true, }); return { nodes: laid.nodes, edges: typedEdges }; }, [graph, nodeTypes, direction, onRename]); }