@octaviaflow/core 3.0.18-beta.6 → 3.0.18-beta.8
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/chunk-2NGC7AI3.js +2637 -0
- package/dist/chunk-2NGC7AI3.js.map +1 -0
- package/dist/chunk-2O6K5PLY.js +2637 -0
- package/dist/chunk-2O6K5PLY.js.map +1 -0
- package/dist/chunk-C3UD2AZ5.js +2637 -0
- package/dist/chunk-C3UD2AZ5.js.map +1 -0
- package/dist/chunk-IOKUV7FD.js +2658 -0
- package/dist/chunk-IOKUV7FD.js.map +1 -0
- package/dist/chunk-KYMYNYFV.js +2656 -0
- package/dist/chunk-KYMYNYFV.js.map +1 -0
- package/dist/chunk-PVJXX6GP.js +2640 -0
- package/dist/chunk-PVJXX6GP.js.map +1 -0
- package/dist/components/CsvViewer/CsvViewer.d.ts +51 -0
- package/dist/components/CsvViewer/CsvViewer.d.ts.map +1 -0
- package/dist/components/CsvViewer/index.d.ts +2 -0
- package/dist/components/CsvViewer/index.d.ts.map +1 -0
- package/dist/components/DropdownMenu/DropdownMenu.d.ts +4 -1
- package/dist/components/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/components/ExecutionConsole/ExecutionConsole.d.ts +8 -2
- package/dist/components/ExecutionConsole/ExecutionConsole.d.ts.map +1 -1
- package/dist/components/FlowMinimap/FlowMinimap.d.ts +17 -1
- package/dist/components/FlowMinimap/FlowMinimap.d.ts.map +1 -1
- package/dist/components/FlowToolbar/FlowToolbar.d.ts +16 -10
- package/dist/components/FlowToolbar/FlowToolbar.d.ts.map +1 -1
- package/dist/components/JsonViewer/JsonViewer.d.ts +42 -7
- package/dist/components/JsonViewer/JsonViewer.d.ts.map +1 -1
- package/dist/components/JsonViewer/index.d.ts +1 -1
- package/dist/components/JsonViewer/index.d.ts.map +1 -1
- package/dist/components/WorkflowHeader/WorkflowHeader.d.ts +130 -0
- package/dist/components/WorkflowHeader/WorkflowHeader.d.ts.map +1 -0
- package/dist/components/WorkflowHeader/WorkflowHeaderExpanded.d.ts +69 -0
- package/dist/components/WorkflowHeader/WorkflowHeaderExpanded.d.ts.map +1 -0
- package/dist/components/WorkflowHeader/index.d.ts +3 -0
- package/dist/components/WorkflowHeader/index.d.ts.map +1 -0
- package/dist/components/WorkflowHeader/misc/WorkflowHeaderCentered.d.ts +40 -0
- package/dist/components/WorkflowHeader/misc/WorkflowHeaderCentered.d.ts.map +1 -0
- package/dist/components/WorkflowHeader/misc/WorkflowHeaderCommand.d.ts +39 -0
- package/dist/components/WorkflowHeader/misc/WorkflowHeaderCommand.d.ts.map +1 -0
- package/dist/components/WorkflowHeader/misc/WorkflowHeaderMinimal.d.ts +44 -0
- package/dist/components/WorkflowHeader/misc/WorkflowHeaderMinimal.d.ts.map +1 -0
- package/dist/components/WorkflowHeader/misc/WorkflowHeaderRail.d.ts +45 -0
- package/dist/components/WorkflowHeader/misc/WorkflowHeaderRail.d.ts.map +1 -0
- package/dist/components/WorkflowHeader/misc/WorkflowHeaderStudio.d.ts +48 -0
- package/dist/components/WorkflowHeader/misc/WorkflowHeaderStudio.d.ts.map +1 -0
- package/dist/components/WorkflowHeader/misc/WorkflowHeaderTiered.d.ts +52 -0
- package/dist/components/WorkflowHeader/misc/WorkflowHeaderTiered.d.ts.map +1 -0
- package/dist/components/XmlViewer/XmlViewer.d.ts +26 -1
- package/dist/components/XmlViewer/XmlViewer.d.ts.map +1 -1
- package/dist/components/XmlViewer/index.d.ts +1 -1
- package/dist/components/XmlViewer/index.d.ts.map +1 -1
- package/dist/components/YamlViewer/YamlViewer.d.ts +26 -1
- package/dist/components/YamlViewer/YamlViewer.d.ts.map +1 -1
- package/dist/components/YamlViewer/index.d.ts +1 -1
- package/dist/components/YamlViewer/index.d.ts.map +1 -1
- package/dist/hooks/useRelativeTime.d.ts +28 -0
- package/dist/hooks/useRelativeTime.d.ts.map +1 -0
- package/dist/hooks/useWorkflowRuntime.d.ts +20 -0
- package/dist/hooks/useWorkflowRuntime.d.ts.map +1 -0
- package/dist/index.cjs +4423 -3411
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4243 -3230
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/workflow/components/FlowCanvas/FlowCanvas.d.ts +14 -1
- package/dist/workflow/components/FlowCanvas/FlowCanvas.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/store/selectors.d.ts +12 -0
- package/dist/workflow/store/selectors.d.ts.map +1 -1
- package/dist/workflow.cjs +84 -46
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.js +15 -57
- package/dist/workflow.js.map +1 -1
- package/package.json +7 -3
package/dist/workflow.js
CHANGED
|
@@ -35,10 +35,21 @@ import {
|
|
|
35
35
|
smoothStepPath,
|
|
36
36
|
stepPath,
|
|
37
37
|
straightPath,
|
|
38
|
+
useConnection,
|
|
39
|
+
useEdgeById,
|
|
40
|
+
useEdges,
|
|
38
41
|
useFlow,
|
|
39
42
|
useFlowNodeContext,
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
useFlowSelector,
|
|
44
|
+
useIsEdgeSelected,
|
|
45
|
+
useIsNodeSelected,
|
|
46
|
+
useNodeById,
|
|
47
|
+
useNodeData,
|
|
48
|
+
useNodes,
|
|
49
|
+
useSelection,
|
|
50
|
+
useViewport,
|
|
51
|
+
useViewportOrNull
|
|
52
|
+
} from "./chunk-IOKUV7FD.js";
|
|
42
53
|
import {
|
|
43
54
|
cn
|
|
44
55
|
} from "./chunk-ZAUUGK2Y.js";
|
|
@@ -339,60 +350,6 @@ function createElkEngine(elk, defaults = {}) {
|
|
|
339
350
|
};
|
|
340
351
|
}
|
|
341
352
|
|
|
342
|
-
// src/workflow/store/selectors.ts
|
|
343
|
-
import { useMemo as useMemo2, useSyncExternalStore } from "react";
|
|
344
|
-
function useFlowSelector(selector, isEqual = Object.is) {
|
|
345
|
-
void isEqual;
|
|
346
|
-
const store = useFlowStore();
|
|
347
|
-
return useSyncExternalStore(
|
|
348
|
-
store.subscribe,
|
|
349
|
-
() => selector(store.getSnapshot()),
|
|
350
|
-
() => selector(store.getSnapshot())
|
|
351
|
-
);
|
|
352
|
-
}
|
|
353
|
-
function useNodes() {
|
|
354
|
-
return useFlowSelector((s) => s.nodes);
|
|
355
|
-
}
|
|
356
|
-
function useEdges() {
|
|
357
|
-
return useFlowSelector((s) => s.edges);
|
|
358
|
-
}
|
|
359
|
-
function useViewport() {
|
|
360
|
-
return useFlowSelector((s) => s.viewport);
|
|
361
|
-
}
|
|
362
|
-
function useNodeById(id) {
|
|
363
|
-
return useFlowSelector((s) => s.nodes.find((n) => n.id === id));
|
|
364
|
-
}
|
|
365
|
-
function useNodeData(id) {
|
|
366
|
-
return useFlowSelector(
|
|
367
|
-
(s) => s.nodes.find((n) => n.id === id)?.data ?? void 0
|
|
368
|
-
);
|
|
369
|
-
}
|
|
370
|
-
function useEdgeById(id) {
|
|
371
|
-
return useFlowSelector((s) => s.edges.find((e) => e.id === id));
|
|
372
|
-
}
|
|
373
|
-
function useIsNodeSelected(id) {
|
|
374
|
-
return useFlowSelector((s) => s.selectedNodeIds.has(id));
|
|
375
|
-
}
|
|
376
|
-
function useIsEdgeSelected(id) {
|
|
377
|
-
return useFlowSelector((s) => s.selectedEdgeIds.has(id));
|
|
378
|
-
}
|
|
379
|
-
function useConnection() {
|
|
380
|
-
return useFlowSelector((s) => s.connection);
|
|
381
|
-
}
|
|
382
|
-
function useSelection() {
|
|
383
|
-
const nodes = useNodes();
|
|
384
|
-
const edges = useEdges();
|
|
385
|
-
const selectedNodeIds = useFlowSelector((s) => s.selectedNodeIds);
|
|
386
|
-
const selectedEdgeIds = useFlowSelector((s) => s.selectedEdgeIds);
|
|
387
|
-
return useMemo2(
|
|
388
|
-
() => ({
|
|
389
|
-
nodes: nodes.filter((n) => selectedNodeIds.has(n.id)),
|
|
390
|
-
edges: edges.filter((e) => selectedEdgeIds.has(e.id))
|
|
391
|
-
}),
|
|
392
|
-
[nodes, edges, selectedNodeIds, selectedEdgeIds]
|
|
393
|
-
);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
353
|
// src/workflow/components/ConfigPanel/ConfigPanel.tsx
|
|
397
354
|
import {
|
|
398
355
|
useCallback as useCallback3,
|
|
@@ -1024,6 +981,7 @@ export {
|
|
|
1024
981
|
useNodeData,
|
|
1025
982
|
useNodes,
|
|
1026
983
|
useSelection,
|
|
1027
|
-
useViewport
|
|
984
|
+
useViewport,
|
|
985
|
+
useViewportOrNull
|
|
1028
986
|
};
|
|
1029
987
|
//# sourceMappingURL=workflow.js.map
|
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/store/selectors.ts","../src/workflow/components/ConfigPanel/ConfigPanel.tsx","../src/workflow/components/NodeResizer/NodeResizer.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","// ════════════════════════════════════════════════════════════════════════\n// Narrow selector hooks — the only sanctioned way to read from the store.\n// Each hook does one thing and subscribes to the smallest possible slice\n// so re-renders are surgical.\n// ════════════════════════════════════════════════════════════════════════\n\nimport { useMemo, useSyncExternalStore } from \"react\";\nimport type {\n ConnectStartParams,\n FlowStoreSnapshot,\n Viewport,\n WorkflowEdge,\n WorkflowNode,\n} from \"../editor\";\nimport { useFlowStore } from \"./context\";\n\n/**\n * Generic selector hook. Use for one-off reads in tests or rare cases.\n * Public hooks below cover the common paths.\n *\n * NB: `selector` must be stable (referentially equal across renders) or\n * memoised — otherwise it re-subscribes every render. We do NOT do this\n * automatically because it would mask the bug.\n */\nexport function useFlowSelector<T>(\n selector: (snapshot: FlowStoreSnapshot) => T,\n isEqual: (a: T, b: T) => boolean = Object.is,\n): T {\n // Note: useSyncExternalStore uses Object.is internally for change\n // detection. The `isEqual` arg is reserved for a future patch that adds\n // structural equality (e.g. for array selectors); reference it so the\n // arg isn't dead.\n void isEqual;\n const store = useFlowStore();\n return useSyncExternalStore(\n store.subscribe,\n () => selector(store.getSnapshot()),\n () => selector(store.getSnapshot()),\n );\n}\n\n// ── Reads ──────────────────────────────────────────────────────────────\n\nexport function useNodes(): WorkflowNode[] {\n return useFlowSelector((s) => s.nodes);\n}\n\nexport function useEdges(): WorkflowEdge[] {\n return useFlowSelector((s) => s.edges);\n}\n\nexport function useViewport(): Viewport {\n return useFlowSelector((s) => s.viewport);\n}\n\nexport function useNodeById(id: string): WorkflowNode | undefined {\n return useFlowSelector((s) => s.nodes.find((n) => n.id === id));\n}\n\n/**\n * Subscribe to the `data` slice of a single node by id. Re-renders only\n * when this specific node's `data` reference changes — no cross-node\n * leakage. Pair with `FlowStateBag.updateNodeData(id, patch)` to write.\n *\n * Returns undefined when the node is not present; consumers should\n * branch on that. Typed via the generic so callers get their `TData`\n * shape back without casting.\n */\nexport function useNodeData<TData = unknown>(id: string): TData | undefined {\n return useFlowSelector(\n (s) => (s.nodes.find((n) => n.id === id)?.data as TData | undefined) ?? undefined,\n );\n}\n\nexport function useEdgeById(id: string): WorkflowEdge | undefined {\n return useFlowSelector((s) => s.edges.find((e) => e.id === id));\n}\n\nexport function useIsNodeSelected(id: string): boolean {\n return useFlowSelector((s) => s.selectedNodeIds.has(id));\n}\n\nexport function useIsEdgeSelected(id: string): boolean {\n return useFlowSelector((s) => s.selectedEdgeIds.has(id));\n}\n\nexport function useConnection(): ConnectStartParams | null {\n return useFlowSelector((s) => s.connection);\n}\n\nexport function useSelection(): { nodes: WorkflowNode[]; edges: WorkflowEdge[] } {\n // Subscribe to each underlying slice separately so useSyncExternalStore\n // sees stable references — building a fresh object inside the selector\n // would force a re-render every render (snapshot-cache violation).\n const nodes = useNodes();\n const edges = useEdges();\n const selectedNodeIds = useFlowSelector((s) => s.selectedNodeIds);\n const selectedEdgeIds = useFlowSelector((s) => s.selectedEdgeIds);\n return useMemo(\n () => ({\n nodes: nodes.filter((n) => selectedNodeIds.has(n.id)),\n edges: edges.filter((e) => selectedEdgeIds.has(e.id)),\n }),\n [nodes, edges, selectedNodeIds, selectedEdgeIds],\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. Double-click opens the inline editor. */\n title?: string;\n /** Optional breadcrumb prefix (e.g. \"Action\"). */\n kindLabel?: string;\n /** Optional icon rendered before the title. */\n icon?: 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. */\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 /** Resizable width — pass `resizable: false` to lock. */\n resizable?: boolean;\n /** Initial width (px). Defaults to 360. */\n defaultWidth?: number;\n /** Lower clamp during resize. Defaults to 280. */\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 /** Custom footer slot — replaces the default save/cancel buttons. */\n footer?: 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 kindLabel,\n icon,\n banner,\n tabs,\n activeTab: controlledActive,\n defaultActiveTab,\n onTabChange,\n saveState = \"clean\",\n onTitleChange,\n onClose,\n onSave,\n onCancel,\n resizable = true,\n defaultWidth = 360,\n minWidth = 280,\n maxWidth = 720,\n maxWidthContainerInset = 80,\n variant = \"pinned\",\n children,\n footer,\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 {kindLabel && <span className=\"ods-flow-config-panel__breadcrumb\">{kindLabel}</span>}\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 {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 className=\"ods-flow-config-panel__body\" role=\"tabpanel\">\n {tabs ? activeTab?.content : children}\n </div>\n\n <footer className=\"ods-flow-config-panel__footer\">\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 {footer ?? (\n <div className=\"ods-flow-config-panel__footer-actions\">\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\" || saveState === \"clean\"}\n >\n {saveState === \"saving\" ? \"Saving…\" : \"Save\"}\n </button>\n )}\n </div>\n )}\n </footer>\n </aside>\n );\n}\n","\"use client\";\n\n// ════════════════════════════════════════════════════════════════════════\n// NodeResizer — drag-to-resize handles attached to a node.\n// ════════════════════════════════════════════════════════════════════════\n// Dropped inside a custom node renderer. Four corner handles each adjust\n// width/height (and, where appropriate, position so the OPPOSITE corner\n// stays pinned). Width/height changes flow back through the imperative\n// instance via `updateNode`.\n//\n// Visible by default only when the host node is selected. Min/max sizes\n// + aspect-ratio lock keep the resize sensible. We deliberately do NOT\n// ship a `shouldResize` veto (yet) — that's a follow-on.\n\nimport { type CSSProperties, type PointerEvent, useRef } from \"react\";\nimport { cn } from \"../../../utils/cn\";\nimport { useFlow } from \"../../hooks/useFlow\";\nimport { useViewport } from \"../../store/selectors\";\nimport { DEFAULT_NODE_HEIGHT, DEFAULT_NODE_WIDTH } from \"../../utils/geometry\";\nimport { useFlowNodeContext } from \"../FlowNode/FlowNodeContext\";\n\nexport interface NodeResizerProps {\n /** Force-visible. Default: visible iff the host node is selected. */\n isVisible?: boolean;\n /** Lower bound for the resize. Defaults to 80×60. */\n minWidth?: number;\n minHeight?: number;\n /** Upper bound for the resize. Defaults unbounded. */\n maxWidth?: number;\n maxHeight?: number;\n /** Lock width:height ratio at the value computed from the node's\n * current dimensions on resize-start. */\n keepAspectRatio?: boolean;\n /** Fired on every pointermove during resize. */\n onResize?: (size: { width: number; height: number }) => void;\n /** Fired on pointerup (the committed size). */\n onResizeEnd?: (size: { width: number; height: number }) => void;\n /** Override handle colour. */\n color?: string;\n}\n\ntype Corner = \"nw\" | \"ne\" | \"sw\" | \"se\";\n\ninterface DragState {\n pointerId: number;\n corner: Corner;\n startClientX: number;\n startClientY: number;\n startWidth: number;\n startHeight: number;\n startX: number;\n startY: number;\n aspect: number; // w / h\n}\n\nexport function NodeResizer({\n isVisible,\n minWidth = 80,\n minHeight = 60,\n maxWidth,\n maxHeight,\n keepAspectRatio = false,\n onResize,\n onResizeEnd,\n color,\n}: NodeResizerProps) {\n const { node, selected } = useFlowNodeContext();\n const viewport = useViewport();\n const flow = useFlow();\n const dragRef = useRef<DragState | null>(null);\n\n const show = isVisible ?? selected;\n if (!show) return null;\n\n const beginResize = (e: PointerEvent<HTMLDivElement>, corner: Corner) => {\n e.preventDefault();\n e.stopPropagation();\n (e.target as HTMLElement).setPointerCapture(e.pointerId);\n const w = node.width ?? DEFAULT_NODE_WIDTH;\n const h = node.height ?? DEFAULT_NODE_HEIGHT;\n dragRef.current = {\n pointerId: e.pointerId,\n corner,\n startClientX: e.clientX,\n startClientY: e.clientY,\n startWidth: w,\n startHeight: h,\n startX: node.position.x,\n startY: node.position.y,\n aspect: w / Math.max(1, h),\n };\n };\n\n const onMove = (e: PointerEvent<HTMLDivElement>) => {\n const drag = dragRef.current;\n if (!drag || drag.pointerId !== e.pointerId) return;\n const dx = (e.clientX - drag.startClientX) / viewport.zoom;\n const dy = (e.clientY - drag.startClientY) / viewport.zoom;\n let nextW = drag.startWidth;\n let nextH = drag.startHeight;\n let nextX = drag.startX;\n let nextY = drag.startY;\n switch (drag.corner) {\n case \"se\":\n nextW = drag.startWidth + dx;\n nextH = drag.startHeight + dy;\n break;\n case \"sw\":\n nextW = drag.startWidth - dx;\n nextH = drag.startHeight + dy;\n nextX = drag.startX + dx;\n break;\n case \"ne\":\n nextW = drag.startWidth + dx;\n nextH = drag.startHeight - dy;\n nextY = drag.startY + dy;\n break;\n case \"nw\":\n nextW = drag.startWidth - dx;\n nextH = drag.startHeight - dy;\n nextX = drag.startX + dx;\n nextY = drag.startY + dy;\n break;\n }\n if (keepAspectRatio) {\n // Maintain the original ratio — fit height to width.\n nextH = nextW / drag.aspect;\n if (drag.corner === \"nw\" || drag.corner === \"ne\") {\n nextY = drag.startY + (drag.startHeight - nextH);\n }\n }\n nextW = Math.max(minWidth, maxWidth ? Math.min(maxWidth, nextW) : nextW);\n nextH = Math.max(minHeight, maxHeight ? Math.min(maxHeight, nextH) : nextH);\n flow.updateNode(node.id, {\n width: nextW,\n height: nextH,\n position: { x: nextX, y: nextY },\n });\n onResize?.({ width: nextW, height: nextH });\n };\n\n const onUp = (e: PointerEvent<HTMLDivElement>) => {\n if (dragRef.current?.pointerId === e.pointerId) {\n const cur = flow.getNode(node.id);\n if (cur) {\n onResizeEnd?.({\n width: cur.width ?? DEFAULT_NODE_WIDTH,\n height: cur.height ?? DEFAULT_NODE_HEIGHT,\n });\n }\n dragRef.current = null;\n }\n };\n\n const handleColor = color ?? \"var(--ods-accent)\";\n const handleStyle = (corner: Corner): CSSProperties => {\n const base: CSSProperties = {\n position: \"absolute\",\n width: 12,\n height: 12,\n background: \"var(--ods-surface-canvas)\",\n border: `2px solid ${handleColor}`,\n borderRadius: 2,\n cursor: cursorFor(corner),\n touchAction: \"none\",\n // Place each handle so its CENTRE sits on the corresponding corner.\n transform: \"translate(-50%, -50%)\",\n };\n switch (corner) {\n case \"nw\":\n return { ...base, top: 0, left: 0 };\n case \"ne\":\n return { ...base, top: 0, left: \"100%\" };\n case \"sw\":\n return { ...base, top: \"100%\", left: 0 };\n case \"se\":\n return { ...base, top: \"100%\", left: \"100%\" };\n }\n };\n\n return (\n <div className={cn(\"ods-node-resizer\")} data-flow-no-drag=\"true\">\n {([\"nw\", \"ne\", \"sw\", \"se\"] as Corner[]).map((corner) => (\n <div\n key={corner}\n style={handleStyle(corner)}\n onPointerDown={(e) => beginResize(e, corner)}\n onPointerMove={onMove}\n onPointerUp={onUp}\n onPointerCancel={onUp}\n aria-label={`Resize ${corner}`}\n />\n ))}\n </div>\n );\n}\n\nfunction cursorFor(corner: Corner): string {\n // Standard 8-direction resize cursors. We only ship corners for v1; the\n // edge handles (n/s/e/w) can come later.\n switch (corner) {\n case \"nw\":\n case \"se\":\n return \"nwse-resize\";\n case \"ne\":\n case \"sw\":\n return \"nesw-resize\";\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","useMemo","useMemo","useCallback","useEffect","useRef","useState","useState","useRef","useEffect","useCallback","useRef","jsx","useRef","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/NodeResizer/NodeResizer.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. Double-click opens the inline editor. */\n title?: string;\n /** Optional breadcrumb prefix (e.g. \"Action\"). */\n kindLabel?: string;\n /** Optional icon rendered before the title. */\n icon?: 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. */\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 /** Resizable width — pass `resizable: false` to lock. */\n resizable?: boolean;\n /** Initial width (px). Defaults to 360. */\n defaultWidth?: number;\n /** Lower clamp during resize. Defaults to 280. */\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 /** Custom footer slot — replaces the default save/cancel buttons. */\n footer?: 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 kindLabel,\n icon,\n banner,\n tabs,\n activeTab: controlledActive,\n defaultActiveTab,\n onTabChange,\n saveState = \"clean\",\n onTitleChange,\n onClose,\n onSave,\n onCancel,\n resizable = true,\n defaultWidth = 360,\n minWidth = 280,\n maxWidth = 720,\n maxWidthContainerInset = 80,\n variant = \"pinned\",\n children,\n footer,\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 {kindLabel && <span className=\"ods-flow-config-panel__breadcrumb\">{kindLabel}</span>}\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 {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 className=\"ods-flow-config-panel__body\" role=\"tabpanel\">\n {tabs ? activeTab?.content : children}\n </div>\n\n <footer className=\"ods-flow-config-panel__footer\">\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 {footer ?? (\n <div className=\"ods-flow-config-panel__footer-actions\">\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\" || saveState === \"clean\"}\n >\n {saveState === \"saving\" ? \"Saving…\" : \"Save\"}\n </button>\n )}\n </div>\n )}\n </footer>\n </aside>\n );\n}\n","\"use client\";\n\n// ════════════════════════════════════════════════════════════════════════\n// NodeResizer — drag-to-resize handles attached to a node.\n// ════════════════════════════════════════════════════════════════════════\n// Dropped inside a custom node renderer. Four corner handles each adjust\n// width/height (and, where appropriate, position so the OPPOSITE corner\n// stays pinned). Width/height changes flow back through the imperative\n// instance via `updateNode`.\n//\n// Visible by default only when the host node is selected. Min/max sizes\n// + aspect-ratio lock keep the resize sensible. We deliberately do NOT\n// ship a `shouldResize` veto (yet) — that's a follow-on.\n\nimport { type CSSProperties, type PointerEvent, useRef } from \"react\";\nimport { cn } from \"../../../utils/cn\";\nimport { useFlow } from \"../../hooks/useFlow\";\nimport { useViewport } from \"../../store/selectors\";\nimport { DEFAULT_NODE_HEIGHT, DEFAULT_NODE_WIDTH } from \"../../utils/geometry\";\nimport { useFlowNodeContext } from \"../FlowNode/FlowNodeContext\";\n\nexport interface NodeResizerProps {\n /** Force-visible. Default: visible iff the host node is selected. */\n isVisible?: boolean;\n /** Lower bound for the resize. Defaults to 80×60. */\n minWidth?: number;\n minHeight?: number;\n /** Upper bound for the resize. Defaults unbounded. */\n maxWidth?: number;\n maxHeight?: number;\n /** Lock width:height ratio at the value computed from the node's\n * current dimensions on resize-start. */\n keepAspectRatio?: boolean;\n /** Fired on every pointermove during resize. */\n onResize?: (size: { width: number; height: number }) => void;\n /** Fired on pointerup (the committed size). */\n onResizeEnd?: (size: { width: number; height: number }) => void;\n /** Override handle colour. */\n color?: string;\n}\n\ntype Corner = \"nw\" | \"ne\" | \"sw\" | \"se\";\n\ninterface DragState {\n pointerId: number;\n corner: Corner;\n startClientX: number;\n startClientY: number;\n startWidth: number;\n startHeight: number;\n startX: number;\n startY: number;\n aspect: number; // w / h\n}\n\nexport function NodeResizer({\n isVisible,\n minWidth = 80,\n minHeight = 60,\n maxWidth,\n maxHeight,\n keepAspectRatio = false,\n onResize,\n onResizeEnd,\n color,\n}: NodeResizerProps) {\n const { node, selected } = useFlowNodeContext();\n const viewport = useViewport();\n const flow = useFlow();\n const dragRef = useRef<DragState | null>(null);\n\n const show = isVisible ?? selected;\n if (!show) return null;\n\n const beginResize = (e: PointerEvent<HTMLDivElement>, corner: Corner) => {\n e.preventDefault();\n e.stopPropagation();\n (e.target as HTMLElement).setPointerCapture(e.pointerId);\n const w = node.width ?? DEFAULT_NODE_WIDTH;\n const h = node.height ?? DEFAULT_NODE_HEIGHT;\n dragRef.current = {\n pointerId: e.pointerId,\n corner,\n startClientX: e.clientX,\n startClientY: e.clientY,\n startWidth: w,\n startHeight: h,\n startX: node.position.x,\n startY: node.position.y,\n aspect: w / Math.max(1, h),\n };\n };\n\n const onMove = (e: PointerEvent<HTMLDivElement>) => {\n const drag = dragRef.current;\n if (!drag || drag.pointerId !== e.pointerId) return;\n const dx = (e.clientX - drag.startClientX) / viewport.zoom;\n const dy = (e.clientY - drag.startClientY) / viewport.zoom;\n let nextW = drag.startWidth;\n let nextH = drag.startHeight;\n let nextX = drag.startX;\n let nextY = drag.startY;\n switch (drag.corner) {\n case \"se\":\n nextW = drag.startWidth + dx;\n nextH = drag.startHeight + dy;\n break;\n case \"sw\":\n nextW = drag.startWidth - dx;\n nextH = drag.startHeight + dy;\n nextX = drag.startX + dx;\n break;\n case \"ne\":\n nextW = drag.startWidth + dx;\n nextH = drag.startHeight - dy;\n nextY = drag.startY + dy;\n break;\n case \"nw\":\n nextW = drag.startWidth - dx;\n nextH = drag.startHeight - dy;\n nextX = drag.startX + dx;\n nextY = drag.startY + dy;\n break;\n }\n if (keepAspectRatio) {\n // Maintain the original ratio — fit height to width.\n nextH = nextW / drag.aspect;\n if (drag.corner === \"nw\" || drag.corner === \"ne\") {\n nextY = drag.startY + (drag.startHeight - nextH);\n }\n }\n nextW = Math.max(minWidth, maxWidth ? Math.min(maxWidth, nextW) : nextW);\n nextH = Math.max(minHeight, maxHeight ? Math.min(maxHeight, nextH) : nextH);\n flow.updateNode(node.id, {\n width: nextW,\n height: nextH,\n position: { x: nextX, y: nextY },\n });\n onResize?.({ width: nextW, height: nextH });\n };\n\n const onUp = (e: PointerEvent<HTMLDivElement>) => {\n if (dragRef.current?.pointerId === e.pointerId) {\n const cur = flow.getNode(node.id);\n if (cur) {\n onResizeEnd?.({\n width: cur.width ?? DEFAULT_NODE_WIDTH,\n height: cur.height ?? DEFAULT_NODE_HEIGHT,\n });\n }\n dragRef.current = null;\n }\n };\n\n const handleColor = color ?? \"var(--ods-accent)\";\n const handleStyle = (corner: Corner): CSSProperties => {\n const base: CSSProperties = {\n position: \"absolute\",\n width: 12,\n height: 12,\n background: \"var(--ods-surface-canvas)\",\n border: `2px solid ${handleColor}`,\n borderRadius: 2,\n cursor: cursorFor(corner),\n touchAction: \"none\",\n // Place each handle so its CENTRE sits on the corresponding corner.\n transform: \"translate(-50%, -50%)\",\n };\n switch (corner) {\n case \"nw\":\n return { ...base, top: 0, left: 0 };\n case \"ne\":\n return { ...base, top: 0, left: \"100%\" };\n case \"sw\":\n return { ...base, top: \"100%\", left: 0 };\n case \"se\":\n return { ...base, top: \"100%\", left: \"100%\" };\n }\n };\n\n return (\n <div className={cn(\"ods-node-resizer\")} data-flow-no-drag=\"true\">\n {([\"nw\", \"ne\", \"sw\", \"se\"] as Corner[]).map((corner) => (\n <div\n key={corner}\n style={handleStyle(corner)}\n onPointerDown={(e) => beginResize(e, corner)}\n onPointerMove={onMove}\n onPointerUp={onUp}\n onPointerCancel={onUp}\n aria-label={`Resize ${corner}`}\n />\n ))}\n </div>\n );\n}\n\nfunction cursorFor(corner: Corner): string {\n // Standard 8-direction resize cursors. We only ship corners for v1; the\n // edge handles (n/s/e/w) can come later.\n switch (corner) {\n case \"nw\":\n case \"se\":\n return \"nwse-resize\";\n case \"ne\":\n case \"sw\":\n return \"nesw-resize\";\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","useRef","jsx","useRef","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.8",
|
|
4
4
|
"description": "OctaviaFlow Design System React components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -48,11 +48,15 @@
|
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@octaviaflow/config": "3.0.18",
|
|
50
50
|
"@octaviaflow/fonts": "3.0.18",
|
|
51
|
-
"@octaviaflow/icons": "3.0.18",
|
|
51
|
+
"@octaviaflow/icons": "3.0.18-beta.8",
|
|
52
52
|
"@octaviaflow/tokens": "3.0.18",
|
|
53
|
+
"@types/papaparse": "5.5.2",
|
|
53
54
|
"clsx": "^2.1.1",
|
|
55
|
+
"fast-xml-parser": "5.8.0",
|
|
54
56
|
"framer-motion": "^12.38.0",
|
|
55
|
-
"
|
|
57
|
+
"papaparse": "5.5.3",
|
|
58
|
+
"react-aria": "^3.48.0",
|
|
59
|
+
"yaml": "2.9.0"
|
|
56
60
|
},
|
|
57
61
|
"peerDependencies": {
|
|
58
62
|
"@monaco-editor/react": "^4.7.0",
|