@octaviaflow/core 3.0.18-beta.25 → 3.0.18-beta.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/styles.css +1 -1
- package/dist/workflow/components/FxPanel/DataRefButton.d.ts +9 -0
- package/dist/workflow/components/FxPanel/DataRefButton.d.ts.map +1 -0
- package/dist/workflow/components/FxPanel/FxPanel.d.ts +0 -2
- package/dist/workflow/components/FxPanel/FxPanel.d.ts.map +1 -1
- package/dist/workflow/index.d.ts +1 -1
- package/dist/workflow/index.d.ts.map +1 -1
- package/dist/workflow.cjs +26 -49
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.js +29 -53
- package/dist/workflow.js.map +1 -1
- package/package.json +2 -1
package/dist/workflow.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/workflow/hooks/useAutoLayout.ts","../src/workflow/hooks/useFlowState.ts","../src/workflow/layout/dagre.ts","../src/workflow/layout/elk.ts","../src/workflow/components/ConfigPanel/ConfigPanel.tsx","../src/workflow/components/FxPanel/FxPanel.tsx","../src/workflow/components/FxPanel/FxToggleButton.tsx","../src/workflow/components/NodeToolbar/NodeToolbar.tsx","../src/workflow/utils/collapse.ts"],"sourcesContent":["// ════════════════════════════════════════════════════════════════════════\n// useAutoLayout — run a layout engine and apply its output via the\n// canvas's `onNodesChange` callback.\n// ════════════════════════════════════════════════════════════════════════\n// Two trigger modes:\n//\n// 1. Imperative — call the returned `runLayout()` function (e.g. from a\n// toolbar button). This is the recommended default; users press\n// \"Auto-arrange\" and the engine runs once.\n//\n// 2. Reactive — pass `auto: true`. The hook debounces 250ms on every\n// node/edge add/remove and runs the engine. Useful for \"always\n// auto-layout\" surfaces; expensive in graphs where users drag nodes\n// around (their positions get clobbered).\n//\n// The hook does NOT subscribe to selection / drag / viewport — it watches\n// only the *shape* of the graph (node ids + edge connections). That\n// keeps reactive mode from re-running on every pointermove.\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport type {\n LayoutEngine,\n LayoutOptions,\n NodeChange,\n WorkflowEdge,\n WorkflowNode,\n} from \"../editor\";\nimport { change } from \"../store/changes\";\n\nexport interface UseAutoLayoutOptions {\n /** The engine. Required. */\n engine: LayoutEngine | null | undefined;\n /** Current nodes (controlled). */\n nodes: WorkflowNode[];\n /** Current edges (controlled). */\n edges: WorkflowEdge[];\n /** Where to send position changes. Required. */\n onNodesChange?: (changes: NodeChange[]) => void;\n /** Layout options forwarded to the engine. */\n options?: LayoutOptions;\n /** Re-run on every graph-shape change (debounced 250ms). Default false. */\n auto?: boolean;\n /** Debounce delay for `auto` mode (ms). Default 250. */\n debounceMs?: number;\n /** Called after each successful run. */\n onLayout?: (positions: { id: string; position: { x: number; y: number } }[]) => void;\n /** Called when the engine throws (network / worker / library). */\n onError?: (err: unknown) => void;\n}\n\nexport interface UseAutoLayoutResult {\n /** Manually trigger a layout pass. Returns the promise of the engine. */\n runLayout(): Promise<void>;\n /** True while a layout pass is in flight. */\n isLaying: boolean;\n}\n\n/**\n * Build a stable graph-shape signature so reactive mode only re-runs when\n * the structure (node ids + edges) changes, not on position/select/drag.\n */\nfunction signature(nodes: WorkflowNode[], edges: WorkflowEdge[]): string {\n // Sorted to be invariant under array re-ordering.\n const nodeKeys = nodes\n .map((n) => `${n.id}:${n.parentId ?? \"\"}`)\n .sort()\n .join(\"|\");\n const edgeKeys = edges\n .map((e) => `${e.id}:${e.source}>${e.target}`)\n .sort()\n .join(\"|\");\n return `${nodeKeys}::${edgeKeys}`;\n}\n\nexport function useAutoLayout({\n engine,\n nodes,\n edges,\n onNodesChange,\n options,\n auto = false,\n debounceMs = 250,\n onLayout,\n onError,\n}: UseAutoLayoutOptions): UseAutoLayoutResult {\n const [isLaying, setIsLaying] = useState(false);\n const runningRef = useRef(false);\n const optionsRef = useRef(options);\n const onLayoutRef = useRef(onLayout);\n const onErrorRef = useRef(onError);\n const onNodesChangeRef = useRef(onNodesChange);\n useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n useEffect(() => {\n onLayoutRef.current = onLayout;\n }, [onLayout]);\n useEffect(() => {\n onErrorRef.current = onError;\n }, [onError]);\n useEffect(() => {\n onNodesChangeRef.current = onNodesChange;\n }, [onNodesChange]);\n\n // Snapshot refs of nodes/edges so the runLayout callback can read fresh\n // data without re-creating itself on every render.\n const nodesRef = useRef(nodes);\n const edgesRef = useRef(edges);\n useEffect(() => {\n nodesRef.current = nodes;\n }, [nodes]);\n useEffect(() => {\n edgesRef.current = edges;\n }, [edges]);\n\n const runLayout = useCallback(async () => {\n if (!engine) return;\n if (runningRef.current) return;\n runningRef.current = true;\n setIsLaying(true);\n try {\n const positions = await engine.layout(nodesRef.current, edgesRef.current, optionsRef.current);\n\n // Index engine output by id + remember per-node deltas so we can\n // transitively apply them to descendants (children move with the\n // parent — otherwise a parent moves and its children get stranded\n // outside the new bbox, which is the v1 subflow auto-layout bug).\n const positionedIds = new Set(positions.map((p) => p.id));\n const deltas = new Map<string, { dx: number; dy: number }>();\n\n const cs: NodeChange[] = [];\n for (const p of positions) {\n const cur = nodesRef.current.find((n) => n.id === p.id);\n if (!cur) continue;\n if (cur.position.x === p.position.x && cur.position.y === p.position.y) continue;\n cs.push(change.node.position(p.id, p.position, false));\n deltas.set(p.id, {\n dx: p.position.x - cur.position.x,\n dy: p.position.y - cur.position.y,\n });\n }\n\n // For every node the engine did NOT reposition (typically because\n // it sat in a sub-flow that the engine treated as opaque), walk up\n // the parentId chain to find the nearest moved ancestor. Shift the\n // descendant by that ancestor's delta. The first moved ancestor\n // wins — we don't sum deltas because each child's position is\n // already relative to its ancestor's old position.\n const findParent = (id: string): WorkflowNode | undefined =>\n nodesRef.current.find((x) => x.id === id);\n\n for (const n of nodesRef.current) {\n if (positionedIds.has(n.id)) continue;\n if (!n.parentId) continue;\n let cursor: string | undefined = n.parentId;\n const seen = new Set<string>();\n let delta: { dx: number; dy: number } | undefined;\n while (cursor) {\n if (seen.has(cursor)) break; // cycle guard\n seen.add(cursor);\n const found = deltas.get(cursor);\n if (found) {\n delta = found;\n break;\n }\n cursor = findParent(cursor)?.parentId;\n }\n if (!delta) continue;\n cs.push(\n change.node.position(\n n.id,\n { x: n.position.x + delta.dx, y: n.position.y + delta.dy },\n false,\n ),\n );\n }\n\n if (cs.length) onNodesChangeRef.current?.(cs);\n onLayoutRef.current?.(positions);\n } catch (err) {\n onErrorRef.current?.(err);\n } finally {\n runningRef.current = false;\n setIsLaying(false);\n }\n }, [engine]);\n\n // Reactive auto-mode — debounce on graph-shape changes.\n const sig = signature(nodes, edges);\n const sigRef = useRef(sig);\n useEffect(() => {\n if (!auto) return;\n if (sigRef.current === sig) return; // first render after toggling auto\n sigRef.current = sig;\n const timer = setTimeout(() => void runLayout(), debounceMs);\n return () => clearTimeout(timer);\n }, [sig, auto, debounceMs, runLayout]);\n\n return { runLayout, isLaying };\n}\n","// ════════════════════════════════════════════════════════════════════════\n// useFlowState — the uncontrolled-mode convenience.\n// ════════════════════════════════════════════════════════════════════════\n// Consumers that don't want to thread state through their own reducer\n// can use this hook and spread the result into <FlowCanvas>:\n//\n// const flow = useFlowState({ initialNodes, initialEdges });\n// <FlowCanvas {...flow} />\n//\n// In addition to the canonical change-stream contract, `useFlowState`\n// exposes per-node helpers — `updateNodeData(id, patch)` and the\n// companion `useNodeData(state, id)` selector — so individual node\n// updates can be expressed without re-mapping the entire nodes array\n// in caller code. Each helper writes ONLY the targeted node's slice;\n// React.memo'd kind components for other nodes never re-render.\n\nimport { useCallback, useMemo, useRef, useState } from \"react\";\nimport type { EdgeChange, NodeChange, WorkflowEdge, WorkflowNode } from \"../editor\";\nimport { applyEdgeChanges, applyNodeChanges } from \"../store/changes\";\n\nexport interface UseFlowStateOptions<TNData = unknown, TEData = unknown> {\n initialNodes?: WorkflowNode<TNData>[];\n initialEdges?: WorkflowEdge<TEData>[];\n}\n\n/**\n * Patch type for `updateNodeData` — accepts a partial object that gets\n * shallow-merged, or a producer function `(prev) => next`.\n */\nexport type NodeDataPatch<TNData> =\n | Partial<TNData>\n | ((prev: TNData | undefined) => TNData | undefined);\n\nexport interface FlowStateBag<TNData = unknown, TEData = unknown> {\n nodes: WorkflowNode<TNData>[];\n edges: WorkflowEdge<TEData>[];\n onNodesChange: (changes: NodeChange<TNData>[]) => void;\n onEdgesChange: (changes: EdgeChange<TEData>[]) => void;\n setNodes: (\n update: WorkflowNode<TNData>[] | ((prev: WorkflowNode<TNData>[]) => WorkflowNode<TNData>[]),\n ) => void;\n setEdges: (\n update: WorkflowEdge<TEData>[] | ((prev: WorkflowEdge<TEData>[]) => WorkflowEdge<TEData>[]),\n ) => void;\n /**\n * Update the `data` slice of a single node by id. Only that node's\n * reference changes — every other node keeps its previous reference,\n * so React.memo'd kind components for unrelated nodes do not re-render.\n * No-ops when `id` is not present in the current nodes array.\n */\n updateNodeData: (id: string, patch: NodeDataPatch<TNData>) => void;\n /**\n * Read a single node by id from the latest snapshot. Returns undefined\n * if the id is unknown. Useful inside imperative callbacks that need\n * the freshest value without subscribing to re-renders.\n */\n getNode: (id: string) => WorkflowNode<TNData> | undefined;\n}\n\nexport function useFlowState<TNData = unknown, TEData = unknown>(\n options: UseFlowStateOptions<TNData, TEData> = {},\n): FlowStateBag<TNData, TEData> {\n const [nodes, setNodes] = useState<WorkflowNode<TNData>[]>(options.initialNodes ?? []);\n const [edges, setEdges] = useState<WorkflowEdge<TEData>[]>(options.initialEdges ?? []);\n\n // Ref-mirror of the latest nodes array. `getNode` reads through it so\n // imperative callers see fresh values without depending on render scope.\n const nodesRef = useRef(nodes);\n nodesRef.current = nodes;\n\n const onNodesChange = useCallback((changes: NodeChange<TNData>[]) => {\n setNodes((prev) => applyNodeChanges(prev, changes));\n }, []);\n\n const onEdgesChange = useCallback((changes: EdgeChange<TEData>[]) => {\n setEdges((prev) => applyEdgeChanges(prev, changes));\n }, []);\n\n const updateNodeData = useCallback((id: string, patch: NodeDataPatch<TNData>) => {\n setNodes((prev) => {\n let touched = false;\n const next: WorkflowNode<TNData>[] = prev.map((n) => {\n if (n.id !== id) return n;\n const nextData =\n typeof patch === \"function\"\n ? (patch as (p: TNData | undefined) => TNData | undefined)(n.data)\n : ({ ...(n.data ?? ({} as TNData)), ...patch } as TNData);\n if (Object.is(nextData, n.data)) return n;\n touched = true;\n return { ...n, data: nextData as TNData };\n });\n return touched ? next : prev;\n });\n }, []);\n\n const getNode = useCallback((id: string) => nodesRef.current.find((n) => n.id === id), []);\n\n return useMemo(\n () => ({\n nodes,\n edges,\n onNodesChange,\n onEdgesChange,\n setNodes,\n setEdges,\n updateNodeData,\n getNode,\n }),\n [nodes, edges, onNodesChange, onEdgesChange, updateNodeData, getNode],\n );\n}\n","// ════════════════════════════════════════════════════════════════════════\n// Dagre adapter — wraps a consumer-supplied `dagre` module.\n// ════════════════════════════════════════════════════════════════════════\n// Usage:\n//\n// import dagre from \"dagre\";\n// import { createDagreEngine } from \"@octaviaflow/core/workflow\";\n//\n// const engine = createDagreEngine(dagre);\n// useAutoLayout({ engine, onLayout });\n//\n// Dagre is synchronous — `layout()` blocks the main thread. Good for\n// small graphs (≤ ~200 nodes). For larger graphs, see `createElkEngine`.\n\nimport type { LayoutEngine, LayoutOptions, Position, WorkflowEdge, WorkflowNode } from \"../editor\";\nimport { DEFAULT_NODE_HEIGHT, DEFAULT_NODE_WIDTH } from \"../utils/geometry\";\nimport type { DagreLike } from \"./types\";\n\nexport interface CreateDagreEngineOptions {\n /** Default spacing between nodes within a rank (default 60). */\n nodeSpacing?: number;\n /** Default spacing between ranks (default 80). */\n rankSpacing?: number;\n /** Default direction — TB, BT, LR, RL. Defaults to TB. */\n direction?: LayoutOptions[\"direction\"];\n}\n\n/**\n * Build a `LayoutEngine` backed by a consumer-supplied `dagre` module.\n *\n * `dagre` can be passed as either the default export or the namespace\n * import (`import * as dagre from \"dagre\"`). The adapter probes both\n * shapes so consumers don't have to think about it.\n */\nexport function createDagreEngine(\n dagre:\n | DagreLike\n | { default: DagreLike }\n | { graphlib: DagreLike[\"graphlib\"]; layout: DagreLike[\"layout\"] },\n defaults: CreateDagreEngineOptions = {},\n): LayoutEngine {\n const lib = resolveDagre(dagre);\n const defaultDir = defaults.direction ?? \"TB\";\n const defaultNodeSpacing = defaults.nodeSpacing ?? 60;\n const defaultRankSpacing = defaults.rankSpacing ?? 80;\n\n return {\n name: \"dagre\",\n async layout(nodes: WorkflowNode[], edges: WorkflowEdge[], options?: LayoutOptions) {\n const direction = options?.direction ?? defaultDir;\n const nodesep = options?.nodeSpacing ?? defaultNodeSpacing;\n const ranksep = options?.rankSpacing ?? defaultRankSpacing;\n\n // Dagre requires every node to have width/height. Use the measured\n // values from the canvas; fall back to our default footprint.\n const g = new lib.graphlib.Graph({ multigraph: true, compound: true });\n g.setGraph({\n rankdir: direction,\n nodesep,\n ranksep,\n ...(options?.engine ?? {}),\n });\n g.setDefaultEdgeLabel(() => ({}));\n\n for (const n of nodes) {\n if (n.hidden) continue;\n g.setNode(n.id, {\n width: n.width ?? DEFAULT_NODE_WIDTH,\n height: n.height ?? DEFAULT_NODE_HEIGHT,\n ...(n.parentId ? { parent: n.parentId } : {}),\n });\n // Compound graph: register the parent–child relationship.\n if (n.parentId) {\n // `setParent` is on the graph but not strongly typed in our\n // narrow surface — call via index access. dagre tolerates a\n // missing parent node (it auto-creates).\n (g as unknown as { setParent: (id: string, parent: string) => void }).setParent(\n n.id,\n n.parentId,\n );\n }\n }\n\n for (const e of edges) {\n if (e.hidden) continue;\n g.setEdge(e.source, e.target, { id: e.id });\n }\n\n lib.layout(g);\n\n // Dagre stores node centres; our model uses top-left. Translate.\n const result: { id: string; position: Position }[] = [];\n for (const id of g.nodes()) {\n const out = g.node(id);\n if (!out) continue;\n result.push({\n id,\n position: { x: out.x - out.width / 2, y: out.y - out.height / 2 },\n });\n }\n return result;\n },\n };\n}\n\nfunction resolveDagre(input: unknown): DagreLike {\n const asAny = input as Partial<DagreLike> & { default?: DagreLike };\n if (asAny && typeof asAny.layout === \"function\" && asAny.graphlib?.Graph) {\n return asAny as DagreLike;\n }\n if (asAny?.default && typeof asAny.default.layout === \"function\") {\n return asAny.default;\n }\n throw new Error(\n \"[@octaviaflow/core/workflow] createDagreEngine: input does not look like the dagre module. \" +\n \"Pass `dagre` (the default export) or `import * as dagre from 'dagre'`.\",\n );\n}\n","// ════════════════════════════════════════════════════════════════════════\n// ELK adapter — wraps a consumer-supplied `elkjs` instance.\n// ════════════════════════════════════════════════════════════════════════\n// Usage (Web Worker — recommended for large graphs):\n//\n// import ELK from \"elkjs/lib/elk-api\";\n// import { createElkEngine } from \"@octaviaflow/core/workflow\";\n//\n// const elk = new ELK({ workerUrl: \"/elk-worker.min.js\" });\n// const engine = createElkEngine(elk);\n//\n// Usage (in-process, no worker):\n//\n// import ELK from \"elkjs/lib/elk.bundled\";\n// const elk = new ELK();\n// const engine = createElkEngine(elk);\n//\n// ELK supports compound graphs natively — nodes with `parentId` get\n// nested into the right `children[]` so subflow layouts just work.\n\nimport type { LayoutEngine, LayoutOptions, Position, WorkflowEdge, WorkflowNode } from \"../editor\";\nimport { DEFAULT_NODE_HEIGHT, DEFAULT_NODE_WIDTH } from \"../utils/geometry\";\nimport type { ElkEdge, ElkGraph, ElkLike } from \"./types\";\n\nexport interface CreateElkEngineOptions {\n /** Default direction — TB / BT / LR / RL. Defaults to TB. */\n direction?: LayoutOptions[\"direction\"];\n /** Default node-to-node spacing within a layer (default 60). */\n nodeSpacing?: number;\n /** Default layer-to-layer spacing (default 80). */\n rankSpacing?: number;\n /** Override the algorithm. Defaults to `layered` (Sugiyama). */\n algorithm?:\n | \"layered\"\n | \"mrtree\"\n | \"force\"\n | \"stress\"\n | \"radial\"\n | \"disco\"\n | \"rectpacking\"\n | \"box\";\n}\n\nconst ELK_DIRECTION: Record<NonNullable<LayoutOptions[\"direction\"]>, string> = {\n TB: \"DOWN\",\n BT: \"UP\",\n LR: \"RIGHT\",\n RL: \"LEFT\",\n};\n\n/**\n * Build a `LayoutEngine` backed by a consumer-supplied `elkjs` instance.\n *\n * The consumer is responsible for constructing the ELK instance (which\n * may or may not run in a Web Worker — that's a `workerUrl` constructor\n * option on their end). Our adapter just calls `elk.layout(graph)` and\n * patches positions back.\n */\nexport function createElkEngine(elk: ElkLike, defaults: CreateElkEngineOptions = {}): LayoutEngine {\n const algorithm = defaults.algorithm ?? \"layered\";\n const defaultDir = defaults.direction ?? \"TB\";\n const defaultNodeSpacing = defaults.nodeSpacing ?? 60;\n const defaultRankSpacing = defaults.rankSpacing ?? 80;\n\n return {\n name: \"elk\",\n async layout(nodes: WorkflowNode[], edges: WorkflowEdge[], options?: LayoutOptions) {\n const direction = options?.direction ?? defaultDir;\n const nodeSpacing = options?.nodeSpacing ?? defaultNodeSpacing;\n const rankSpacing = options?.rankSpacing ?? defaultRankSpacing;\n\n // Build a nested ELK graph respecting `parentId` so compound layout\n // works. We walk the node list once into a parent → children map,\n // then recursively build ElkGraph.children[].\n const childrenByParent = new Map<string | undefined, WorkflowNode[]>();\n for (const n of nodes) {\n if (n.hidden) continue;\n const key = n.parentId ?? undefined;\n const list = childrenByParent.get(key);\n if (list) list.push(n);\n else childrenByParent.set(key, [n]);\n }\n\n const buildChildren = (parentId: string | undefined): ElkGraph[] => {\n const list = childrenByParent.get(parentId) ?? [];\n return list.map<ElkGraph>((n) => ({\n id: n.id,\n width: n.width ?? DEFAULT_NODE_WIDTH,\n height: n.height ?? DEFAULT_NODE_HEIGHT,\n children: childrenByParent.has(n.id) ? buildChildren(n.id) : undefined,\n layoutOptions: childrenByParent.has(n.id)\n ? {\n \"elk.algorithm\": algorithm,\n \"elk.direction\": ELK_DIRECTION[direction],\n \"elk.spacing.nodeNode\": String(nodeSpacing),\n \"elk.layered.spacing.nodeNodeBetweenLayers\": String(rankSpacing),\n }\n : undefined,\n }));\n };\n\n const elkEdges: ElkEdge[] = edges\n .filter((e) => !e.hidden)\n .map((e) => ({\n id: e.id,\n sources: [e.source],\n targets: [e.target],\n }));\n\n const graph: ElkGraph = {\n id: \"root\",\n layoutOptions: {\n \"elk.algorithm\": algorithm,\n \"elk.direction\": ELK_DIRECTION[direction],\n \"elk.spacing.nodeNode\": String(nodeSpacing),\n \"elk.layered.spacing.nodeNodeBetweenLayers\": String(rankSpacing),\n ...Object.fromEntries(\n Object.entries(options?.engine ?? {}).map(([k, v]) => [k, String(v)]),\n ),\n },\n children: buildChildren(undefined),\n edges: elkEdges,\n };\n\n const laidOut = await elk.layout(graph);\n\n // Walk the resulting graph and collect (id, position). ELK returns\n // positions relative to the parent — we walk parents to flatten\n // back to absolute flow-space coordinates (matches our ADR-003\n // \"positions are always absolute\" rule).\n const result: { id: string; position: Position }[] = [];\n const walk = (g: ElkGraph, ox: number, oy: number) => {\n const ax = ox + (g.x ?? 0);\n const ay = oy + (g.y ?? 0);\n if (g.id !== \"root\") {\n result.push({ id: g.id, position: { x: ax, y: ay } });\n }\n if (g.children) {\n for (const c of g.children) walk(c, ax, ay);\n }\n };\n walk(laidOut, 0, 0);\n return result;\n },\n };\n}\n","\"use client\";\n\n// ════════════════════════════════════════════════════════════════════════\n// ConfigPanel v2 — right-side editor for the selected node.\n// ════════════════════════════════════════════════════════════════════════\n// Improvements over v1:\n// - Tabbed body (config / input / output schema; consumer can override)\n// - Resizable width (drag the left edge)\n// - Pinned vs floating modes\n// - Breadcrumb header showing node kind → title\n// - Footer with save-state pill (dirty / saving / saved)\n// - Slot for inline status banner (validation errors, etc.)\n// - Inline-edit-the-title affordance (double-click)\n// - Sticky footer with save / cancel buttons\n\nimport {\n type CSSProperties,\n type PointerEvent,\n type KeyboardEvent as ReactKeyboardEvent,\n type ReactNode,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { cn } from \"../../../utils/cn\";\n\nexport type ConfigPanelSaveState = \"clean\" | \"dirty\" | \"saving\" | \"saved\" | \"error\";\n\nexport interface ConfigPanelTab {\n id: string;\n label: ReactNode;\n /** Optional badge (e.g. count of errors). */\n badge?: ReactNode;\n /** When true, the tab is rendered but disabled. */\n disabled?: boolean;\n content: ReactNode;\n}\n\nexport interface ConfigPanelProps {\n open?: boolean;\n /** Title shown in the header — the action name. Double-click opens\n * the inline editor. The header shows this single line only. */\n title?: string;\n /** Optional icon rendered before the title. */\n icon?: ReactNode;\n /**\n * Modular header action slot — buttons rendered between the title and\n * the close (✕). Use it for per-action affordances like the FX\n * toggle; omit entirely for actions that don't need them.\n */\n headerActions?: ReactNode;\n /** Inline status banner — error / warning / hint, shown at the top of the body. */\n banner?: ReactNode;\n /** Tab definitions. When omitted, `children` renders directly (no tabs). */\n tabs?: ConfigPanelTab[];\n /** Initial active tab. Controlled if `onTabChange` is provided. */\n activeTab?: string;\n defaultActiveTab?: string;\n onTabChange?: (id: string) => void;\n /** Save-state pill in the footer. Omit it entirely to hide the pill —\n * the footer then shows only its action buttons. */\n saveState?: ConfigPanelSaveState;\n /** Fires on the title editor's commit. Omit to disable title editing. */\n onTitleChange?: (next: string) => void;\n onClose?: () => void;\n onSave?: () => void;\n onCancel?: () => void;\n /**\n * Padding (px, all sides) applied to the scrollable body — the work\n * area that holds the active tab's content. Default 12. Pass `0` when\n * the consumer's tab content already pads itself.\n */\n padding?: number;\n /** Resizable width — pass `resizable: false` to lock. */\n resizable?: boolean;\n /** Initial width (px). Defaults to 400. */\n defaultWidth?: number;\n /** Lower clamp during resize. Defaults to 400 — the panel can be\n * widened but never narrowed past its default. */\n minWidth?: number;\n /**\n * Upper clamp during resize. Defaults to 720. The panel ALSO clamps\n * to its parent container's measured width minus\n * `maxWidthContainerInset` (default 80 px) so the panel can never\n * cover the whole canvas — whichever is smaller wins.\n */\n maxWidth?: number;\n /**\n * How much of the parent container to leave visible at the panel's\n * widest. Subtracted from the parent's clientWidth before clamping\n * `maxWidth`. Defaults to 80 px.\n */\n maxWidthContainerInset?: number;\n /** \"pinned\" sits flush against the canvas; \"floating\" raises with shadow. */\n variant?: \"pinned\" | \"floating\";\n /** Body slot used only when `tabs` is omitted. */\n children?: ReactNode;\n /**\n * Modular footer action buttons — replaces the built-in Cancel/Save\n * pair. The footer bar itself stays static and pinned; this slot just\n * decides which buttons live in it, so a consumer can add, remove, or\n * reorder actions freely.\n */\n footerActions?: ReactNode;\n className?: string;\n style?: CSSProperties;\n}\n\nconst SAVE_STATE_LABEL: Record<ConfigPanelSaveState, string> = {\n clean: \"Saved\",\n dirty: \"Unsaved changes\",\n saving: \"Saving…\",\n saved: \"Saved\",\n error: \"Save failed\",\n};\n\nconst SAVE_STATE_DOT: Record<ConfigPanelSaveState, string> = {\n clean: \"var(--ods-text-tertiary)\",\n dirty: \"var(--ods-status-warning, #d97706)\",\n saving: \"var(--ods-status-running, #2563eb)\",\n saved: \"var(--ods-status-success, #16a34a)\",\n error: \"var(--ods-status-failed, #dc2626)\",\n};\n\nexport function ConfigPanel({\n open = true,\n title,\n icon,\n headerActions,\n banner,\n tabs,\n activeTab: controlledActive,\n defaultActiveTab,\n onTabChange,\n saveState,\n onTitleChange,\n onClose,\n onSave,\n onCancel,\n padding = 12,\n resizable = true,\n defaultWidth = 400,\n minWidth = 400,\n maxWidth = 720,\n maxWidthContainerInset = 80,\n variant = \"pinned\",\n children,\n footerActions,\n className,\n style,\n}: ConfigPanelProps) {\n const [width, setWidth] = useState(defaultWidth);\n // Effective upper bound — `min(maxWidth, parentClientWidth - inset)`.\n // Recomputed on every parent resize so the panel can never cover the\n // canvas. Starts at `maxWidth` so the first paint is sane; the\n // ResizeObserver below tightens it as soon as the layout settles.\n const [effectiveMax, setEffectiveMax] = useState(maxWidth);\n const panelRef = useRef<HTMLElement>(null);\n useEffect(() => {\n const el = panelRef.current;\n if (!el) return;\n const parent = el.parentElement;\n if (!parent || typeof ResizeObserver === \"undefined\") return;\n const update = () => {\n const room = parent.clientWidth - maxWidthContainerInset;\n const next = Math.max(minWidth, Math.min(maxWidth, room));\n setEffectiveMax(next);\n // If the current width exceeds the new ceiling (window shrank),\n // snap the panel down so it remains inside the canvas.\n setWidth((w) => (w > next ? next : w));\n };\n update();\n const ro = new ResizeObserver(update);\n ro.observe(parent);\n return () => ro.disconnect();\n }, [maxWidth, minWidth, maxWidthContainerInset]);\n const [internalActive, setInternalActive] = useState<string | undefined>(\n defaultActiveTab ?? tabs?.[0]?.id,\n );\n const activeTabId = controlledActive ?? internalActive;\n const selectTab = (id: string) => {\n if (controlledActive === undefined) setInternalActive(id);\n onTabChange?.(id);\n };\n\n // ── Title editor ────────────────────────────────────────────────────\n const [editingTitle, setEditingTitle] = useState(false);\n const [draftTitle, setDraftTitle] = useState(title ?? \"\");\n useEffect(() => {\n if (!editingTitle) setDraftTitle(title ?? \"\");\n }, [title, editingTitle]);\n const commitTitle = (next: string) => {\n setEditingTitle(false);\n if (next !== title) onTitleChange?.(next);\n };\n const titleKeyDown = (e: ReactKeyboardEvent<HTMLInputElement>) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n commitTitle(draftTitle);\n } else if (e.key === \"Escape\") {\n e.preventDefault();\n setEditingTitle(false);\n setDraftTitle(title ?? \"\");\n }\n };\n\n // ── Resize handle ───────────────────────────────────────────────────\n // We capture pointer events on the drag handle; the panel width is\n // updated on every pointermove. Width is clamped to [min, max].\n const resizeRef = useRef<{ pointerId: number; startX: number; startWidth: number } | null>(null);\n const onResizeDown = useCallback(\n (e: PointerEvent<HTMLDivElement>) => {\n if (!resizable) return;\n e.preventDefault();\n e.stopPropagation();\n resizeRef.current = { pointerId: e.pointerId, startX: e.clientX, startWidth: width };\n (e.target as HTMLElement).setPointerCapture(e.pointerId);\n },\n [resizable, width],\n );\n const onResizeMove = (e: PointerEvent<HTMLDivElement>) => {\n const r = resizeRef.current;\n if (!r || r.pointerId !== e.pointerId) return;\n // Dragging LEFT widens the panel (it sticks to the right edge of\n // the canvas). Upper bound is `effectiveMax` — the smaller of the\n // consumer's `maxWidth` and the parent container's available room,\n // so the panel can never grow past the canvas itself.\n const delta = r.startX - e.clientX;\n const next = Math.max(minWidth, Math.min(effectiveMax, r.startWidth + delta));\n setWidth(next);\n };\n const onResizeUp = (e: PointerEvent<HTMLDivElement>) => {\n if (resizeRef.current?.pointerId === e.pointerId) resizeRef.current = null;\n };\n\n if (!open) return null;\n\n const activeTab = tabs?.find((t) => t.id === activeTabId);\n\n return (\n <aside\n ref={panelRef}\n className={cn(\n \"ods-flow-config-panel\",\n `ods-flow-config-panel--${variant}`,\n editingTitle && \"ods-flow-config-panel--editing-title\",\n className,\n )}\n style={{ width, ...style }}\n aria-label={title ?? \"Configuration\"}\n >\n {resizable && (\n <div\n className=\"ods-flow-config-panel__resize\"\n onPointerDown={onResizeDown}\n onPointerMove={onResizeMove}\n onPointerUp={onResizeUp}\n onPointerCancel={onResizeUp}\n aria-hidden=\"true\"\n />\n )}\n\n <header className=\"ods-flow-config-panel__header\">\n <div className=\"ods-flow-config-panel__title-row\">\n {icon && <span className=\"ods-flow-config-panel__icon\">{icon}</span>}\n {editingTitle ? (\n <input\n className=\"ods-flow-config-panel__title-input\"\n value={draftTitle}\n autoFocus\n onChange={(e) => setDraftTitle(e.target.value)}\n onBlur={() => commitTitle(draftTitle)}\n onKeyDown={titleKeyDown}\n aria-label=\"Edit title\"\n />\n ) : (\n <button\n type=\"button\"\n className=\"ods-flow-config-panel__title\"\n onDoubleClick={() => {\n if (!onTitleChange) return;\n setDraftTitle(title ?? \"\");\n setEditingTitle(true);\n }}\n title={onTitleChange ? \"Double-click to rename\" : undefined}\n >\n {title ?? \"Untitled\"}\n </button>\n )}\n {headerActions && (\n <div className=\"ods-flow-config-panel__header-actions\">\n {headerActions}\n </div>\n )}\n {onClose && (\n <button\n type=\"button\"\n className=\"ods-flow-config-panel__close\"\n onClick={onClose}\n aria-label=\"Close panel\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" aria-hidden=\"true\">\n <path\n d=\"M3 3l8 8M11 3l-8 8\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n />\n </svg>\n </button>\n )}\n </div>\n </header>\n\n {tabs && tabs.length > 1 && (\n <nav className=\"ods-flow-config-panel__tabs\" role=\"tablist\">\n {tabs.map((t) => (\n <button\n key={t.id}\n type=\"button\"\n role=\"tab\"\n aria-selected={activeTabId === t.id}\n disabled={t.disabled}\n className={cn(\n \"ods-flow-config-panel__tab\",\n activeTabId === t.id && \"ods-flow-config-panel__tab--active\",\n )}\n onClick={() => selectTab(t.id)}\n >\n <span>{t.label}</span>\n {t.badge !== undefined && t.badge !== null && (\n <span className=\"ods-flow-config-panel__tab-badge\">{t.badge}</span>\n )}\n </button>\n ))}\n </nav>\n )}\n\n {banner && <div className=\"ods-flow-config-panel__banner\">{banner}</div>}\n\n <div\n className=\"ods-flow-config-panel__body\"\n role=\"tabpanel\"\n style={{ padding }}\n >\n {tabs ? activeTab?.content : children}\n </div>\n\n <footer className=\"ods-flow-config-panel__footer\">\n {saveState && (\n <span\n className={cn(\n \"ods-flow-config-panel__save-state\",\n `ods-flow-config-panel__save-state--${saveState}`,\n )}\n >\n <span\n className=\"ods-flow-config-panel__save-dot\"\n style={{ background: SAVE_STATE_DOT[saveState] }}\n aria-hidden=\"true\"\n />\n {SAVE_STATE_LABEL[saveState]}\n </span>\n )}\n <div className=\"ods-flow-config-panel__footer-actions\">\n {footerActions ?? (\n <>\n {onCancel && (\n <button\n type=\"button\"\n className=\"ods-flow-config-panel__btn ods-flow-config-panel__btn--ghost\"\n onClick={onCancel}\n >\n Cancel\n </button>\n )}\n {onSave && (\n <button\n type=\"button\"\n className=\"ods-flow-config-panel__btn ods-flow-config-panel__btn--primary\"\n onClick={onSave}\n disabled={saveState === \"saving\"}\n >\n {saveState === \"saving\" ? \"Saving…\" : \"Save\"}\n </button>\n )}\n </>\n )}\n </div>\n </footer>\n </aside>\n );\n}\n","\"use client\";\n\n// ════════════════════════════════════════════════════════════════════════\n// FxPanel — the FX / IO catalogue that docks beside the ConfigPanel.\n// ════════════════════════════════════════════════════════════════════════\n// A searchable catalogue of expression building-blocks:\n// - Functions — flat lists (CONCAT, UPPER, …).\n// - Variables — flat lists of system/session tokens.\n// - Source Data — an n-th-level NESTED TREE of upstream-node output\n// fields. Objects + arrays-of-objects are expandable branches;\n// primitives are leaves.\n//\n// Every item is draggable into any FX-aware input, click-to-insert, and\n// copy-to-clipboard. The component is data-driven: the consumer supplies\n// `categories`; FxPanel owns search, the accordion, the tree expansion,\n// keyboard navigation, and the drag/copy affordances.\n\nimport {\n type CSSProperties,\n type DragEvent,\n type KeyboardEvent as ReactKeyboardEvent,\n type ReactNode,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport {\n CheckmarkIcon,\n ChevronDownIcon,\n ChevronRightIcon,\n CloseIcon,\n CollapseAllIcon,\n CopyIcon,\n DraggableIcon,\n ExpandAllIcon,\n SearchIcon,\n} from \"@octaviaflow/icons\";\nimport { cn } from \"../../../utils/cn\";\n\nexport type FxItemKind = \"function\" | \"variable\";\n\n/** Data type of a Source-Data field — drives the colour-coded badge. */\nexport type FxValueType =\n | \"string\"\n | \"number\"\n | \"boolean\"\n | \"object\"\n | \"array\"\n | \"date\"\n | \"any\";\n\nexport interface FxPanelItem {\n /** Stable key + tree/keyboard identity. */\n id: string;\n /** Display label — a function signature, variable name, or field key. */\n label: string;\n /**\n * The string handed to `dataTransfer` / inserted into a field.\n * Branches may still set it (drag the whole object/array reference).\n */\n insertValue?: string;\n /** Short one-line description shown under the label. */\n description?: string;\n /** Data type — renders a colour-coded badge. Source-Data fields only. */\n valueType?: FxValueType;\n /** Sample value preview, shown dimmed at the end of the row. */\n preview?: string;\n /**\n * Nested children. When present the item renders as an expandable\n * branch — this is what powers the n-th-level Source-Data tree.\n */\n children?: FxPanelItem[];\n}\n\nexport interface FxPanelCategory {\n /** Stable key + accordion identity. */\n id: string;\n /** Section heading. */\n label: string;\n /** Drives the badge glyph + drag payload. Defaults to \"function\". */\n kind?: FxItemKind;\n /** Items — flat for functions/variables, nested for Source Data. */\n items: FxPanelItem[];\n}\n\nexport interface FxPanelProps {\n /** Categorised entries. Empty categories are dropped from render. */\n categories: FxPanelCategory[];\n /** Header title. Default \"FX / IO\". */\n title?: ReactNode;\n /** Instructional line under the search. Pass `null` to hide it. */\n hint?: ReactNode;\n /** Controlled search term. */\n search?: string;\n /** Initial search term when uncontrolled. */\n defaultSearch?: string;\n onSearchChange?: (next: string) => void;\n /** Controlled open category (single-open accordion). `null` = closed. */\n expandedCategory?: string | null;\n /** Initial open category when uncontrolled. */\n defaultExpandedCategory?: string | null;\n onExpandedCategoryChange?: (id: string | null) => void;\n /** Close (✕) handler. Omit to hide the close button. */\n onClose?: () => void;\n /**\n * Fires when an item begins dragging. Use it to populate\n * `e.dataTransfer`. When omitted, FxPanel writes a sensible default:\n * - `text/plain` → insertValue\n * - `application/x-fx-insert` → insertValue\n * - `application/x-fx-type` → category kind\n */\n onItemDragStart?: (\n item: FxPanelItem,\n category: FxPanelCategory,\n e: DragEvent<HTMLElement>,\n ) => void;\n /** Fires on click / Enter of an item (insert at cursor). */\n onItemSelect?: (item: FxPanelItem, category: FxPanelCategory) => void;\n /** Panel width (px). Default 300. */\n width?: number;\n /** Message shown when a search matches nothing. Default \"No matches\". */\n emptyLabel?: ReactNode;\n /** Placeholder for the search box. */\n searchPlaceholder?: string;\n className?: string;\n style?: CSSProperties;\n}\n\nconst KIND_GLYPH: Record<FxItemKind, string> = {\n function: \"ƒ\",\n variable: \"⬡\",\n};\n\n// ── Tree helpers ─────────────────────────────────────────────────────────\n\n/** Recursively count leaf items (primitives — the drag end-points). */\nfunction countLeaves(items: FxPanelItem[]): number {\n let n = 0;\n for (const it of items) {\n if (it.children && it.children.length > 0) n += countLeaves(it.children);\n else n += 1;\n }\n return n;\n}\n\n/** Collect the ids of every branch (has children) under `items`. */\nfunction collectBranchIds(items: FxPanelItem[], out: Set<string>): void {\n for (const it of items) {\n if (it.children && it.children.length > 0) {\n out.add(it.id);\n collectBranchIds(it.children, out);\n }\n }\n}\n\ninterface FlatRow {\n item: FxPanelItem;\n depth: number;\n hasChildren: boolean;\n expanded: boolean;\n}\n\n/** Flatten the visible tree (respecting `expandedItems`) into draw rows. */\nfunction flattenTree(\n items: FxPanelItem[],\n depth: number,\n expandedItems: Set<string>,\n out: FlatRow[],\n): void {\n for (const it of items) {\n const hasChildren = !!it.children && it.children.length > 0;\n const expanded = hasChildren && expandedItems.has(it.id);\n out.push({ item: it, depth, hasChildren, expanded });\n if (expanded) flattenTree(it.children!, depth + 1, expandedItems, out);\n }\n}\n\ninterface SearchHit {\n item: FxPanelItem;\n /** Ancestor labels (excluding the item itself) for the path crumb. */\n ancestors: string[];\n}\n\n/** Collect leaf items whose label / description / path matches `q`. */\nfunction collectHits(\n items: FxPanelItem[],\n ancestors: string[],\n q: string,\n out: SearchHit[],\n): void {\n for (const it of items) {\n if (it.children && it.children.length > 0) {\n collectHits(it.children, [...ancestors, it.label], q, out);\n } else {\n const path = [...ancestors, it.label].join(\" / \");\n const hay =\n `${it.label} ${it.description ?? \"\"} ${path} ${it.insertValue ?? \"\"}`.toLowerCase();\n if (hay.includes(q)) out.push({ item: it, ancestors });\n }\n }\n}\n\n// ════════════════════════════════════════════════════════════════════════\n\nexport function FxPanel({\n categories,\n title = \"FX / IO\",\n hint = (\n <>\n Drag a field into any input with the <strong>FX</strong> badge, or\n click to insert.\n </>\n ),\n search: controlledSearch,\n defaultSearch = \"\",\n onSearchChange,\n expandedCategory: controlledExpanded,\n defaultExpandedCategory,\n onExpandedCategoryChange,\n onClose,\n onItemDragStart,\n onItemSelect,\n width = 300,\n emptyLabel = \"No matches\",\n searchPlaceholder = \"Search fields, functions & variables…\",\n className,\n style,\n}: FxPanelProps) {\n // ── Search — controlled or uncontrolled ─────────────────────────────\n const [internalSearch, setInternalSearch] = useState(defaultSearch);\n const search = controlledSearch ?? internalSearch;\n const query = search.trim().toLowerCase();\n const setSearch = (next: string) => {\n if (controlledSearch === undefined) setInternalSearch(next);\n onSearchChange?.(next);\n };\n\n // Non-empty categories only — empty ones never render.\n const visibleCategories = useMemo(\n () => categories.filter((c) => c.items.length > 0),\n [categories],\n );\n\n // ── Category accordion — controlled or uncontrolled ────────────────\n const [internalCat, setInternalCat] = useState<string | null>(\n defaultExpandedCategory !== undefined\n ? defaultExpandedCategory\n : (visibleCategories[0]?.id ?? null),\n );\n const openCategoryId = controlledExpanded ?? internalCat;\n const setOpenCategory = (id: string | null) => {\n if (controlledExpanded === undefined) setInternalCat(id);\n onExpandedCategoryChange?.(id);\n };\n\n // ── Tree expansion — branch ids that are open. Defaults to every\n // depth-0 branch so the first level of each tree shows on open. ──\n const [expandedItems, setExpandedItems] = useState<Set<string>>(() => {\n const ids = new Set<string>();\n for (const cat of categories) {\n for (const item of cat.items) {\n if (item.children && item.children.length > 0) ids.add(item.id);\n }\n }\n return ids;\n });\n const toggleItem = (id: string) => {\n setExpandedItems((prev) => {\n const next = new Set(prev);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n };\n const setCategoryExpansion = (cat: FxPanelCategory, expand: boolean) => {\n const branchIds = new Set<string>();\n collectBranchIds(cat.items, branchIds);\n setExpandedItems((prev) => {\n const next = new Set(prev);\n for (const id of branchIds) {\n if (expand) next.add(id);\n else next.delete(id);\n }\n return next;\n });\n };\n\n // ── Transient UI state ──────────────────────────────────────────────\n const [draggingId, setDraggingId] = useState<string | null>(null);\n const [copiedId, setCopiedId] = useState<string | null>(null);\n const [focusedId, setFocusedId] = useState<string | null>(null);\n const copyTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n useEffect(\n () => () => {\n if (copyTimer.current) clearTimeout(copyTimer.current);\n },\n [],\n );\n\n // ── Search results ──────────────────────────────────────────────────\n const hits = useMemo(() => {\n if (!query) return null;\n const all: { category: FxPanelCategory; hit: SearchHit }[] = [];\n for (const cat of visibleCategories) {\n const out: SearchHit[] = [];\n collectHits(cat.items, [], query, out);\n for (const hit of out) all.push({ category: cat, hit });\n }\n return all;\n }, [query, visibleCategories]);\n\n // ── Drag / copy / insert ────────────────────────────────────────────\n const handleDragStart =\n (item: FxPanelItem, category: FxPanelCategory) =>\n (e: DragEvent<HTMLElement>) => {\n setDraggingId(item.id);\n if (onItemDragStart) {\n onItemDragStart(item, category, e);\n } else if (item.insertValue) {\n e.dataTransfer.setData(\"text/plain\", item.insertValue);\n e.dataTransfer.setData(\"application/x-fx-insert\", item.insertValue);\n e.dataTransfer.setData(\n \"application/x-fx-type\",\n category.kind ?? \"function\",\n );\n e.dataTransfer.effectAllowed = \"copy\";\n }\n };\n\n const handleCopy = (item: FxPanelItem) => {\n if (!item.insertValue || typeof navigator === \"undefined\") return;\n void navigator.clipboard?.writeText(item.insertValue);\n setCopiedId(item.id);\n if (copyTimer.current) clearTimeout(copyTimer.current);\n copyTimer.current = setTimeout(() => setCopiedId(null), 1400);\n };\n\n // ── Keyboard navigation over the open category's visible rows ───────\n const listRef = useRef<HTMLDivElement>(null);\n const focusRow = (id: string) => {\n setFocusedId(id);\n requestAnimationFrame(() => {\n listRef.current\n ?.querySelector<HTMLElement>(`[data-fx-row=\"${CSS.escape(id)}\"]`)\n ?.focus();\n });\n };\n const onRowKeyDown = (\n e: ReactKeyboardEvent<HTMLDivElement>,\n rows: FlatRow[],\n row: FlatRow,\n category: FxPanelCategory,\n ) => {\n const idx = rows.findIndex((r) => r.item.id === row.item.id);\n if (idx < 0) return;\n switch (e.key) {\n case \"ArrowDown\":\n e.preventDefault();\n if (idx < rows.length - 1) focusRow(rows[idx + 1].item.id);\n break;\n case \"ArrowUp\":\n e.preventDefault();\n if (idx > 0) focusRow(rows[idx - 1].item.id);\n break;\n case \"ArrowRight\":\n if (row.hasChildren) {\n e.preventDefault();\n if (!row.expanded) toggleItem(row.item.id);\n else if (idx < rows.length - 1) focusRow(rows[idx + 1].item.id);\n }\n break;\n case \"ArrowLeft\":\n if (row.hasChildren && row.expanded) {\n e.preventDefault();\n toggleItem(row.item.id);\n } else {\n // Walk back to the parent (first shallower row).\n for (let i = idx - 1; i >= 0; i--) {\n if (rows[i].depth < row.depth) {\n e.preventDefault();\n focusRow(rows[i].item.id);\n break;\n }\n }\n }\n break;\n case \"Home\":\n e.preventDefault();\n if (rows.length) focusRow(rows[0].item.id);\n break;\n case \"End\":\n e.preventDefault();\n if (rows.length) focusRow(rows[rows.length - 1].item.id);\n break;\n case \"Enter\":\n case \" \":\n e.preventDefault();\n if (row.hasChildren) toggleItem(row.item.id);\n else onItemSelect?.(row.item, category);\n break;\n default:\n break;\n }\n };\n\n // Flat draw-rows for the active search results — drives keyboard nav.\n const searchRows = useMemo<FlatRow[]>(\n () =>\n hits\n ? hits.map(({ hit }) => ({\n item: hit.item,\n depth: 0,\n hasChildren: false,\n expanded: false,\n }))\n : [],\n [hits],\n );\n\n // ── Row renderer ────────────────────────────────────────────────────\n const renderRow = (\n row: FlatRow,\n category: FxPanelCategory,\n rows: FlatRow[],\n crumb?: string,\n ) => {\n const { item, depth, hasChildren, expanded } = row;\n const mono = category.kind === \"function\";\n const draggable = !!item.insertValue;\n const isFocused = focusedId === item.id;\n return (\n <div\n key={item.id}\n data-fx-row={item.id}\n className={cn(\n \"ods-flow-fx-panel__row\",\n hasChildren\n ? \"ods-flow-fx-panel__row--branch\"\n : \"ods-flow-fx-panel__row--leaf\",\n draggingId === item.id && \"ods-flow-fx-panel__row--dragging\",\n isFocused && \"ods-flow-fx-panel__row--focused\",\n )}\n role=\"treeitem\"\n aria-expanded={hasChildren ? expanded : undefined}\n aria-level={depth + 1}\n tabIndex={\n isFocused || (focusedId === null && rows[0]?.item.id === item.id)\n ? 0\n : -1\n }\n draggable={draggable}\n onDragStart={draggable ? handleDragStart(item, category) : undefined}\n onDragEnd={() => setDraggingId(null)}\n onFocus={() => setFocusedId(item.id)}\n onKeyDown={(e) => onRowKeyDown(e, rows, row, category)}\n onClick={() => {\n if (hasChildren) toggleItem(item.id);\n else onItemSelect?.(item, category);\n }}\n title={item.insertValue}\n >\n {/* Indent guides — one per ancestor depth. */}\n {Array.from({ length: depth }, (_, i) => (\n <span key={i} className=\"ods-flow-fx-panel__indent\" aria-hidden=\"true\" />\n ))}\n\n {/* Twisty (branches) or drag handle (leaves). */}\n {hasChildren ? (\n <span className=\"ods-flow-fx-panel__twisty\" aria-hidden=\"true\">\n {expanded ? (\n <ChevronDownIcon size=\"sm\" />\n ) : (\n <ChevronRightIcon size=\"sm\" />\n )}\n </span>\n ) : (\n <span className=\"ods-flow-fx-panel__grip\" aria-hidden=\"true\">\n <DraggableIcon size=\"sm\" />\n </span>\n )}\n\n <span className=\"ods-flow-fx-panel__row-main\">\n <span className=\"ods-flow-fx-panel__row-line\">\n {mono ? (\n <code className=\"ods-flow-fx-panel__row-label ods-flow-fx-panel__row-label--mono\">\n {item.label}\n </code>\n ) : (\n <span className=\"ods-flow-fx-panel__row-label\">{item.label}</span>\n )}\n {item.valueType && (\n <span\n className={cn(\n \"ods-flow-fx-panel__type\",\n `ods-flow-fx-panel__type--${item.valueType}`,\n )}\n >\n {item.valueType}\n </span>\n )}\n {item.preview !== undefined && (\n <span className=\"ods-flow-fx-panel__preview\" title={item.preview}>\n {item.preview}\n </span>\n )}\n </span>\n {(item.description || crumb) && (\n <span className=\"ods-flow-fx-panel__row-sub\">\n {crumb && (\n <span className=\"ods-flow-fx-panel__crumb\">{crumb}</span>\n )}\n {item.description}\n </span>\n )}\n </span>\n\n {/* Copy — fades in on hover. */}\n {item.insertValue && (\n <button\n type=\"button\"\n className=\"ods-flow-fx-panel__copy\"\n aria-label={`Copy ${item.label}`}\n onClick={(e) => {\n e.stopPropagation();\n handleCopy(item);\n }}\n >\n {copiedId === item.id ? (\n <CheckmarkIcon size=\"sm\" />\n ) : (\n <CopyIcon size=\"sm\" />\n )}\n </button>\n )}\n </div>\n );\n };\n\n // ── Render ──────────────────────────────────────────────────────────\n return (\n <aside\n className={cn(\"ods-flow-fx-panel\", className)}\n style={{ width, ...style }}\n aria-label={typeof title === \"string\" ? title : \"FX / IO\"}\n >\n <header className=\"ods-flow-fx-panel__header\">\n <h3 className=\"ods-flow-fx-panel__title\">{title}</h3>\n {onClose && (\n <button\n type=\"button\"\n className=\"ods-flow-fx-panel__close\"\n onClick={onClose}\n aria-label=\"Close FX panel\"\n >\n <CloseIcon size=\"sm\" />\n </button>\n )}\n </header>\n\n <div className=\"ods-flow-fx-panel__search\">\n <span className=\"ods-flow-fx-panel__search-icon\" aria-hidden=\"true\">\n <SearchIcon size=\"sm\" />\n </span>\n <input\n type=\"text\"\n className=\"ods-flow-fx-panel__search-input\"\n placeholder={searchPlaceholder}\n aria-label=\"Search fields, functions and variables\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n />\n {search && (\n <button\n type=\"button\"\n className=\"ods-flow-fx-panel__search-clear\"\n onClick={() => setSearch(\"\")}\n aria-label=\"Clear search\"\n >\n <CloseIcon size=\"sm\" />\n </button>\n )}\n </div>\n\n {hint && !query && <p className=\"ods-flow-fx-panel__hint\">{hint}</p>}\n\n <div className=\"ods-flow-fx-panel__list\" ref={listRef} role=\"tree\">\n {/* ── Search mode — flat, path-crumbed leaf matches. ── */}\n {query &&\n hits &&\n (hits.length > 0 ? (\n <div className=\"ods-flow-fx-panel__results\">\n <div className=\"ods-flow-fx-panel__results-count\">\n {hits.length} {hits.length === 1 ? \"match\" : \"matches\"}\n </div>\n {hits.map(({ category, hit }, i) =>\n renderRow(\n searchRows[i],\n category,\n searchRows,\n hit.ancestors.length\n ? hit.ancestors.join(\" / \")\n : category.label,\n ),\n )}\n </div>\n ) : (\n <div className=\"ods-flow-fx-panel__empty\">{emptyLabel}</div>\n ))}\n\n {/* ── Browse mode — category accordion + per-branch tree. ── */}\n {!query &&\n visibleCategories.map((cat) => {\n const isOpen = openCategoryId === cat.id;\n const kind = cat.kind ?? \"function\";\n const branchIds = new Set<string>();\n collectBranchIds(cat.items, branchIds);\n const hasTree = branchIds.size > 0;\n const rows: FlatRow[] = [];\n if (isOpen) flattenTree(cat.items, 0, expandedItems, rows);\n const allExpanded =\n hasTree &&\n [...branchIds].every((id) => expandedItems.has(id));\n\n return (\n <div className=\"ods-flow-fx-panel__category\" key={cat.id}>\n <div\n className={cn(\n \"ods-flow-fx-panel__cat-header\",\n isOpen && \"ods-flow-fx-panel__cat-header--open\",\n )}\n >\n <button\n type=\"button\"\n className=\"ods-flow-fx-panel__cat-toggle\"\n aria-expanded={isOpen}\n onClick={() => setOpenCategory(isOpen ? null : cat.id)}\n >\n <ChevronRightIcon\n size=\"sm\"\n className={cn(\n \"ods-flow-fx-panel__chevron\",\n isOpen && \"ods-flow-fx-panel__chevron--open\",\n )}\n />\n <span\n className={cn(\n \"ods-flow-fx-panel__badge\",\n `ods-flow-fx-panel__badge--${kind}`,\n )}\n >\n <span\n className=\"ods-flow-fx-panel__badge-glyph\"\n aria-hidden=\"true\"\n >\n {KIND_GLYPH[kind]}\n </span>\n {cat.label}\n </span>\n <span className=\"ods-flow-fx-panel__count\">\n {countLeaves(cat.items)}\n </span>\n </button>\n {/* Expand / collapse the whole tree of this category. */}\n {isOpen && hasTree && (\n <button\n type=\"button\"\n className=\"ods-flow-fx-panel__cat-action\"\n aria-label={\n allExpanded ? \"Collapse all\" : \"Expand all\"\n }\n title={allExpanded ? \"Collapse all\" : \"Expand all\"}\n onClick={() => setCategoryExpansion(cat, !allExpanded)}\n >\n {allExpanded ? (\n <CollapseAllIcon size=\"sm\" />\n ) : (\n <ExpandAllIcon size=\"sm\" />\n )}\n </button>\n )}\n </div>\n\n {isOpen && (\n <div\n className=\"ods-flow-fx-panel__rows\"\n role=\"group\"\n aria-label={cat.label}\n >\n {rows.map((row) => renderRow(row, cat, rows))}\n </div>\n )}\n </div>\n );\n })}\n </div>\n </aside>\n );\n}\n","\"use client\";\n\n// ════════════════════════════════════════════════════════════════════════\n// FxToggleButton — the trigger that opens/closes the FxPanel.\n// ════════════════════════════════════════════════════════════════════════\n// A compact toggle that lives in the ConfigPanel header's modular action\n// slot. It carries the `ƒx` glyph + label and reflects the panel's open\n// state through `aria-pressed` + an `--active` visual.\n\nimport { type ButtonHTMLAttributes, type ReactNode } from \"react\";\nimport { FunctionIcon } from \"@octaviaflow/icons\";\nimport { cn } from \"../../../utils/cn\";\n\nexport interface FxToggleButtonProps\n extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, \"children\"> {\n /** Whether the FxPanel is currently open. Drives `aria-pressed`. */\n active?: boolean;\n /** Visible label beside the glyph. Default \"FX\". Pass `null` for icon-only. */\n label?: ReactNode;\n}\n\nexport function FxToggleButton({\n active = false,\n label = \"FX\",\n className,\n type = \"button\",\n ...rest\n}: FxToggleButtonProps) {\n return (\n <button\n {...rest}\n type={type}\n className={cn(\n \"ods-flow-fx-toggle\",\n active && \"ods-flow-fx-toggle--active\",\n label == null && \"ods-flow-fx-toggle--icon-only\",\n className,\n )}\n aria-pressed={active}\n title={active ? \"Hide FX / IO\" : \"Show FX / IO\"}\n >\n <FunctionIcon size=\"sm\" className=\"ods-flow-fx-toggle__icon\" />\n {label != null && (\n <span className=\"ods-flow-fx-toggle__label\">{label}</span>\n )}\n </button>\n );\n}\n","\"use client\";\n\n// ════════════════════════════════════════════════════════════════════════\n// NodeToolbar — floating action bar attached to a node.\n// ════════════════════════════════════════════════════════════════════════\n// Dropped inside a custom node renderer (or anywhere with a\n// `FlowNodeContext`). Visible by default only when the host node is\n// selected; consumers can force it on with `isVisible`. Position-stable\n// across zoom — the bar applies `scale(1 / zoom)` to counter the viewport\n// transform so the buttons stay at constant pixel size.\n//\n// Pointer events on the toolbar are isolated from the node's drag /\n// connection gestures via `data-flow-no-drag=\"true\"` on the wrapper.\n\nimport type { CSSProperties, ReactNode } from \"react\";\nimport { cn } from \"../../../utils/cn\";\nimport type { HandlePosition } from \"../../editor\";\nimport { useViewport } from \"../../store/selectors\";\nimport { useFlowNodeContext } from \"../FlowNode/FlowNodeContext\";\n\nexport interface NodeToolbarProps {\n /** Force-visible state. Defaults to `undefined` → auto (visible iff\n * the host node is selected). Pass `true`/`false` to override. */\n isVisible?: boolean;\n /** Where the toolbar sits relative to the node. Defaults to `top`. */\n position?: HandlePosition;\n /** Pixel offset from the node's edge. Default 10. */\n offset?: number;\n /** Horizontal/vertical alignment along the chosen edge. Default center. */\n align?: \"start\" | \"center\" | \"end\";\n /** Toolbar contents (typically a row of `<button>` elements). */\n children: ReactNode;\n className?: string;\n style?: CSSProperties;\n}\n\nexport function NodeToolbar({\n isVisible,\n position = \"top\",\n offset = 10,\n align = \"center\",\n children,\n className,\n style,\n}: NodeToolbarProps) {\n const node = useFlowNodeContext();\n const viewport = useViewport();\n const show = isVisible ?? node.selected;\n if (!show) return null;\n\n // Counter the canvas's `scale(zoom)` so the toolbar reads at constant\n // pixel size at any zoom level. Translation is in flow-space units; the\n // inverse-scale wraps the visible content so children also stay crisp.\n const inverseScale = 1 / viewport.zoom;\n\n return (\n <div\n className={cn(\"ods-node-toolbar\", `ods-node-toolbar--${position}`, className)}\n data-flow-no-drag=\"true\"\n style={{\n ...positionStyle(position, offset, align),\n ...style,\n }}\n onPointerDown={(e) => e.stopPropagation()}\n onMouseDown={(e) => e.stopPropagation()}\n onClick={(e) => e.stopPropagation()}\n >\n {/* Inner element absorbs the inverse zoom so the toolbar's\n children stay screen-stable. */}\n <div\n className=\"ods-node-toolbar__inner\"\n style={{\n transform: `scale(${inverseScale})`,\n transformOrigin: transformOriginFor(position, align),\n }}\n >\n {children}\n </div>\n </div>\n );\n}\n\nfunction positionStyle(\n side: HandlePosition,\n offset: number,\n align: NodeToolbarProps[\"align\"],\n): CSSProperties {\n const center = align === \"center\";\n const start = align === \"start\";\n switch (side) {\n case \"top\":\n return {\n position: \"absolute\",\n bottom: \"100%\",\n marginBottom: offset,\n left: start ? 0 : center ? \"50%\" : undefined,\n right: align === \"end\" ? 0 : undefined,\n transform: center ? \"translateX(-50%)\" : undefined,\n };\n case \"bottom\":\n return {\n position: \"absolute\",\n top: \"100%\",\n marginTop: offset,\n left: start ? 0 : center ? \"50%\" : undefined,\n right: align === \"end\" ? 0 : undefined,\n transform: center ? \"translateX(-50%)\" : undefined,\n };\n case \"left\":\n return {\n position: \"absolute\",\n right: \"100%\",\n marginRight: offset,\n top: start ? 0 : center ? \"50%\" : undefined,\n bottom: align === \"end\" ? 0 : undefined,\n transform: center ? \"translateY(-50%)\" : undefined,\n };\n case \"right\":\n return {\n position: \"absolute\",\n left: \"100%\",\n marginLeft: offset,\n top: start ? 0 : center ? \"50%\" : undefined,\n bottom: align === \"end\" ? 0 : undefined,\n transform: center ? \"translateY(-50%)\" : undefined,\n };\n }\n}\n\nfunction transformOriginFor(side: HandlePosition, align: NodeToolbarProps[\"align\"]): string {\n // Choose an origin that visually attaches the bar to the node edge,\n // even after inverse-scaling. Defaults match the `position*` styles.\n if (side === \"top\")\n return align === \"start\" ? \"left bottom\" : align === \"end\" ? \"right bottom\" : \"center bottom\";\n if (side === \"bottom\")\n return align === \"start\" ? \"left top\" : align === \"end\" ? \"right top\" : \"center top\";\n if (side === \"left\")\n return align === \"start\" ? \"right top\" : align === \"end\" ? \"right bottom\" : \"right center\";\n return align === \"start\" ? \"left top\" : align === \"end\" ? \"left bottom\" : \"left center\";\n}\n","// ════════════════════════════════════════════════════════════════════════\n// Collapse helpers — when a group's `data.collapsed === true`, hide its\n// descendants AND re-route any edges that crossed the collapse boundary\n// so they terminate at the group instead of dangling at a hidden child.\n// ════════════════════════════════════════════════════════════════════════\n//\n// Consumers feed `nodes` + `edges` (their canonical state) through\n// `collapseFor()` and pass the result to <FlowCanvas>. The helpers are\n// pure — no React state, no DOM. They run in O(N + E) so they're cheap\n// to call on every render.\n\nimport type { WorkflowEdge, WorkflowNode } from \"../editor\";\n\ninterface CollapseResult {\n nodes: WorkflowNode[];\n edges: WorkflowEdge[];\n /** Per-collapsed-group: how many descendants got hidden. The host UI\n * reads this to render a \"N steps\" badge. */\n hiddenCountByGroupId: Map<string, number>;\n}\n\n/**\n * Given the canonical graph, return a derived graph that respects the\n * `collapsed` flag on every group node:\n *\n * - Descendants of a collapsed group are marked `hidden`.\n * - Edges that crossed the collapse boundary are rewritten: their\n * source (or target) becomes the collapsed group's id, and the\n * handle is reset to a synthetic perimeter handle (`__group_out` /\n * `__group_in`). Internal-to-internal edges are dropped.\n */\nexport function collapseFor(nodes: WorkflowNode[], edges: WorkflowEdge[]): CollapseResult {\n // Map child → topmost collapsed ancestor (if any).\n // If a node has multiple collapsed ancestors, the OUTERMOST wins so the\n // edge terminates at the visible boundary.\n const collapsedAncestorOf = new Map<string, string>();\n const parents = new Map<string, string>();\n for (const n of nodes) if (n.parentId) parents.set(n.id, n.parentId);\n\n // ANY node with `data.collapsed === true` collapses its descendants —\n // not just `group`. This lets `forEach` (and consumer-defined container\n // kinds) opt into the same collapse model without each one being\n // hard-coded in this helper.\n const isCollapsed = (node: WorkflowNode | undefined): boolean => {\n const d = node?.data as { collapsed?: boolean } | undefined;\n return !!d?.collapsed;\n };\n\n for (const n of nodes) {\n let cursor: string | undefined = n.id;\n let outermostCollapsed: string | undefined;\n const seen = new Set<string>([cursor]);\n while (cursor) {\n const p = parents.get(cursor);\n if (!p || seen.has(p)) break;\n seen.add(p);\n const parentNode = nodes.find((x) => x.id === p);\n if (isCollapsed(parentNode)) outermostCollapsed = p;\n cursor = p;\n }\n if (outermostCollapsed && outermostCollapsed !== n.id) {\n collapsedAncestorOf.set(n.id, outermostCollapsed);\n }\n }\n\n // Hide every descendant of a collapsed group.\n const outNodes: WorkflowNode[] = nodes.map((n) => {\n const anc = collapsedAncestorOf.get(n.id);\n if (anc) return { ...n, hidden: true };\n return n;\n });\n\n // Rewrite edges: any endpoint that sits inside a collapsed group gets\n // hoisted to the group itself. Edges entirely within a collapsed group\n // are dropped. Edges that start at the group and end inside it (or\n // vice versa) become self-loops that we also drop.\n //\n // **Boundary marker** — edges that get rewritten to terminate at a\n // collapsed group's perimeter are stroked with a sparse dot pattern so\n // the user immediately sees they're a \"summary\" of one or more hidden\n // inner connections rather than a real handle-to-handle edge. The\n // original `edge.style` is preserved; only `strokeDasharray` is\n // overlaid. FlowEdge gives `edge.style.strokeDasharray` precedence\n // over the type's default dash.\n const BOUNDARY_DASH = \"2 4\";\n const outEdges: WorkflowEdge[] = [];\n const hiddenCount = new Map<string, number>();\n for (const e of edges) {\n const srcAnc = collapsedAncestorOf.get(e.source);\n const tgtAnc = collapsedAncestorOf.get(e.target);\n if (srcAnc && tgtAnc && srcAnc === tgtAnc) continue; // wholly inside one collapsed group — drop\n let src = e.source;\n let srcHandle = e.sourceHandle;\n let tgt = e.target;\n let tgtHandle = e.targetHandle;\n const rewritten = !!(srcAnc || tgtAnc);\n if (srcAnc) {\n src = srcAnc;\n // Synthetic perimeter handle — the GroupNode renderer registers\n // matching `__group_out` / `__group_in` handles when collapsed.\n srcHandle = \"__group_out\";\n }\n if (tgtAnc) {\n tgt = tgtAnc;\n tgtHandle = \"__group_in\";\n }\n if (src === tgt) continue; // collapsed self-loop\n const nextEdge: WorkflowEdge = {\n ...e,\n source: src,\n sourceHandle: srcHandle,\n target: tgt,\n targetHandle: tgtHandle,\n };\n if (rewritten) {\n nextEdge.style = { ...(e.style ?? {}), strokeDasharray: BOUNDARY_DASH };\n }\n outEdges.push(nextEdge);\n }\n\n // Count hidden descendants per collapsed group for the badge.\n for (const [_id, anc] of collapsedAncestorOf) {\n hiddenCount.set(anc, (hiddenCount.get(anc) ?? 0) + 1);\n }\n\n return { nodes: outNodes, edges: outEdges, hiddenCountByGroupId: hiddenCount };\n}\n\n/**\n * Toggle the `data.collapsed` flag on a group node. Returns the patched\n * nodes array (immutable). Useful in event handlers.\n */\nexport function toggleGroupCollapse(groupId: string, nodes: WorkflowNode[]): WorkflowNode[] {\n return nodes.map((n) => {\n if (n.id !== groupId) return n;\n const data = (n.data ?? {}) as { collapsed?: boolean } & Record<string, unknown>;\n return { ...n, data: { ...data, collapsed: !data.collapsed } };\n });\n}\n"],"mappings":"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","names":["useCallback","useRef","useState","useState","useRef","useCallback","useCallback","useEffect","useRef","useState","useState","useRef","useEffect","useCallback","useEffect","useMemo","useRef","useState","Fragment","jsx","jsxs","useState","useMemo","useRef","useEffect","jsx","jsxs","jsx"]}
|
|
1
|
+
{"version":3,"sources":["../src/workflow/hooks/useAutoLayout.ts","../src/workflow/hooks/useFlowState.ts","../src/workflow/layout/dagre.ts","../src/workflow/layout/elk.ts","../src/workflow/components/ConfigPanel/ConfigPanel.tsx","../src/workflow/components/FxPanel/FxPanel.tsx","../src/workflow/components/FxPanel/DataRefButton.tsx","../src/workflow/components/NodeToolbar/NodeToolbar.tsx","../src/workflow/utils/collapse.ts"],"sourcesContent":["// ════════════════════════════════════════════════════════════════════════\n// useAutoLayout — run a layout engine and apply its output via the\n// canvas's `onNodesChange` callback.\n// ════════════════════════════════════════════════════════════════════════\n// Two trigger modes:\n//\n// 1. Imperative — call the returned `runLayout()` function (e.g. from a\n// toolbar button). This is the recommended default; users press\n// \"Auto-arrange\" and the engine runs once.\n//\n// 2. Reactive — pass `auto: true`. The hook debounces 250ms on every\n// node/edge add/remove and runs the engine. Useful for \"always\n// auto-layout\" surfaces; expensive in graphs where users drag nodes\n// around (their positions get clobbered).\n//\n// The hook does NOT subscribe to selection / drag / viewport — it watches\n// only the *shape* of the graph (node ids + edge connections). That\n// keeps reactive mode from re-running on every pointermove.\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport type {\n LayoutEngine,\n LayoutOptions,\n NodeChange,\n WorkflowEdge,\n WorkflowNode,\n} from \"../editor\";\nimport { change } from \"../store/changes\";\n\nexport interface UseAutoLayoutOptions {\n /** The engine. Required. */\n engine: LayoutEngine | null | undefined;\n /** Current nodes (controlled). */\n nodes: WorkflowNode[];\n /** Current edges (controlled). */\n edges: WorkflowEdge[];\n /** Where to send position changes. Required. */\n onNodesChange?: (changes: NodeChange[]) => void;\n /** Layout options forwarded to the engine. */\n options?: LayoutOptions;\n /** Re-run on every graph-shape change (debounced 250ms). Default false. */\n auto?: boolean;\n /** Debounce delay for `auto` mode (ms). Default 250. */\n debounceMs?: number;\n /** Called after each successful run. */\n onLayout?: (positions: { id: string; position: { x: number; y: number } }[]) => void;\n /** Called when the engine throws (network / worker / library). */\n onError?: (err: unknown) => void;\n}\n\nexport interface UseAutoLayoutResult {\n /** Manually trigger a layout pass. Returns the promise of the engine. */\n runLayout(): Promise<void>;\n /** True while a layout pass is in flight. */\n isLaying: boolean;\n}\n\n/**\n * Build a stable graph-shape signature so reactive mode only re-runs when\n * the structure (node ids + edges) changes, not on position/select/drag.\n */\nfunction signature(nodes: WorkflowNode[], edges: WorkflowEdge[]): string {\n // Sorted to be invariant under array re-ordering.\n const nodeKeys = nodes\n .map((n) => `${n.id}:${n.parentId ?? \"\"}`)\n .sort()\n .join(\"|\");\n const edgeKeys = edges\n .map((e) => `${e.id}:${e.source}>${e.target}`)\n .sort()\n .join(\"|\");\n return `${nodeKeys}::${edgeKeys}`;\n}\n\nexport function useAutoLayout({\n engine,\n nodes,\n edges,\n onNodesChange,\n options,\n auto = false,\n debounceMs = 250,\n onLayout,\n onError,\n}: UseAutoLayoutOptions): UseAutoLayoutResult {\n const [isLaying, setIsLaying] = useState(false);\n const runningRef = useRef(false);\n const optionsRef = useRef(options);\n const onLayoutRef = useRef(onLayout);\n const onErrorRef = useRef(onError);\n const onNodesChangeRef = useRef(onNodesChange);\n useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n useEffect(() => {\n onLayoutRef.current = onLayout;\n }, [onLayout]);\n useEffect(() => {\n onErrorRef.current = onError;\n }, [onError]);\n useEffect(() => {\n onNodesChangeRef.current = onNodesChange;\n }, [onNodesChange]);\n\n // Snapshot refs of nodes/edges so the runLayout callback can read fresh\n // data without re-creating itself on every render.\n const nodesRef = useRef(nodes);\n const edgesRef = useRef(edges);\n useEffect(() => {\n nodesRef.current = nodes;\n }, [nodes]);\n useEffect(() => {\n edgesRef.current = edges;\n }, [edges]);\n\n const runLayout = useCallback(async () => {\n if (!engine) return;\n if (runningRef.current) return;\n runningRef.current = true;\n setIsLaying(true);\n try {\n const positions = await engine.layout(nodesRef.current, edgesRef.current, optionsRef.current);\n\n // Index engine output by id + remember per-node deltas so we can\n // transitively apply them to descendants (children move with the\n // parent — otherwise a parent moves and its children get stranded\n // outside the new bbox, which is the v1 subflow auto-layout bug).\n const positionedIds = new Set(positions.map((p) => p.id));\n const deltas = new Map<string, { dx: number; dy: number }>();\n\n const cs: NodeChange[] = [];\n for (const p of positions) {\n const cur = nodesRef.current.find((n) => n.id === p.id);\n if (!cur) continue;\n if (cur.position.x === p.position.x && cur.position.y === p.position.y) continue;\n cs.push(change.node.position(p.id, p.position, false));\n deltas.set(p.id, {\n dx: p.position.x - cur.position.x,\n dy: p.position.y - cur.position.y,\n });\n }\n\n // For every node the engine did NOT reposition (typically because\n // it sat in a sub-flow that the engine treated as opaque), walk up\n // the parentId chain to find the nearest moved ancestor. Shift the\n // descendant by that ancestor's delta. The first moved ancestor\n // wins — we don't sum deltas because each child's position is\n // already relative to its ancestor's old position.\n const findParent = (id: string): WorkflowNode | undefined =>\n nodesRef.current.find((x) => x.id === id);\n\n for (const n of nodesRef.current) {\n if (positionedIds.has(n.id)) continue;\n if (!n.parentId) continue;\n let cursor: string | undefined = n.parentId;\n const seen = new Set<string>();\n let delta: { dx: number; dy: number } | undefined;\n while (cursor) {\n if (seen.has(cursor)) break; // cycle guard\n seen.add(cursor);\n const found = deltas.get(cursor);\n if (found) {\n delta = found;\n break;\n }\n cursor = findParent(cursor)?.parentId;\n }\n if (!delta) continue;\n cs.push(\n change.node.position(\n n.id,\n { x: n.position.x + delta.dx, y: n.position.y + delta.dy },\n false,\n ),\n );\n }\n\n if (cs.length) onNodesChangeRef.current?.(cs);\n onLayoutRef.current?.(positions);\n } catch (err) {\n onErrorRef.current?.(err);\n } finally {\n runningRef.current = false;\n setIsLaying(false);\n }\n }, [engine]);\n\n // Reactive auto-mode — debounce on graph-shape changes.\n const sig = signature(nodes, edges);\n const sigRef = useRef(sig);\n useEffect(() => {\n if (!auto) return;\n if (sigRef.current === sig) return; // first render after toggling auto\n sigRef.current = sig;\n const timer = setTimeout(() => void runLayout(), debounceMs);\n return () => clearTimeout(timer);\n }, [sig, auto, debounceMs, runLayout]);\n\n return { runLayout, isLaying };\n}\n","// ════════════════════════════════════════════════════════════════════════\n// useFlowState — the uncontrolled-mode convenience.\n// ════════════════════════════════════════════════════════════════════════\n// Consumers that don't want to thread state through their own reducer\n// can use this hook and spread the result into <FlowCanvas>:\n//\n// const flow = useFlowState({ initialNodes, initialEdges });\n// <FlowCanvas {...flow} />\n//\n// In addition to the canonical change-stream contract, `useFlowState`\n// exposes per-node helpers — `updateNodeData(id, patch)` and the\n// companion `useNodeData(state, id)` selector — so individual node\n// updates can be expressed without re-mapping the entire nodes array\n// in caller code. Each helper writes ONLY the targeted node's slice;\n// React.memo'd kind components for other nodes never re-render.\n\nimport { useCallback, useMemo, useRef, useState } from \"react\";\nimport type { EdgeChange, NodeChange, WorkflowEdge, WorkflowNode } from \"../editor\";\nimport { applyEdgeChanges, applyNodeChanges } from \"../store/changes\";\n\nexport interface UseFlowStateOptions<TNData = unknown, TEData = unknown> {\n initialNodes?: WorkflowNode<TNData>[];\n initialEdges?: WorkflowEdge<TEData>[];\n}\n\n/**\n * Patch type for `updateNodeData` — accepts a partial object that gets\n * shallow-merged, or a producer function `(prev) => next`.\n */\nexport type NodeDataPatch<TNData> =\n | Partial<TNData>\n | ((prev: TNData | undefined) => TNData | undefined);\n\nexport interface FlowStateBag<TNData = unknown, TEData = unknown> {\n nodes: WorkflowNode<TNData>[];\n edges: WorkflowEdge<TEData>[];\n onNodesChange: (changes: NodeChange<TNData>[]) => void;\n onEdgesChange: (changes: EdgeChange<TEData>[]) => void;\n setNodes: (\n update: WorkflowNode<TNData>[] | ((prev: WorkflowNode<TNData>[]) => WorkflowNode<TNData>[]),\n ) => void;\n setEdges: (\n update: WorkflowEdge<TEData>[] | ((prev: WorkflowEdge<TEData>[]) => WorkflowEdge<TEData>[]),\n ) => void;\n /**\n * Update the `data` slice of a single node by id. Only that node's\n * reference changes — every other node keeps its previous reference,\n * so React.memo'd kind components for unrelated nodes do not re-render.\n * No-ops when `id` is not present in the current nodes array.\n */\n updateNodeData: (id: string, patch: NodeDataPatch<TNData>) => void;\n /**\n * Read a single node by id from the latest snapshot. Returns undefined\n * if the id is unknown. Useful inside imperative callbacks that need\n * the freshest value without subscribing to re-renders.\n */\n getNode: (id: string) => WorkflowNode<TNData> | undefined;\n}\n\nexport function useFlowState<TNData = unknown, TEData = unknown>(\n options: UseFlowStateOptions<TNData, TEData> = {},\n): FlowStateBag<TNData, TEData> {\n const [nodes, setNodes] = useState<WorkflowNode<TNData>[]>(options.initialNodes ?? []);\n const [edges, setEdges] = useState<WorkflowEdge<TEData>[]>(options.initialEdges ?? []);\n\n // Ref-mirror of the latest nodes array. `getNode` reads through it so\n // imperative callers see fresh values without depending on render scope.\n const nodesRef = useRef(nodes);\n nodesRef.current = nodes;\n\n const onNodesChange = useCallback((changes: NodeChange<TNData>[]) => {\n setNodes((prev) => applyNodeChanges(prev, changes));\n }, []);\n\n const onEdgesChange = useCallback((changes: EdgeChange<TEData>[]) => {\n setEdges((prev) => applyEdgeChanges(prev, changes));\n }, []);\n\n const updateNodeData = useCallback((id: string, patch: NodeDataPatch<TNData>) => {\n setNodes((prev) => {\n let touched = false;\n const next: WorkflowNode<TNData>[] = prev.map((n) => {\n if (n.id !== id) return n;\n const nextData =\n typeof patch === \"function\"\n ? (patch as (p: TNData | undefined) => TNData | undefined)(n.data)\n : ({ ...(n.data ?? ({} as TNData)), ...patch } as TNData);\n if (Object.is(nextData, n.data)) return n;\n touched = true;\n return { ...n, data: nextData as TNData };\n });\n return touched ? next : prev;\n });\n }, []);\n\n const getNode = useCallback((id: string) => nodesRef.current.find((n) => n.id === id), []);\n\n return useMemo(\n () => ({\n nodes,\n edges,\n onNodesChange,\n onEdgesChange,\n setNodes,\n setEdges,\n updateNodeData,\n getNode,\n }),\n [nodes, edges, onNodesChange, onEdgesChange, updateNodeData, getNode],\n );\n}\n","// ════════════════════════════════════════════════════════════════════════\n// Dagre adapter — wraps a consumer-supplied `dagre` module.\n// ════════════════════════════════════════════════════════════════════════\n// Usage:\n//\n// import dagre from \"dagre\";\n// import { createDagreEngine } from \"@octaviaflow/core/workflow\";\n//\n// const engine = createDagreEngine(dagre);\n// useAutoLayout({ engine, onLayout });\n//\n// Dagre is synchronous — `layout()` blocks the main thread. Good for\n// small graphs (≤ ~200 nodes). For larger graphs, see `createElkEngine`.\n\nimport type { LayoutEngine, LayoutOptions, Position, WorkflowEdge, WorkflowNode } from \"../editor\";\nimport { DEFAULT_NODE_HEIGHT, DEFAULT_NODE_WIDTH } from \"../utils/geometry\";\nimport type { DagreLike } from \"./types\";\n\nexport interface CreateDagreEngineOptions {\n /** Default spacing between nodes within a rank (default 60). */\n nodeSpacing?: number;\n /** Default spacing between ranks (default 80). */\n rankSpacing?: number;\n /** Default direction — TB, BT, LR, RL. Defaults to TB. */\n direction?: LayoutOptions[\"direction\"];\n}\n\n/**\n * Build a `LayoutEngine` backed by a consumer-supplied `dagre` module.\n *\n * `dagre` can be passed as either the default export or the namespace\n * import (`import * as dagre from \"dagre\"`). The adapter probes both\n * shapes so consumers don't have to think about it.\n */\nexport function createDagreEngine(\n dagre:\n | DagreLike\n | { default: DagreLike }\n | { graphlib: DagreLike[\"graphlib\"]; layout: DagreLike[\"layout\"] },\n defaults: CreateDagreEngineOptions = {},\n): LayoutEngine {\n const lib = resolveDagre(dagre);\n const defaultDir = defaults.direction ?? \"TB\";\n const defaultNodeSpacing = defaults.nodeSpacing ?? 60;\n const defaultRankSpacing = defaults.rankSpacing ?? 80;\n\n return {\n name: \"dagre\",\n async layout(nodes: WorkflowNode[], edges: WorkflowEdge[], options?: LayoutOptions) {\n const direction = options?.direction ?? defaultDir;\n const nodesep = options?.nodeSpacing ?? defaultNodeSpacing;\n const ranksep = options?.rankSpacing ?? defaultRankSpacing;\n\n // Dagre requires every node to have width/height. Use the measured\n // values from the canvas; fall back to our default footprint.\n const g = new lib.graphlib.Graph({ multigraph: true, compound: true });\n g.setGraph({\n rankdir: direction,\n nodesep,\n ranksep,\n ...(options?.engine ?? {}),\n });\n g.setDefaultEdgeLabel(() => ({}));\n\n for (const n of nodes) {\n if (n.hidden) continue;\n g.setNode(n.id, {\n width: n.width ?? DEFAULT_NODE_WIDTH,\n height: n.height ?? DEFAULT_NODE_HEIGHT,\n ...(n.parentId ? { parent: n.parentId } : {}),\n });\n // Compound graph: register the parent–child relationship.\n if (n.parentId) {\n // `setParent` is on the graph but not strongly typed in our\n // narrow surface — call via index access. dagre tolerates a\n // missing parent node (it auto-creates).\n (g as unknown as { setParent: (id: string, parent: string) => void }).setParent(\n n.id,\n n.parentId,\n );\n }\n }\n\n for (const e of edges) {\n if (e.hidden) continue;\n g.setEdge(e.source, e.target, { id: e.id });\n }\n\n lib.layout(g);\n\n // Dagre stores node centres; our model uses top-left. Translate.\n const result: { id: string; position: Position }[] = [];\n for (const id of g.nodes()) {\n const out = g.node(id);\n if (!out) continue;\n result.push({\n id,\n position: { x: out.x - out.width / 2, y: out.y - out.height / 2 },\n });\n }\n return result;\n },\n };\n}\n\nfunction resolveDagre(input: unknown): DagreLike {\n const asAny = input as Partial<DagreLike> & { default?: DagreLike };\n if (asAny && typeof asAny.layout === \"function\" && asAny.graphlib?.Graph) {\n return asAny as DagreLike;\n }\n if (asAny?.default && typeof asAny.default.layout === \"function\") {\n return asAny.default;\n }\n throw new Error(\n \"[@octaviaflow/core/workflow] createDagreEngine: input does not look like the dagre module. \" +\n \"Pass `dagre` (the default export) or `import * as dagre from 'dagre'`.\",\n );\n}\n","// ════════════════════════════════════════════════════════════════════════\n// ELK adapter — wraps a consumer-supplied `elkjs` instance.\n// ════════════════════════════════════════════════════════════════════════\n// Usage (Web Worker — recommended for large graphs):\n//\n// import ELK from \"elkjs/lib/elk-api\";\n// import { createElkEngine } from \"@octaviaflow/core/workflow\";\n//\n// const elk = new ELK({ workerUrl: \"/elk-worker.min.js\" });\n// const engine = createElkEngine(elk);\n//\n// Usage (in-process, no worker):\n//\n// import ELK from \"elkjs/lib/elk.bundled\";\n// const elk = new ELK();\n// const engine = createElkEngine(elk);\n//\n// ELK supports compound graphs natively — nodes with `parentId` get\n// nested into the right `children[]` so subflow layouts just work.\n\nimport type { LayoutEngine, LayoutOptions, Position, WorkflowEdge, WorkflowNode } from \"../editor\";\nimport { DEFAULT_NODE_HEIGHT, DEFAULT_NODE_WIDTH } from \"../utils/geometry\";\nimport type { ElkEdge, ElkGraph, ElkLike } from \"./types\";\n\nexport interface CreateElkEngineOptions {\n /** Default direction — TB / BT / LR / RL. Defaults to TB. */\n direction?: LayoutOptions[\"direction\"];\n /** Default node-to-node spacing within a layer (default 60). */\n nodeSpacing?: number;\n /** Default layer-to-layer spacing (default 80). */\n rankSpacing?: number;\n /** Override the algorithm. Defaults to `layered` (Sugiyama). */\n algorithm?:\n | \"layered\"\n | \"mrtree\"\n | \"force\"\n | \"stress\"\n | \"radial\"\n | \"disco\"\n | \"rectpacking\"\n | \"box\";\n}\n\nconst ELK_DIRECTION: Record<NonNullable<LayoutOptions[\"direction\"]>, string> = {\n TB: \"DOWN\",\n BT: \"UP\",\n LR: \"RIGHT\",\n RL: \"LEFT\",\n};\n\n/**\n * Build a `LayoutEngine` backed by a consumer-supplied `elkjs` instance.\n *\n * The consumer is responsible for constructing the ELK instance (which\n * may or may not run in a Web Worker — that's a `workerUrl` constructor\n * option on their end). Our adapter just calls `elk.layout(graph)` and\n * patches positions back.\n */\nexport function createElkEngine(elk: ElkLike, defaults: CreateElkEngineOptions = {}): LayoutEngine {\n const algorithm = defaults.algorithm ?? \"layered\";\n const defaultDir = defaults.direction ?? \"TB\";\n const defaultNodeSpacing = defaults.nodeSpacing ?? 60;\n const defaultRankSpacing = defaults.rankSpacing ?? 80;\n\n return {\n name: \"elk\",\n async layout(nodes: WorkflowNode[], edges: WorkflowEdge[], options?: LayoutOptions) {\n const direction = options?.direction ?? defaultDir;\n const nodeSpacing = options?.nodeSpacing ?? defaultNodeSpacing;\n const rankSpacing = options?.rankSpacing ?? defaultRankSpacing;\n\n // Build a nested ELK graph respecting `parentId` so compound layout\n // works. We walk the node list once into a parent → children map,\n // then recursively build ElkGraph.children[].\n const childrenByParent = new Map<string | undefined, WorkflowNode[]>();\n for (const n of nodes) {\n if (n.hidden) continue;\n const key = n.parentId ?? undefined;\n const list = childrenByParent.get(key);\n if (list) list.push(n);\n else childrenByParent.set(key, [n]);\n }\n\n const buildChildren = (parentId: string | undefined): ElkGraph[] => {\n const list = childrenByParent.get(parentId) ?? [];\n return list.map<ElkGraph>((n) => ({\n id: n.id,\n width: n.width ?? DEFAULT_NODE_WIDTH,\n height: n.height ?? DEFAULT_NODE_HEIGHT,\n children: childrenByParent.has(n.id) ? buildChildren(n.id) : undefined,\n layoutOptions: childrenByParent.has(n.id)\n ? {\n \"elk.algorithm\": algorithm,\n \"elk.direction\": ELK_DIRECTION[direction],\n \"elk.spacing.nodeNode\": String(nodeSpacing),\n \"elk.layered.spacing.nodeNodeBetweenLayers\": String(rankSpacing),\n }\n : undefined,\n }));\n };\n\n const elkEdges: ElkEdge[] = edges\n .filter((e) => !e.hidden)\n .map((e) => ({\n id: e.id,\n sources: [e.source],\n targets: [e.target],\n }));\n\n const graph: ElkGraph = {\n id: \"root\",\n layoutOptions: {\n \"elk.algorithm\": algorithm,\n \"elk.direction\": ELK_DIRECTION[direction],\n \"elk.spacing.nodeNode\": String(nodeSpacing),\n \"elk.layered.spacing.nodeNodeBetweenLayers\": String(rankSpacing),\n ...Object.fromEntries(\n Object.entries(options?.engine ?? {}).map(([k, v]) => [k, String(v)]),\n ),\n },\n children: buildChildren(undefined),\n edges: elkEdges,\n };\n\n const laidOut = await elk.layout(graph);\n\n // Walk the resulting graph and collect (id, position). ELK returns\n // positions relative to the parent — we walk parents to flatten\n // back to absolute flow-space coordinates (matches our ADR-003\n // \"positions are always absolute\" rule).\n const result: { id: string; position: Position }[] = [];\n const walk = (g: ElkGraph, ox: number, oy: number) => {\n const ax = ox + (g.x ?? 0);\n const ay = oy + (g.y ?? 0);\n if (g.id !== \"root\") {\n result.push({ id: g.id, position: { x: ax, y: ay } });\n }\n if (g.children) {\n for (const c of g.children) walk(c, ax, ay);\n }\n };\n walk(laidOut, 0, 0);\n return result;\n },\n };\n}\n","\"use client\";\n\n// ════════════════════════════════════════════════════════════════════════\n// ConfigPanel v2 — right-side editor for the selected node.\n// ════════════════════════════════════════════════════════════════════════\n// Improvements over v1:\n// - Tabbed body (config / input / output schema; consumer can override)\n// - Resizable width (drag the left edge)\n// - Pinned vs floating modes\n// - Breadcrumb header showing node kind → title\n// - Footer with save-state pill (dirty / saving / saved)\n// - Slot for inline status banner (validation errors, etc.)\n// - Inline-edit-the-title affordance (double-click)\n// - Sticky footer with save / cancel buttons\n\nimport {\n type CSSProperties,\n type PointerEvent,\n type KeyboardEvent as ReactKeyboardEvent,\n type ReactNode,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { cn } from \"../../../utils/cn\";\n\nexport type ConfigPanelSaveState = \"clean\" | \"dirty\" | \"saving\" | \"saved\" | \"error\";\n\nexport interface ConfigPanelTab {\n id: string;\n label: ReactNode;\n /** Optional badge (e.g. count of errors). */\n badge?: ReactNode;\n /** When true, the tab is rendered but disabled. */\n disabled?: boolean;\n content: ReactNode;\n}\n\nexport interface ConfigPanelProps {\n open?: boolean;\n /** Title shown in the header — the action name. Double-click opens\n * the inline editor. The header shows this single line only. */\n title?: string;\n /** Optional icon rendered before the title. */\n icon?: ReactNode;\n /**\n * Modular header action slot — buttons rendered between the title and\n * the close (✕). Use it for per-action affordances like the FX\n * toggle; omit entirely for actions that don't need them.\n */\n headerActions?: ReactNode;\n /** Inline status banner — error / warning / hint, shown at the top of the body. */\n banner?: ReactNode;\n /** Tab definitions. When omitted, `children` renders directly (no tabs). */\n tabs?: ConfigPanelTab[];\n /** Initial active tab. Controlled if `onTabChange` is provided. */\n activeTab?: string;\n defaultActiveTab?: string;\n onTabChange?: (id: string) => void;\n /** Save-state pill in the footer. Omit it entirely to hide the pill —\n * the footer then shows only its action buttons. */\n saveState?: ConfigPanelSaveState;\n /** Fires on the title editor's commit. Omit to disable title editing. */\n onTitleChange?: (next: string) => void;\n onClose?: () => void;\n onSave?: () => void;\n onCancel?: () => void;\n /**\n * Padding (px, all sides) applied to the scrollable body — the work\n * area that holds the active tab's content. Default 12. Pass `0` when\n * the consumer's tab content already pads itself.\n */\n padding?: number;\n /** Resizable width — pass `resizable: false` to lock. */\n resizable?: boolean;\n /** Initial width (px). Defaults to 400. */\n defaultWidth?: number;\n /** Lower clamp during resize. Defaults to 400 — the panel can be\n * widened but never narrowed past its default. */\n minWidth?: number;\n /**\n * Upper clamp during resize. Defaults to 720. The panel ALSO clamps\n * to its parent container's measured width minus\n * `maxWidthContainerInset` (default 80 px) so the panel can never\n * cover the whole canvas — whichever is smaller wins.\n */\n maxWidth?: number;\n /**\n * How much of the parent container to leave visible at the panel's\n * widest. Subtracted from the parent's clientWidth before clamping\n * `maxWidth`. Defaults to 80 px.\n */\n maxWidthContainerInset?: number;\n /** \"pinned\" sits flush against the canvas; \"floating\" raises with shadow. */\n variant?: \"pinned\" | \"floating\";\n /** Body slot used only when `tabs` is omitted. */\n children?: ReactNode;\n /**\n * Modular footer action buttons — replaces the built-in Cancel/Save\n * pair. The footer bar itself stays static and pinned; this slot just\n * decides which buttons live in it, so a consumer can add, remove, or\n * reorder actions freely.\n */\n footerActions?: ReactNode;\n className?: string;\n style?: CSSProperties;\n}\n\nconst SAVE_STATE_LABEL: Record<ConfigPanelSaveState, string> = {\n clean: \"Saved\",\n dirty: \"Unsaved changes\",\n saving: \"Saving…\",\n saved: \"Saved\",\n error: \"Save failed\",\n};\n\nconst SAVE_STATE_DOT: Record<ConfigPanelSaveState, string> = {\n clean: \"var(--ods-text-tertiary)\",\n dirty: \"var(--ods-status-warning, #d97706)\",\n saving: \"var(--ods-status-running, #2563eb)\",\n saved: \"var(--ods-status-success, #16a34a)\",\n error: \"var(--ods-status-failed, #dc2626)\",\n};\n\nexport function ConfigPanel({\n open = true,\n title,\n icon,\n headerActions,\n banner,\n tabs,\n activeTab: controlledActive,\n defaultActiveTab,\n onTabChange,\n saveState,\n onTitleChange,\n onClose,\n onSave,\n onCancel,\n padding = 12,\n resizable = true,\n defaultWidth = 400,\n minWidth = 400,\n maxWidth = 720,\n maxWidthContainerInset = 80,\n variant = \"pinned\",\n children,\n footerActions,\n className,\n style,\n}: ConfigPanelProps) {\n const [width, setWidth] = useState(defaultWidth);\n // Effective upper bound — `min(maxWidth, parentClientWidth - inset)`.\n // Recomputed on every parent resize so the panel can never cover the\n // canvas. Starts at `maxWidth` so the first paint is sane; the\n // ResizeObserver below tightens it as soon as the layout settles.\n const [effectiveMax, setEffectiveMax] = useState(maxWidth);\n const panelRef = useRef<HTMLElement>(null);\n useEffect(() => {\n const el = panelRef.current;\n if (!el) return;\n const parent = el.parentElement;\n if (!parent || typeof ResizeObserver === \"undefined\") return;\n const update = () => {\n const room = parent.clientWidth - maxWidthContainerInset;\n const next = Math.max(minWidth, Math.min(maxWidth, room));\n setEffectiveMax(next);\n // If the current width exceeds the new ceiling (window shrank),\n // snap the panel down so it remains inside the canvas.\n setWidth((w) => (w > next ? next : w));\n };\n update();\n const ro = new ResizeObserver(update);\n ro.observe(parent);\n return () => ro.disconnect();\n }, [maxWidth, minWidth, maxWidthContainerInset]);\n const [internalActive, setInternalActive] = useState<string | undefined>(\n defaultActiveTab ?? tabs?.[0]?.id,\n );\n const activeTabId = controlledActive ?? internalActive;\n const selectTab = (id: string) => {\n if (controlledActive === undefined) setInternalActive(id);\n onTabChange?.(id);\n };\n\n // ── Title editor ────────────────────────────────────────────────────\n const [editingTitle, setEditingTitle] = useState(false);\n const [draftTitle, setDraftTitle] = useState(title ?? \"\");\n useEffect(() => {\n if (!editingTitle) setDraftTitle(title ?? \"\");\n }, [title, editingTitle]);\n const commitTitle = (next: string) => {\n setEditingTitle(false);\n if (next !== title) onTitleChange?.(next);\n };\n const titleKeyDown = (e: ReactKeyboardEvent<HTMLInputElement>) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n commitTitle(draftTitle);\n } else if (e.key === \"Escape\") {\n e.preventDefault();\n setEditingTitle(false);\n setDraftTitle(title ?? \"\");\n }\n };\n\n // ── Resize handle ───────────────────────────────────────────────────\n // We capture pointer events on the drag handle; the panel width is\n // updated on every pointermove. Width is clamped to [min, max].\n const resizeRef = useRef<{ pointerId: number; startX: number; startWidth: number } | null>(null);\n const onResizeDown = useCallback(\n (e: PointerEvent<HTMLDivElement>) => {\n if (!resizable) return;\n e.preventDefault();\n e.stopPropagation();\n resizeRef.current = { pointerId: e.pointerId, startX: e.clientX, startWidth: width };\n (e.target as HTMLElement).setPointerCapture(e.pointerId);\n },\n [resizable, width],\n );\n const onResizeMove = (e: PointerEvent<HTMLDivElement>) => {\n const r = resizeRef.current;\n if (!r || r.pointerId !== e.pointerId) return;\n // Dragging LEFT widens the panel (it sticks to the right edge of\n // the canvas). Upper bound is `effectiveMax` — the smaller of the\n // consumer's `maxWidth` and the parent container's available room,\n // so the panel can never grow past the canvas itself.\n const delta = r.startX - e.clientX;\n const next = Math.max(minWidth, Math.min(effectiveMax, r.startWidth + delta));\n setWidth(next);\n };\n const onResizeUp = (e: PointerEvent<HTMLDivElement>) => {\n if (resizeRef.current?.pointerId === e.pointerId) resizeRef.current = null;\n };\n\n if (!open) return null;\n\n const activeTab = tabs?.find((t) => t.id === activeTabId);\n\n return (\n <aside\n ref={panelRef}\n className={cn(\n \"ods-flow-config-panel\",\n `ods-flow-config-panel--${variant}`,\n editingTitle && \"ods-flow-config-panel--editing-title\",\n className,\n )}\n style={{ width, ...style }}\n aria-label={title ?? \"Configuration\"}\n >\n {resizable && (\n <div\n className=\"ods-flow-config-panel__resize\"\n onPointerDown={onResizeDown}\n onPointerMove={onResizeMove}\n onPointerUp={onResizeUp}\n onPointerCancel={onResizeUp}\n aria-hidden=\"true\"\n />\n )}\n\n <header className=\"ods-flow-config-panel__header\">\n <div className=\"ods-flow-config-panel__title-row\">\n {icon && <span className=\"ods-flow-config-panel__icon\">{icon}</span>}\n {editingTitle ? (\n <input\n className=\"ods-flow-config-panel__title-input\"\n value={draftTitle}\n autoFocus\n onChange={(e) => setDraftTitle(e.target.value)}\n onBlur={() => commitTitle(draftTitle)}\n onKeyDown={titleKeyDown}\n aria-label=\"Edit title\"\n />\n ) : (\n <button\n type=\"button\"\n className=\"ods-flow-config-panel__title\"\n onDoubleClick={() => {\n if (!onTitleChange) return;\n setDraftTitle(title ?? \"\");\n setEditingTitle(true);\n }}\n title={onTitleChange ? \"Double-click to rename\" : undefined}\n >\n {title ?? \"Untitled\"}\n </button>\n )}\n {headerActions && (\n <div className=\"ods-flow-config-panel__header-actions\">\n {headerActions}\n </div>\n )}\n {onClose && (\n <button\n type=\"button\"\n className=\"ods-flow-config-panel__close\"\n onClick={onClose}\n aria-label=\"Close panel\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" aria-hidden=\"true\">\n <path\n d=\"M3 3l8 8M11 3l-8 8\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n />\n </svg>\n </button>\n )}\n </div>\n </header>\n\n {tabs && tabs.length > 1 && (\n <nav className=\"ods-flow-config-panel__tabs\" role=\"tablist\">\n {tabs.map((t) => (\n <button\n key={t.id}\n type=\"button\"\n role=\"tab\"\n aria-selected={activeTabId === t.id}\n disabled={t.disabled}\n className={cn(\n \"ods-flow-config-panel__tab\",\n activeTabId === t.id && \"ods-flow-config-panel__tab--active\",\n )}\n onClick={() => selectTab(t.id)}\n >\n <span>{t.label}</span>\n {t.badge !== undefined && t.badge !== null && (\n <span className=\"ods-flow-config-panel__tab-badge\">{t.badge}</span>\n )}\n </button>\n ))}\n </nav>\n )}\n\n {banner && <div className=\"ods-flow-config-panel__banner\">{banner}</div>}\n\n <div\n className=\"ods-flow-config-panel__body\"\n role=\"tabpanel\"\n style={{ padding }}\n >\n {tabs ? activeTab?.content : children}\n </div>\n\n <footer className=\"ods-flow-config-panel__footer\">\n {saveState && (\n <span\n className={cn(\n \"ods-flow-config-panel__save-state\",\n `ods-flow-config-panel__save-state--${saveState}`,\n )}\n >\n <span\n className=\"ods-flow-config-panel__save-dot\"\n style={{ background: SAVE_STATE_DOT[saveState] }}\n aria-hidden=\"true\"\n />\n {SAVE_STATE_LABEL[saveState]}\n </span>\n )}\n <div className=\"ods-flow-config-panel__footer-actions\">\n {footerActions ?? (\n <>\n {onCancel && (\n <button\n type=\"button\"\n className=\"ods-flow-config-panel__btn ods-flow-config-panel__btn--ghost\"\n onClick={onCancel}\n >\n Cancel\n </button>\n )}\n {onSave && (\n <button\n type=\"button\"\n className=\"ods-flow-config-panel__btn ods-flow-config-panel__btn--primary\"\n onClick={onSave}\n disabled={saveState === \"saving\"}\n >\n {saveState === \"saving\" ? \"Saving…\" : \"Save\"}\n </button>\n )}\n </>\n )}\n </div>\n </footer>\n </aside>\n );\n}\n","\"use client\";\n\n// ════════════════════════════════════════════════════════════════════════\n// FxPanel — the FX / IO catalogue that docks beside the ConfigPanel.\n// ════════════════════════════════════════════════════════════════════════\n// A searchable catalogue of expression building-blocks:\n// - Functions — flat lists (CONCAT, UPPER, …).\n// - Variables — flat lists of system/session tokens.\n// - Source Data — an n-th-level NESTED TREE of upstream-node output\n// fields. Objects + arrays-of-objects are expandable branches;\n// primitives are leaves.\n//\n// Every item is draggable into any FX-aware input, click-to-insert, and\n// copy-to-clipboard. The component is data-driven: the consumer supplies\n// `categories`; FxPanel owns search, the accordion, the tree expansion,\n// keyboard navigation, and the drag/copy affordances.\n\nimport {\n type CSSProperties,\n type DragEvent,\n type KeyboardEvent as ReactKeyboardEvent,\n type ReactNode,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport {\n ChevronDownIcon,\n ChevronRightIcon,\n CloseIcon,\n CollapseAllIcon,\n DraggableIcon,\n ExpandAllIcon,\n FunctionMathIcon,\n SearchIcon,\n ValueVariableIcon,\n} from \"@octaviaflow/icons\";\nimport { cn } from \"../../../utils/cn\";\n\nexport type FxItemKind = \"function\" | \"variable\";\n\n/** Data type of a Source-Data field — drives the colour-coded badge. */\nexport type FxValueType =\n | \"string\"\n | \"number\"\n | \"boolean\"\n | \"object\"\n | \"array\"\n | \"date\"\n | \"any\";\n\nexport interface FxPanelItem {\n /** Stable key + tree/keyboard identity. */\n id: string;\n /** Display label — a function signature, variable name, or field key. */\n label: string;\n /**\n * The string handed to `dataTransfer` / inserted into a field.\n * Branches may still set it (drag the whole object/array reference).\n */\n insertValue?: string;\n /** Short one-line description shown under the label. */\n description?: string;\n /** Data type — renders a colour-coded badge. Source-Data fields only. */\n valueType?: FxValueType;\n /**\n * Nested children. When present the item renders as an expandable\n * branch — this is what powers the n-th-level Source-Data tree.\n */\n children?: FxPanelItem[];\n}\n\nexport interface FxPanelCategory {\n /** Stable key + accordion identity. */\n id: string;\n /** Section heading. */\n label: string;\n /** Drives the badge glyph + drag payload. Defaults to \"function\". */\n kind?: FxItemKind;\n /** Items — flat for functions/variables, nested for Source Data. */\n items: FxPanelItem[];\n}\n\nexport interface FxPanelProps {\n /** Categorised entries. Empty categories are dropped from render. */\n categories: FxPanelCategory[];\n /** Header title. Default \"FX / IO\". */\n title?: ReactNode;\n /** Instructional line under the search. Pass `null` to hide it. */\n hint?: ReactNode;\n /** Controlled search term. */\n search?: string;\n /** Initial search term when uncontrolled. */\n defaultSearch?: string;\n onSearchChange?: (next: string) => void;\n /** Controlled open category (single-open accordion). `null` = closed. */\n expandedCategory?: string | null;\n /** Initial open category when uncontrolled. */\n defaultExpandedCategory?: string | null;\n onExpandedCategoryChange?: (id: string | null) => void;\n /** Close (✕) handler. Omit to hide the close button. */\n onClose?: () => void;\n /**\n * Fires when an item begins dragging. Use it to populate\n * `e.dataTransfer`. When omitted, FxPanel writes a sensible default:\n * - `text/plain` → insertValue\n * - `application/x-fx-insert` → insertValue\n * - `application/x-fx-type` → category kind\n */\n onItemDragStart?: (\n item: FxPanelItem,\n category: FxPanelCategory,\n e: DragEvent<HTMLElement>,\n ) => void;\n /** Fires on click / Enter of an item (insert at cursor). */\n onItemSelect?: (item: FxPanelItem, category: FxPanelCategory) => void;\n /** Panel width (px). Default 300. */\n width?: number;\n /** Message shown when a search matches nothing. Default \"No matches\". */\n emptyLabel?: ReactNode;\n /** Placeholder for the search box. */\n searchPlaceholder?: string;\n className?: string;\n style?: CSSProperties;\n}\n\n// Category badge icon — a relatable glyph per kind (functions get the\n// math-function mark, variables/data get the value-variable mark).\nconst KIND_ICON: Record<FxItemKind, typeof FunctionMathIcon> = {\n function: FunctionMathIcon,\n variable: ValueVariableIcon,\n};\n\n// ── Tree helpers ─────────────────────────────────────────────────────────\n\n/** Recursively count leaf items (primitives — the drag end-points). */\nfunction countLeaves(items: FxPanelItem[]): number {\n let n = 0;\n for (const it of items) {\n if (it.children && it.children.length > 0) n += countLeaves(it.children);\n else n += 1;\n }\n return n;\n}\n\n/** Collect the ids of every branch (has children) under `items`. */\nfunction collectBranchIds(items: FxPanelItem[], out: Set<string>): void {\n for (const it of items) {\n if (it.children && it.children.length > 0) {\n out.add(it.id);\n collectBranchIds(it.children, out);\n }\n }\n}\n\ninterface FlatRow {\n item: FxPanelItem;\n depth: number;\n hasChildren: boolean;\n expanded: boolean;\n}\n\n/** Flatten the visible tree (respecting `expandedItems`) into draw rows. */\nfunction flattenTree(\n items: FxPanelItem[],\n depth: number,\n expandedItems: Set<string>,\n out: FlatRow[],\n): void {\n for (const it of items) {\n const hasChildren = !!it.children && it.children.length > 0;\n const expanded = hasChildren && expandedItems.has(it.id);\n out.push({ item: it, depth, hasChildren, expanded });\n if (expanded) flattenTree(it.children!, depth + 1, expandedItems, out);\n }\n}\n\ninterface SearchHit {\n item: FxPanelItem;\n /** Ancestor labels (excluding the item itself) for the path crumb. */\n ancestors: string[];\n}\n\n/** Collect leaf items whose label / description / path matches `q`. */\nfunction collectHits(\n items: FxPanelItem[],\n ancestors: string[],\n q: string,\n out: SearchHit[],\n): void {\n for (const it of items) {\n if (it.children && it.children.length > 0) {\n collectHits(it.children, [...ancestors, it.label], q, out);\n } else {\n const path = [...ancestors, it.label].join(\" / \");\n const hay =\n `${it.label} ${it.description ?? \"\"} ${path} ${it.insertValue ?? \"\"}`.toLowerCase();\n if (hay.includes(q)) out.push({ item: it, ancestors });\n }\n }\n}\n\n// ════════════════════════════════════════════════════════════════════════\n\nexport function FxPanel({\n categories,\n title = \"FX / IO\",\n hint = (\n <>\n Drag a field into any input with the <strong>FX</strong> badge, or\n click to insert.\n </>\n ),\n search: controlledSearch,\n defaultSearch = \"\",\n onSearchChange,\n expandedCategory: controlledExpanded,\n defaultExpandedCategory,\n onExpandedCategoryChange,\n onClose,\n onItemDragStart,\n onItemSelect,\n width = 300,\n emptyLabel = \"No matches\",\n searchPlaceholder = \"Search fields, functions & variables…\",\n className,\n style,\n}: FxPanelProps) {\n // ── Search — controlled or uncontrolled ─────────────────────────────\n const [internalSearch, setInternalSearch] = useState(defaultSearch);\n const search = controlledSearch ?? internalSearch;\n const query = search.trim().toLowerCase();\n const setSearch = (next: string) => {\n if (controlledSearch === undefined) setInternalSearch(next);\n onSearchChange?.(next);\n };\n\n // Non-empty categories only — empty ones never render.\n const visibleCategories = useMemo(\n () => categories.filter((c) => c.items.length > 0),\n [categories],\n );\n\n // ── Category accordion — controlled or uncontrolled ────────────────\n const [internalCat, setInternalCat] = useState<string | null>(\n defaultExpandedCategory !== undefined\n ? defaultExpandedCategory\n : (visibleCategories[0]?.id ?? null),\n );\n const openCategoryId = controlledExpanded ?? internalCat;\n const setOpenCategory = (id: string | null) => {\n if (controlledExpanded === undefined) setInternalCat(id);\n onExpandedCategoryChange?.(id);\n };\n\n // ── Tree expansion — branch ids that are open. Defaults to every\n // depth-0 branch so the first level of each tree shows on open. ──\n const [expandedItems, setExpandedItems] = useState<Set<string>>(() => {\n const ids = new Set<string>();\n for (const cat of categories) {\n for (const item of cat.items) {\n if (item.children && item.children.length > 0) ids.add(item.id);\n }\n }\n return ids;\n });\n const toggleItem = (id: string) => {\n setExpandedItems((prev) => {\n const next = new Set(prev);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n };\n const setCategoryExpansion = (cat: FxPanelCategory, expand: boolean) => {\n const branchIds = new Set<string>();\n collectBranchIds(cat.items, branchIds);\n setExpandedItems((prev) => {\n const next = new Set(prev);\n for (const id of branchIds) {\n if (expand) next.add(id);\n else next.delete(id);\n }\n return next;\n });\n };\n\n // ── Transient UI state ──────────────────────────────────────────────\n const [draggingId, setDraggingId] = useState<string | null>(null);\n const [focusedId, setFocusedId] = useState<string | null>(null);\n\n // ── Search results ──────────────────────────────────────────────────\n const hits = useMemo(() => {\n if (!query) return null;\n const all: { category: FxPanelCategory; hit: SearchHit }[] = [];\n for (const cat of visibleCategories) {\n const out: SearchHit[] = [];\n collectHits(cat.items, [], query, out);\n for (const hit of out) all.push({ category: cat, hit });\n }\n return all;\n }, [query, visibleCategories]);\n\n // ── Drag / copy / insert ────────────────────────────────────────────\n const handleDragStart =\n (item: FxPanelItem, category: FxPanelCategory) =>\n (e: DragEvent<HTMLElement>) => {\n setDraggingId(item.id);\n if (onItemDragStart) {\n onItemDragStart(item, category, e);\n } else if (item.insertValue) {\n e.dataTransfer.setData(\"text/plain\", item.insertValue);\n e.dataTransfer.setData(\"application/x-fx-insert\", item.insertValue);\n e.dataTransfer.setData(\n \"application/x-fx-type\",\n category.kind ?? \"function\",\n );\n e.dataTransfer.effectAllowed = \"copy\";\n }\n };\n\n // ── Keyboard navigation over the open category's visible rows ───────\n const listRef = useRef<HTMLDivElement>(null);\n const focusRow = (id: string) => {\n setFocusedId(id);\n requestAnimationFrame(() => {\n listRef.current\n ?.querySelector<HTMLElement>(`[data-fx-row=\"${CSS.escape(id)}\"]`)\n ?.focus();\n });\n };\n const onRowKeyDown = (\n e: ReactKeyboardEvent<HTMLDivElement>,\n rows: FlatRow[],\n row: FlatRow,\n category: FxPanelCategory,\n ) => {\n const idx = rows.findIndex((r) => r.item.id === row.item.id);\n if (idx < 0) return;\n switch (e.key) {\n case \"ArrowDown\":\n e.preventDefault();\n if (idx < rows.length - 1) focusRow(rows[idx + 1].item.id);\n break;\n case \"ArrowUp\":\n e.preventDefault();\n if (idx > 0) focusRow(rows[idx - 1].item.id);\n break;\n case \"ArrowRight\":\n if (row.hasChildren) {\n e.preventDefault();\n if (!row.expanded) toggleItem(row.item.id);\n else if (idx < rows.length - 1) focusRow(rows[idx + 1].item.id);\n }\n break;\n case \"ArrowLeft\":\n if (row.hasChildren && row.expanded) {\n e.preventDefault();\n toggleItem(row.item.id);\n } else {\n // Walk back to the parent (first shallower row).\n for (let i = idx - 1; i >= 0; i--) {\n if (rows[i].depth < row.depth) {\n e.preventDefault();\n focusRow(rows[i].item.id);\n break;\n }\n }\n }\n break;\n case \"Home\":\n e.preventDefault();\n if (rows.length) focusRow(rows[0].item.id);\n break;\n case \"End\":\n e.preventDefault();\n if (rows.length) focusRow(rows[rows.length - 1].item.id);\n break;\n case \"Enter\":\n case \" \":\n e.preventDefault();\n if (row.hasChildren) toggleItem(row.item.id);\n else onItemSelect?.(row.item, category);\n break;\n default:\n break;\n }\n };\n\n // Flat draw-rows for the active search results — drives keyboard nav.\n const searchRows = useMemo<FlatRow[]>(\n () =>\n hits\n ? hits.map(({ hit }) => ({\n item: hit.item,\n depth: 0,\n hasChildren: false,\n expanded: false,\n }))\n : [],\n [hits],\n );\n\n // ── Row renderer ────────────────────────────────────────────────────\n const renderRow = (\n row: FlatRow,\n category: FxPanelCategory,\n rows: FlatRow[],\n crumb?: string,\n ) => {\n const { item, depth, hasChildren, expanded } = row;\n const mono = category.kind === \"function\";\n const draggable = !!item.insertValue;\n const isFocused = focusedId === item.id;\n return (\n <div\n key={item.id}\n data-fx-row={item.id}\n className={cn(\n \"ods-flow-fx-panel__row\",\n hasChildren\n ? \"ods-flow-fx-panel__row--branch\"\n : \"ods-flow-fx-panel__row--leaf\",\n draggingId === item.id && \"ods-flow-fx-panel__row--dragging\",\n isFocused && \"ods-flow-fx-panel__row--focused\",\n )}\n role=\"treeitem\"\n aria-expanded={hasChildren ? expanded : undefined}\n aria-level={depth + 1}\n tabIndex={\n isFocused || (focusedId === null && rows[0]?.item.id === item.id)\n ? 0\n : -1\n }\n draggable={draggable}\n onDragStart={draggable ? handleDragStart(item, category) : undefined}\n onDragEnd={() => setDraggingId(null)}\n onFocus={() => setFocusedId(item.id)}\n onKeyDown={(e) => onRowKeyDown(e, rows, row, category)}\n onClick={() => {\n if (hasChildren) toggleItem(item.id);\n else onItemSelect?.(item, category);\n }}\n title={item.insertValue}\n >\n {/* Indent guides — one per ancestor depth. */}\n {Array.from({ length: depth }, (_, i) => (\n <span key={i} className=\"ods-flow-fx-panel__indent\" aria-hidden=\"true\" />\n ))}\n\n {/* Twisty (branches) or drag handle (leaves). */}\n {hasChildren ? (\n <span className=\"ods-flow-fx-panel__twisty\" aria-hidden=\"true\">\n {expanded ? (\n <ChevronDownIcon size=\"sm\" />\n ) : (\n <ChevronRightIcon size=\"sm\" />\n )}\n </span>\n ) : (\n <span className=\"ods-flow-fx-panel__grip\" aria-hidden=\"true\">\n <DraggableIcon size=\"sm\" />\n </span>\n )}\n\n <span className=\"ods-flow-fx-panel__row-main\">\n <span className=\"ods-flow-fx-panel__row-line\">\n {mono ? (\n <code className=\"ods-flow-fx-panel__row-label ods-flow-fx-panel__row-label--mono\">\n {item.label}\n </code>\n ) : (\n <span className=\"ods-flow-fx-panel__row-label\">{item.label}</span>\n )}\n {item.valueType && (\n <span\n className={cn(\n \"ods-flow-fx-panel__type\",\n `ods-flow-fx-panel__type--${item.valueType}`,\n )}\n >\n {item.valueType}\n </span>\n )}\n </span>\n {(item.description || crumb) && (\n <span className=\"ods-flow-fx-panel__row-sub\">\n {crumb && (\n <span className=\"ods-flow-fx-panel__crumb\">{crumb}</span>\n )}\n {item.description}\n </span>\n )}\n </span>\n </div>\n );\n };\n\n // ── Render ──────────────────────────────────────────────────────────\n return (\n <aside\n className={cn(\"ods-flow-fx-panel\", className)}\n style={{ width, ...style }}\n aria-label={typeof title === \"string\" ? title : \"FX / IO\"}\n >\n <header className=\"ods-flow-fx-panel__header\">\n <h3 className=\"ods-flow-fx-panel__title\">{title}</h3>\n {onClose && (\n <button\n type=\"button\"\n className=\"ods-flow-fx-panel__close\"\n onClick={onClose}\n aria-label=\"Close FX panel\"\n >\n <CloseIcon size=\"sm\" />\n </button>\n )}\n </header>\n\n <div className=\"ods-flow-fx-panel__search\">\n <span className=\"ods-flow-fx-panel__search-icon\" aria-hidden=\"true\">\n <SearchIcon size=\"sm\" />\n </span>\n <input\n type=\"text\"\n className=\"ods-flow-fx-panel__search-input\"\n placeholder={searchPlaceholder}\n aria-label=\"Search fields, functions and variables\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n />\n {search && (\n <button\n type=\"button\"\n className=\"ods-flow-fx-panel__search-clear\"\n onClick={() => setSearch(\"\")}\n aria-label=\"Clear search\"\n >\n <CloseIcon size=\"sm\" />\n </button>\n )}\n </div>\n\n {hint && !query && <p className=\"ods-flow-fx-panel__hint\">{hint}</p>}\n\n <div className=\"ods-flow-fx-panel__list\" ref={listRef} role=\"tree\">\n {/* ── Search mode — flat, path-crumbed leaf matches. ── */}\n {query &&\n hits &&\n (hits.length > 0 ? (\n <div className=\"ods-flow-fx-panel__results\">\n <div className=\"ods-flow-fx-panel__results-count\">\n {hits.length} {hits.length === 1 ? \"match\" : \"matches\"}\n </div>\n {hits.map(({ category, hit }, i) =>\n renderRow(\n searchRows[i],\n category,\n searchRows,\n hit.ancestors.length\n ? hit.ancestors.join(\" / \")\n : category.label,\n ),\n )}\n </div>\n ) : (\n <div className=\"ods-flow-fx-panel__empty\">{emptyLabel}</div>\n ))}\n\n {/* ── Browse mode — category accordion + per-branch tree. ── */}\n {!query &&\n visibleCategories.map((cat) => {\n const isOpen = openCategoryId === cat.id;\n const kind = cat.kind ?? \"function\";\n const KindIcon = KIND_ICON[kind];\n const branchIds = new Set<string>();\n collectBranchIds(cat.items, branchIds);\n const hasTree = branchIds.size > 0;\n const rows: FlatRow[] = [];\n if (isOpen) flattenTree(cat.items, 0, expandedItems, rows);\n const allExpanded =\n hasTree &&\n [...branchIds].every((id) => expandedItems.has(id));\n\n return (\n <div className=\"ods-flow-fx-panel__category\" key={cat.id}>\n <div\n className={cn(\n \"ods-flow-fx-panel__cat-header\",\n isOpen && \"ods-flow-fx-panel__cat-header--open\",\n )}\n >\n <button\n type=\"button\"\n className=\"ods-flow-fx-panel__cat-toggle\"\n aria-expanded={isOpen}\n onClick={() => setOpenCategory(isOpen ? null : cat.id)}\n >\n <ChevronRightIcon\n size=\"sm\"\n className={cn(\n \"ods-flow-fx-panel__chevron\",\n isOpen && \"ods-flow-fx-panel__chevron--open\",\n )}\n />\n <span\n className={cn(\n \"ods-flow-fx-panel__badge\",\n `ods-flow-fx-panel__badge--${kind}`,\n )}\n >\n <KindIcon\n size=\"sm\"\n className=\"ods-flow-fx-panel__badge-icon\"\n />\n {cat.label}\n </span>\n <span className=\"ods-flow-fx-panel__count\">\n {countLeaves(cat.items)}\n </span>\n </button>\n {/* Expand / collapse the whole tree of this category. */}\n {isOpen && hasTree && (\n <button\n type=\"button\"\n className=\"ods-flow-fx-panel__cat-action\"\n aria-label={\n allExpanded ? \"Collapse all\" : \"Expand all\"\n }\n title={allExpanded ? \"Collapse all\" : \"Expand all\"}\n onClick={() => setCategoryExpansion(cat, !allExpanded)}\n >\n {allExpanded ? (\n <CollapseAllIcon size=\"sm\" />\n ) : (\n <ExpandAllIcon size=\"sm\" />\n )}\n </button>\n )}\n </div>\n\n {isOpen && (\n <div\n className=\"ods-flow-fx-panel__rows\"\n role=\"group\"\n aria-label={cat.label}\n >\n {rows.map((row) => renderRow(row, cat, rows))}\n </div>\n )}\n </div>\n );\n })}\n </div>\n </aside>\n );\n}\n","\"use client\";\n\n// ════════════════════════════════════════════════════════════════════════\n// DataRefButton — the trigger that opens/closes the FxPanel.\n// ════════════════════════════════════════════════════════════════════════\n// A compact toggle in the ConfigPanel header's modular action slot. It\n// carries the data-reference icon + label and reflects the panel's open\n// state through `aria-pressed` + an `--active` visual.\n\nimport { type ButtonHTMLAttributes, type ReactNode } from \"react\";\nimport { DataReferenceIcon } from \"@octaviaflow/icons\";\nimport { cn } from \"../../../utils/cn\";\n\nexport interface DataRefButtonProps\n extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, \"children\"> {\n /** Whether the FxPanel is currently open. Drives `aria-pressed`. */\n active?: boolean;\n /** Visible label beside the icon. Default \"Data ref\". `null` = icon-only. */\n label?: ReactNode;\n}\n\nexport function DataRefButton({\n active = false,\n label = \"Data ref\",\n className,\n type = \"button\",\n ...rest\n}: DataRefButtonProps) {\n return (\n <button\n {...rest}\n type={type}\n className={cn(\n \"ods-flow-dataref-button\",\n active && \"ods-flow-dataref-button--active\",\n label == null && \"ods-flow-dataref-button--icon-only\",\n className,\n )}\n aria-pressed={active}\n title={active ? \"Hide data references\" : \"Show data references\"}\n >\n <DataReferenceIcon\n size=\"sm\"\n className=\"ods-flow-dataref-button__icon\"\n />\n {label != null && (\n <span className=\"ods-flow-dataref-button__label\">{label}</span>\n )}\n </button>\n );\n}\n","\"use client\";\n\n// ════════════════════════════════════════════════════════════════════════\n// NodeToolbar — floating action bar attached to a node.\n// ════════════════════════════════════════════════════════════════════════\n// Dropped inside a custom node renderer (or anywhere with a\n// `FlowNodeContext`). Visible by default only when the host node is\n// selected; consumers can force it on with `isVisible`. Position-stable\n// across zoom — the bar applies `scale(1 / zoom)` to counter the viewport\n// transform so the buttons stay at constant pixel size.\n//\n// Pointer events on the toolbar are isolated from the node's drag /\n// connection gestures via `data-flow-no-drag=\"true\"` on the wrapper.\n\nimport type { CSSProperties, ReactNode } from \"react\";\nimport { cn } from \"../../../utils/cn\";\nimport type { HandlePosition } from \"../../editor\";\nimport { useViewport } from \"../../store/selectors\";\nimport { useFlowNodeContext } from \"../FlowNode/FlowNodeContext\";\n\nexport interface NodeToolbarProps {\n /** Force-visible state. Defaults to `undefined` → auto (visible iff\n * the host node is selected). Pass `true`/`false` to override. */\n isVisible?: boolean;\n /** Where the toolbar sits relative to the node. Defaults to `top`. */\n position?: HandlePosition;\n /** Pixel offset from the node's edge. Default 10. */\n offset?: number;\n /** Horizontal/vertical alignment along the chosen edge. Default center. */\n align?: \"start\" | \"center\" | \"end\";\n /** Toolbar contents (typically a row of `<button>` elements). */\n children: ReactNode;\n className?: string;\n style?: CSSProperties;\n}\n\nexport function NodeToolbar({\n isVisible,\n position = \"top\",\n offset = 10,\n align = \"center\",\n children,\n className,\n style,\n}: NodeToolbarProps) {\n const node = useFlowNodeContext();\n const viewport = useViewport();\n const show = isVisible ?? node.selected;\n if (!show) return null;\n\n // Counter the canvas's `scale(zoom)` so the toolbar reads at constant\n // pixel size at any zoom level. Translation is in flow-space units; the\n // inverse-scale wraps the visible content so children also stay crisp.\n const inverseScale = 1 / viewport.zoom;\n\n return (\n <div\n className={cn(\"ods-node-toolbar\", `ods-node-toolbar--${position}`, className)}\n data-flow-no-drag=\"true\"\n style={{\n ...positionStyle(position, offset, align),\n ...style,\n }}\n onPointerDown={(e) => e.stopPropagation()}\n onMouseDown={(e) => e.stopPropagation()}\n onClick={(e) => e.stopPropagation()}\n >\n {/* Inner element absorbs the inverse zoom so the toolbar's\n children stay screen-stable. */}\n <div\n className=\"ods-node-toolbar__inner\"\n style={{\n transform: `scale(${inverseScale})`,\n transformOrigin: transformOriginFor(position, align),\n }}\n >\n {children}\n </div>\n </div>\n );\n}\n\nfunction positionStyle(\n side: HandlePosition,\n offset: number,\n align: NodeToolbarProps[\"align\"],\n): CSSProperties {\n const center = align === \"center\";\n const start = align === \"start\";\n switch (side) {\n case \"top\":\n return {\n position: \"absolute\",\n bottom: \"100%\",\n marginBottom: offset,\n left: start ? 0 : center ? \"50%\" : undefined,\n right: align === \"end\" ? 0 : undefined,\n transform: center ? \"translateX(-50%)\" : undefined,\n };\n case \"bottom\":\n return {\n position: \"absolute\",\n top: \"100%\",\n marginTop: offset,\n left: start ? 0 : center ? \"50%\" : undefined,\n right: align === \"end\" ? 0 : undefined,\n transform: center ? \"translateX(-50%)\" : undefined,\n };\n case \"left\":\n return {\n position: \"absolute\",\n right: \"100%\",\n marginRight: offset,\n top: start ? 0 : center ? \"50%\" : undefined,\n bottom: align === \"end\" ? 0 : undefined,\n transform: center ? \"translateY(-50%)\" : undefined,\n };\n case \"right\":\n return {\n position: \"absolute\",\n left: \"100%\",\n marginLeft: offset,\n top: start ? 0 : center ? \"50%\" : undefined,\n bottom: align === \"end\" ? 0 : undefined,\n transform: center ? \"translateY(-50%)\" : undefined,\n };\n }\n}\n\nfunction transformOriginFor(side: HandlePosition, align: NodeToolbarProps[\"align\"]): string {\n // Choose an origin that visually attaches the bar to the node edge,\n // even after inverse-scaling. Defaults match the `position*` styles.\n if (side === \"top\")\n return align === \"start\" ? \"left bottom\" : align === \"end\" ? \"right bottom\" : \"center bottom\";\n if (side === \"bottom\")\n return align === \"start\" ? \"left top\" : align === \"end\" ? \"right top\" : \"center top\";\n if (side === \"left\")\n return align === \"start\" ? \"right top\" : align === \"end\" ? \"right bottom\" : \"right center\";\n return align === \"start\" ? \"left top\" : align === \"end\" ? \"left bottom\" : \"left center\";\n}\n","// ════════════════════════════════════════════════════════════════════════\n// Collapse helpers — when a group's `data.collapsed === true`, hide its\n// descendants AND re-route any edges that crossed the collapse boundary\n// so they terminate at the group instead of dangling at a hidden child.\n// ════════════════════════════════════════════════════════════════════════\n//\n// Consumers feed `nodes` + `edges` (their canonical state) through\n// `collapseFor()` and pass the result to <FlowCanvas>. The helpers are\n// pure — no React state, no DOM. They run in O(N + E) so they're cheap\n// to call on every render.\n\nimport type { WorkflowEdge, WorkflowNode } from \"../editor\";\n\ninterface CollapseResult {\n nodes: WorkflowNode[];\n edges: WorkflowEdge[];\n /** Per-collapsed-group: how many descendants got hidden. The host UI\n * reads this to render a \"N steps\" badge. */\n hiddenCountByGroupId: Map<string, number>;\n}\n\n/**\n * Given the canonical graph, return a derived graph that respects the\n * `collapsed` flag on every group node:\n *\n * - Descendants of a collapsed group are marked `hidden`.\n * - Edges that crossed the collapse boundary are rewritten: their\n * source (or target) becomes the collapsed group's id, and the\n * handle is reset to a synthetic perimeter handle (`__group_out` /\n * `__group_in`). Internal-to-internal edges are dropped.\n */\nexport function collapseFor(nodes: WorkflowNode[], edges: WorkflowEdge[]): CollapseResult {\n // Map child → topmost collapsed ancestor (if any).\n // If a node has multiple collapsed ancestors, the OUTERMOST wins so the\n // edge terminates at the visible boundary.\n const collapsedAncestorOf = new Map<string, string>();\n const parents = new Map<string, string>();\n for (const n of nodes) if (n.parentId) parents.set(n.id, n.parentId);\n\n // ANY node with `data.collapsed === true` collapses its descendants —\n // not just `group`. This lets `forEach` (and consumer-defined container\n // kinds) opt into the same collapse model without each one being\n // hard-coded in this helper.\n const isCollapsed = (node: WorkflowNode | undefined): boolean => {\n const d = node?.data as { collapsed?: boolean } | undefined;\n return !!d?.collapsed;\n };\n\n for (const n of nodes) {\n let cursor: string | undefined = n.id;\n let outermostCollapsed: string | undefined;\n const seen = new Set<string>([cursor]);\n while (cursor) {\n const p = parents.get(cursor);\n if (!p || seen.has(p)) break;\n seen.add(p);\n const parentNode = nodes.find((x) => x.id === p);\n if (isCollapsed(parentNode)) outermostCollapsed = p;\n cursor = p;\n }\n if (outermostCollapsed && outermostCollapsed !== n.id) {\n collapsedAncestorOf.set(n.id, outermostCollapsed);\n }\n }\n\n // Hide every descendant of a collapsed group.\n const outNodes: WorkflowNode[] = nodes.map((n) => {\n const anc = collapsedAncestorOf.get(n.id);\n if (anc) return { ...n, hidden: true };\n return n;\n });\n\n // Rewrite edges: any endpoint that sits inside a collapsed group gets\n // hoisted to the group itself. Edges entirely within a collapsed group\n // are dropped. Edges that start at the group and end inside it (or\n // vice versa) become self-loops that we also drop.\n //\n // **Boundary marker** — edges that get rewritten to terminate at a\n // collapsed group's perimeter are stroked with a sparse dot pattern so\n // the user immediately sees they're a \"summary\" of one or more hidden\n // inner connections rather than a real handle-to-handle edge. The\n // original `edge.style` is preserved; only `strokeDasharray` is\n // overlaid. FlowEdge gives `edge.style.strokeDasharray` precedence\n // over the type's default dash.\n const BOUNDARY_DASH = \"2 4\";\n const outEdges: WorkflowEdge[] = [];\n const hiddenCount = new Map<string, number>();\n for (const e of edges) {\n const srcAnc = collapsedAncestorOf.get(e.source);\n const tgtAnc = collapsedAncestorOf.get(e.target);\n if (srcAnc && tgtAnc && srcAnc === tgtAnc) continue; // wholly inside one collapsed group — drop\n let src = e.source;\n let srcHandle = e.sourceHandle;\n let tgt = e.target;\n let tgtHandle = e.targetHandle;\n const rewritten = !!(srcAnc || tgtAnc);\n if (srcAnc) {\n src = srcAnc;\n // Synthetic perimeter handle — the GroupNode renderer registers\n // matching `__group_out` / `__group_in` handles when collapsed.\n srcHandle = \"__group_out\";\n }\n if (tgtAnc) {\n tgt = tgtAnc;\n tgtHandle = \"__group_in\";\n }\n if (src === tgt) continue; // collapsed self-loop\n const nextEdge: WorkflowEdge = {\n ...e,\n source: src,\n sourceHandle: srcHandle,\n target: tgt,\n targetHandle: tgtHandle,\n };\n if (rewritten) {\n nextEdge.style = { ...(e.style ?? {}), strokeDasharray: BOUNDARY_DASH };\n }\n outEdges.push(nextEdge);\n }\n\n // Count hidden descendants per collapsed group for the badge.\n for (const [_id, anc] of collapsedAncestorOf) {\n hiddenCount.set(anc, (hiddenCount.get(anc) ?? 0) + 1);\n }\n\n return { nodes: outNodes, edges: outEdges, hiddenCountByGroupId: hiddenCount };\n}\n\n/**\n * Toggle the `data.collapsed` flag on a group node. Returns the patched\n * nodes array (immutable). Useful in event handlers.\n */\nexport function toggleGroupCollapse(groupId: string, nodes: WorkflowNode[]): WorkflowNode[] {\n return nodes.map((n) => {\n if (n.id !== groupId) return n;\n const data = (n.data ?? {}) as { collapsed?: boolean } & Record<string, unknown>;\n return { ...n, data: { ...data, collapsed: !data.collapsed } };\n });\n}\n"],"mappings":"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","names":["useCallback","useRef","useState","useState","useRef","useCallback","useCallback","useEffect","useRef","useState","useState","useRef","useEffect","useCallback","useMemo","useRef","useState","Fragment","jsx","jsxs","useState","useMemo","useRef","jsx","jsxs","jsx"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@octaviaflow/core",
|
|
3
|
-
"version": "3.0.18-beta.
|
|
3
|
+
"version": "3.0.18-beta.27",
|
|
4
4
|
"description": "OctaviaFlow Design System React components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@octaviaflow/config": "3.0.18",
|
|
50
|
+
"@octaviaflow/core": "3.0.18-beta.26",
|
|
50
51
|
"@octaviaflow/fonts": "3.0.18",
|
|
51
52
|
"@octaviaflow/icons": "3.0.18-beta.8",
|
|
52
53
|
"@octaviaflow/tokens": "3.0.18",
|