@elabs-ai/components-flow 4.0.0 → 4.1.0
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/README.md +8 -8
- package/dist/index.d.ts +734 -20
- package/dist/index.js +976 -178
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
- package/src/canvas-shell/canvas-shell.tsx +116 -1
- package/src/canvas-shell/use-measured-nodes.ts +101 -0
- package/src/flow-button-edge/flow-button-edge.stories.tsx +13 -0
- package/src/flow-button-edge/flow-button-edge.tsx +8 -10
- package/src/flow-edge/flow-edge.stories.tsx +20 -0
- package/src/flow-edge/flow-edge.tsx +10 -3
- package/src/flow-edge-path/flow-edge-path.tsx +149 -0
- package/src/flow-edge-path/index.ts +1 -0
- package/src/flow-edge-path/no-raw-base-edge.test.ts +45 -0
- package/src/flow-floating-edge/flow-floating-edge.tsx +7 -3
- package/src/flow-group-node/flow-group-node.stories.tsx +1 -1
- package/src/flow-group-node/flow-group-node.tsx +24 -8
- package/src/flow-handle/flow-handle-anchor.test.tsx +97 -0
- package/src/flow-handle/flow-handle-anchor.ts +36 -0
- package/src/flow-handle/index.ts +1 -0
- package/src/flow-layout/flow-layout.stories.tsx +2 -2
- package/src/flow-layout/flow-layout.test.tsx +91 -0
- package/src/flow-layout/flow-layout.ts +77 -1
- package/src/flow-layout/layout-graph.test.ts +83 -2
- package/src/flow-layout/layout-graph.ts +23 -15
- package/src/flow-mini-map/flow-mini-map.stories.tsx +86 -0
- package/src/flow-node/flow-node.stories.tsx +151 -0
- package/src/flow-node/flow-node.tsx +56 -1
- package/src/flow-placeholder-node/flow-placeholder-node.tsx +5 -2
- package/src/flow-self-loop-edge/flow-self-loop-edge.stories.tsx +275 -0
- package/src/flow-self-loop-edge/flow-self-loop-edge.test.tsx +196 -0
- package/src/flow-self-loop-edge/flow-self-loop-edge.tsx +172 -0
- package/src/flow-self-loop-edge/index.ts +13 -0
- package/src/flow-self-loop-edge/self-loop-geometry.test.ts +128 -0
- package/src/flow-self-loop-edge/self-loop-geometry.ts +165 -0
- package/src/flow-smart-edge/flow-smart-edge.stories.tsx +55 -8
- package/src/flow-smart-edge/flow-smart-edge.tsx +125 -35
- package/src/flow-smart-edge/index.ts +5 -1
- package/src/flow-smart-edge/smart-edge-geometry.test.ts +88 -47
- package/src/flow-smart-edge/smart-edge-geometry.ts +69 -33
- package/src/flow-weighted-edge/back-edge-geometry.test.ts +54 -0
- package/src/flow-weighted-edge/back-edge-geometry.ts +60 -0
- package/src/flow-weighted-edge/edge-aria.test.ts +108 -0
- package/src/flow-weighted-edge/edge-aria.ts +117 -0
- package/src/flow-weighted-edge/edge-label-pill.test.tsx +65 -0
- package/src/flow-weighted-edge/edge-label-pill.tsx +82 -0
- package/src/flow-weighted-edge/flow-weighted-edge.stories.tsx +691 -0
- package/src/flow-weighted-edge/flow-weighted-edge.test.tsx +405 -0
- package/src/flow-weighted-edge/flow-weighted-edge.tsx +308 -0
- package/src/flow-weighted-edge/index.ts +18 -0
- package/src/flow-weighted-edge/weight-scale.test.ts +92 -0
- package/src/flow-weighted-edge/weight-scale.ts +86 -0
- package/src/index.ts +9 -0
- package/src/inspector-panel/inspector-panel.stories.tsx +1 -1
- package/src/inspector-panel/inspector-panel.test.tsx +20 -0
- package/src/inspector-panel/inspector-panel.tsx +13 -3
- package/src/legend/index.ts +7 -1
- package/src/legend/legend.stories.tsx +126 -0
- package/src/legend/legend.test.tsx +169 -0
- package/src/legend/legend.tsx +214 -3
- package/src/templates-flow-workspace.stories.tsx +1 -1
- package/src/testing/canvas-framing.test.ts +107 -0
- package/src/testing/canvas-framing.ts +396 -0
- package/src/testing/edge-anchors.ts +107 -0
- package/src/testing/index.ts +36 -0
- package/src/zoom-controls/zoom-controls.tsx +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/canvas-shell/canvas-shell.tsx","../src/helper-lines/helper-lines.tsx","../src/helper-lines/use-helper-lines.ts","../src/helper-lines/get-helper-lines.ts","../src/flow-node/flow-node.tsx","../src/flow-edge/flow-edge.tsx","../src/flow-smart-edge/flow-smart-edge.tsx","../src/flow-smart-edge/smart-edge-geometry.ts","../src/flow-floating-edge/flow-floating-edge.tsx","../src/flow-floating-edge/floating-edge-geometry.ts","../src/flow-placeholder-node/flow-placeholder-node.tsx","../src/flow-button-edge/flow-button-edge.tsx","../src/flow-layout/flow-layout.ts","../src/flow-layout/use-flow-layout.ts","../src/flow-layout/layout-graph.ts","../src/flow-layout/use-auto-layout.ts","../src/flow-mini-map/flow-mini-map.tsx","../src/inspector-panel/inspector-panel.tsx","../src/legend/legend.tsx","../src/zoom-controls/zoom-controls.tsx","../src/flow-group-node/flow-group-node.tsx","../src/use-flow-groups/use-flow-groups.ts","../src/use-flow-groups/group-operations.ts","../src/index.ts"],"sourcesContent":["import { type ReactNode } from \"react\";\nimport {\n Background,\n ReactFlow,\n ReactFlowProvider,\n type AriaLabelConfig,\n type Edge,\n type Node,\n type ReactFlowProps,\n} from \"@xyflow/react\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport { HelperLines } from \"../helper-lines/helper-lines\";\nimport { useHelperLines } from \"../helper-lines/use-helper-lines\";\n\nexport interface CanvasShellProps<\n NodeType extends Node = Node,\n EdgeType extends Edge = Edge,\n> extends ReactFlowProps<NodeType, EdgeType> {\n /** Render a branded dotted background grid. Defaults to true. */\n background?: boolean;\n /**\n * Enable alignment guides + snapping while a single node is dragged. Off by\n * default; when false, CanvasShell behaves exactly as before. When true,\n * CanvasShell wires `useHelperLines` around your `onNodesChange` and renders\n * the `<HelperLines>` overlay internally — pass `nodes` + `onNodesChange`\n * (the standard controlled setup, e.g. via `useNodesState`). For full control\n * use the exported `useHelperLines` + `<HelperLines>` directly.\n */\n helperLines?: boolean;\n /** Overlays rendered inside the flow (ZoomControls, Legend, Panels). */\n children?: ReactNode;\n className?: string;\n}\n\ntype CanvasShellInnerProps<NodeType extends Node, EdgeType extends Edge> = Omit<\n CanvasShellProps<NodeType, EdgeType>,\n \"helperLines\"\n>;\n\n/**\n * Branded defaults for React Flow's `ariaLabelConfig` (12.7+) — sentence-case\n * labels that match the rest of brand-ui's voice (see `ZoomControls`'\n * \"Zoom in\"/\"Zoom out\"/\"Fit view\"), instead of the library's Title Case\n * defaults. Callers can override any key; unset keys keep these defaults, and\n * React Flow itself fills in any key neither side sets.\n */\nconst DEFAULT_ARIA_LABEL_CONFIG: Partial<AriaLabelConfig> = {\n \"node.a11yDescription.default\":\n \"Press Enter or Space to select this node. Press Delete to remove it, Escape to cancel.\",\n \"node.a11yDescription.keyboardDisabled\":\n \"Press Enter or Space to select this node. Then use the arrow keys to move it. Press Delete to remove it, Escape to cancel.\",\n \"edge.a11yDescription.default\":\n \"Press Enter or Space to select this edge. Press Delete to remove it, Escape to cancel.\",\n \"controls.ariaLabel\": \"Canvas controls\",\n \"controls.zoomIn.ariaLabel\": \"Zoom in\",\n \"controls.zoomOut.ariaLabel\": \"Zoom out\",\n \"controls.fitView.ariaLabel\": \"Fit view\",\n \"controls.interactive.ariaLabel\": \"Toggle interactivity\",\n \"minimap.ariaLabel\": \"Minimap\",\n \"handle.ariaLabel\": \"Connection handle\",\n};\n\n/**\n * Branded React Flow canvas. Sets a token-driven background + sensible\n * defaults; pass nodes/edges/handlers through as normal React Flow props.\n *\n * Consumers MUST import the React Flow stylesheet once at the app root:\n * import \"@xyflow/react/dist/style.css\";\n *\n * Children render inside the flow context, so <ZoomControls /> and React Flow\n * <Panel>s work when placed here.\n */\nexport function CanvasShell<NodeType extends Node = Node, EdgeType extends Edge = Edge>({\n helperLines = false,\n ...props\n}: CanvasShellProps<NodeType, EdgeType>) {\n if (helperLines) {\n // A provider is needed so `useHelperLines` can read the live node store; the\n // ReactFlow rendered below connects to this same provider.\n return (\n <ReactFlowProvider>\n <CanvasShellWithHelperLines<NodeType, EdgeType> {...props} />\n </ReactFlowProvider>\n );\n }\n return <CanvasShellBase<NodeType, EdgeType> {...props} />;\n}\n\nfunction CanvasShellBase<NodeType extends Node, EdgeType extends Edge>({\n background = true,\n children,\n className,\n ariaLabelConfig,\n ...props\n}: CanvasShellInnerProps<NodeType, EdgeType>) {\n return (\n <div className={cn(\"h-full w-full bg-canvas\", className)}>\n <ReactFlow\n fitView\n proOptions={{ hideAttribution: true }}\n ariaLabelConfig={{ ...DEFAULT_ARIA_LABEL_CONFIG, ...ariaLabelConfig }}\n {...props}\n >\n {background ? <Background gap={20} size={1} color=\"var(--canvas-grid)\" /> : null}\n {children}\n </ReactFlow>\n </div>\n );\n}\n\nfunction CanvasShellWithHelperLines<NodeType extends Node, EdgeType extends Edge>({\n onNodesChange,\n children,\n ...props\n}: CanvasShellInnerProps<NodeType, EdgeType>) {\n const {\n onNodesChange: wrappedOnNodesChange,\n helperLineHorizontal,\n helperLineVertical,\n } = useHelperLines<NodeType>(onNodesChange);\n\n return (\n <CanvasShellBase<NodeType, EdgeType> {...props} onNodesChange={wrappedOnNodesChange}>\n {children}\n <HelperLines horizontal={helperLineHorizontal} vertical={helperLineVertical} />\n </CanvasShellBase>\n );\n}\n","\"use client\";\n\nimport { forwardRef, type SVGAttributes } from \"react\";\nimport { useViewport } from \"@xyflow/react\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\n\nexport interface HelperLinesProps extends SVGAttributes<SVGSVGElement> {\n /** Absolute flow-y of the horizontal guide (from `useHelperLines`). */\n horizontal?: number;\n /** Absolute flow-x of the vertical guide (from `useHelperLines`). */\n vertical?: number;\n}\n\n/**\n * Decorative alignment-guide overlay. Draws a full-width horizontal line at\n * `horizontal` and/or a full-height vertical line at `vertical`, coloured with\n * the `--flow-helper-line` token. It reads the live viewport transform\n * (zoom + pan) so the guides sit on the real alignment position when the canvas\n * is zoomed or panned.\n *\n * Render as a child of `<CanvasShell>` / `<ReactFlow>` (it must be inside the\n * flow context to read the viewport). Purely decorative — hidden from AT.\n */\nexport const HelperLines = forwardRef<SVGSVGElement, HelperLinesProps>(function HelperLines(\n { horizontal, vertical, className, ...props },\n ref,\n) {\n const { x, y, zoom } = useViewport();\n\n if (horizontal == null && vertical == null) return null;\n\n // flow coordinate → pane pixel: coord * zoom + pan offset.\n const screenY = horizontal == null ? null : y + horizontal * zoom;\n const screenX = vertical == null ? null : x + vertical * zoom;\n\n return (\n <svg\n ref={ref}\n aria-hidden=\"true\"\n width=\"100%\"\n height=\"100%\"\n className={cn(\"pointer-events-none absolute inset-0 size-full\", className)}\n {...props}\n >\n {screenY != null ? (\n <line\n x1={0}\n x2=\"100%\"\n y1={screenY}\n y2={screenY}\n stroke=\"var(--flow-helper-line, var(--primary))\"\n strokeWidth={1}\n />\n ) : null}\n {screenX != null ? (\n <line\n x1={screenX}\n x2={screenX}\n y1={0}\n y2=\"100%\"\n stroke=\"var(--flow-helper-line, var(--primary))\"\n strokeWidth={1}\n />\n ) : null}\n </svg>\n );\n});\n","\"use client\";\n\nimport { useCallback, useState } from \"react\";\nimport { useReactFlow, type Node, type NodeChange } from \"@xyflow/react\";\nimport { getHelperLines, type HelperLineRect } from \"./get-helper-lines\";\n\nexport interface UseHelperLinesOptions {\n /** Alignment threshold in flow coordinates. Defaults to 5. */\n threshold?: number;\n}\n\nexport interface UseHelperLinesResult<NodeType extends Node = Node> {\n /**\n * Drop-in replacement for the consumer's `onNodesChange`: it snaps a dragged\n * node onto any near-aligned edge/center, then forwards the (mutated) changes\n * to the wrapped handler.\n */\n onNodesChange: (changes: NodeChange<NodeType>[]) => void;\n /** Absolute flow-y of the active horizontal guide, or `undefined`. */\n helperLineHorizontal: number | undefined;\n /** Absolute flow-x of the active vertical guide, or `undefined`. */\n helperLineVertical: number | undefined;\n}\n\nfunction nodeRect(node: Node): HelperLineRect {\n return {\n x: node.position.x,\n y: node.position.y,\n width: node.measured?.width ?? node.width ?? 0,\n height: node.measured?.height ?? node.height ?? 0,\n };\n}\n\n/**\n * Decorate a consumer's `onNodesChange` with alignment guides + snapping.\n *\n * While a **single** node is dragged, the returned handler compares its\n * left/center/right and top/middle/bottom against every other node; when a pair\n * is within `threshold` flow px it (a) snaps the dragged node's position change\n * onto the guide and (b) exposes the guide coordinates so `<HelperLines>` can\n * draw them. Guides clear when the drag ends or nothing aligns.\n *\n * Must be called inside a React Flow context (a `<ReactFlowProvider>` or a\n * component rendered under `<ReactFlow>`), since it reads the live node store.\n *\n * @example\n * const [nodes, , onNodesChange] = useNodesState(initialNodes);\n * const { onNodesChange: handle, helperLineHorizontal, helperLineVertical } =\n * useHelperLines(onNodesChange);\n * // <ReactFlow onNodesChange={handle}>\n * // <HelperLines horizontal={helperLineHorizontal} vertical={helperLineVertical} />\n */\nexport function useHelperLines<NodeType extends Node = Node>(\n onNodesChange?: (changes: NodeChange<NodeType>[]) => void,\n options: UseHelperLinesOptions = {},\n): UseHelperLinesResult<NodeType> {\n const { threshold = 5 } = options;\n const { getNodes } = useReactFlow<NodeType>();\n const [helperLineHorizontal, setHelperLineHorizontal] = useState<number>();\n const [helperLineVertical, setHelperLineVertical] = useState<number>();\n\n const handleNodesChange = useCallback(\n (changes: NodeChange<NodeType>[]) => {\n let horizontal: number | undefined;\n let vertical: number | undefined;\n\n const dragChanges = changes.filter(\n (c): c is Extract<NodeChange<NodeType>, { type: \"position\" }> =>\n c.type === \"position\" && c.dragging === true && c.position != null,\n );\n\n // Only guide/snap a single dragged node — multi-select drags are ambiguous.\n if (dragChanges.length === 1) {\n const change = dragChanges[0];\n const nodes = getNodes();\n const dragged = change ? nodes.find((n) => n.id === change.id) : undefined;\n\n if (change && change.position && dragged) {\n const draggedRect: HelperLineRect = {\n x: change.position.x,\n y: change.position.y,\n width: dragged.measured?.width ?? dragged.width ?? 0,\n height: dragged.measured?.height ?? dragged.height ?? 0,\n };\n const others = nodes.filter((n) => n.id !== change.id).map(nodeRect);\n const {\n snapX,\n snapY,\n vertical: v,\n horizontal: h,\n } = getHelperLines(draggedRect, others, threshold);\n\n // Mutate the position change so the node lands exactly on the guide.\n if (snapX !== undefined) change.position.x = snapX;\n if (snapY !== undefined) change.position.y = snapY;\n vertical = v;\n horizontal = h;\n }\n }\n\n setHelperLineHorizontal(horizontal);\n setHelperLineVertical(vertical);\n onNodesChange?.(changes);\n },\n [getNodes, onNodesChange, threshold],\n );\n\n return { onNodesChange: handleNodesChange, helperLineHorizontal, helperLineVertical };\n}\n","/**\n * Pure alignment-guide geometry for the helper-lines feature.\n *\n * Implemented fresh from the documented \"helper lines\" behaviour (the React Flow\n * Pro example is paid — only the concept is reused): while a single node drags,\n * compare its left / horizontal-center / right against every other node's\n * left / center / right (and top / middle / bottom on the vertical axis). When a\n * pair falls within `threshold` flow-coordinate pixels, record the guide's\n * absolute coordinate and the snapped top-left position for the dragged node.\n *\n * Everything here is a pure function of plain rectangles so it is unit-testable\n * without a live React Flow instance.\n */\n\n/** A node's bounding box in absolute flow coordinates (top-left origin). */\nexport interface HelperLineRect {\n /** Left edge (top-left x) in flow coordinates. */\n x: number;\n /** Top edge (top-left y) in flow coordinates. */\n y: number;\n width: number;\n height: number;\n}\n\nexport interface HelperLinesResult {\n /** New top-left x for the dragged node once snapped to the vertical guide. */\n snapX?: number;\n /** New top-left y for the dragged node once snapped to the horizontal guide. */\n snapY?: number;\n /** Absolute flow-x of the active vertical guide line, if any. */\n vertical?: number;\n /** Absolute flow-y of the active horizontal guide line, if any. */\n horizontal?: number;\n}\n\n/**\n * Fractional anchor offsets along an axis: 0 = start edge (left/top),\n * 0.5 = center/middle, 1 = end edge (right/bottom). Comparing all three anchors\n * of the dragged node against all three of every other node yields\n * edge-to-edge, edge-to-center and center-to-center alignment.\n */\nconst ANCHORS = [0, 0.5, 1] as const;\n\n/**\n * Compute the closest vertical + horizontal alignment guides (and the snapped\n * position) for a dragged rectangle against the other rectangles.\n *\n * @param dragged the dragged node's proposed bounding box (flow coordinates)\n * @param others every other node's bounding box\n * @param threshold max distance (flow px) at which alignment engages; a pair at\n * exactly `threshold` does NOT match (strict boundary)\n */\nexport function getHelperLines(\n dragged: HelperLineRect,\n others: Iterable<HelperLineRect>,\n threshold = 5,\n): HelperLinesResult {\n const result: HelperLinesResult = {};\n // Track the closest match on each axis; initialising to `threshold` makes the\n // boundary strict (a distance equal to threshold never wins).\n let bestVerticalDist = threshold;\n let bestHorizontalDist = threshold;\n\n // Each anchor as { a: fractional offset, pos: absolute coordinate }.\n const draggedX = ANCHORS.map((a) => ({ a, pos: dragged.x + dragged.width * a }));\n const draggedY = ANCHORS.map((a) => ({ a, pos: dragged.y + dragged.height * a }));\n\n for (const other of others) {\n const otherX = ANCHORS.map((a) => other.x + other.width * a);\n const otherY = ANCHORS.map((a) => other.y + other.height * a);\n\n // Vertical guide (align on the X axis).\n for (const d of draggedX) {\n for (const oPos of otherX) {\n const dx = Math.abs(d.pos - oPos);\n if (dx < bestVerticalDist) {\n bestVerticalDist = dx;\n result.vertical = oPos;\n result.snapX = oPos - dragged.width * d.a;\n }\n }\n }\n\n // Horizontal guide (align on the Y axis).\n for (const d of draggedY) {\n for (const oPos of otherY) {\n const dy = Math.abs(d.pos - oPos);\n if (dy < bestHorizontalDist) {\n bestHorizontalDist = dy;\n result.horizontal = oPos;\n result.snapY = oPos - dragged.height * d.a;\n }\n }\n }\n }\n\n return result;\n}\n","import { type ReactNode } from \"react\";\nimport { Handle, Position, type Node, type NodeProps } from \"@xyflow/react\";\nimport { Star, type LucideIcon } from \"lucide-react\";\nimport { STATUS_TONE_ICONS } from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\n\n/** A node side that can carry a handle. Doubles as the handle's stable id. */\nexport type FlowHandleSide = \"top\" | \"right\" | \"bottom\" | \"left\";\n\n/**\n * Which sides of a node expose source and/or target handles. When omitted,\n * `FlowNode` keeps its default single top **target** + bottom **source**. When\n * set, a `<Handle>` is rendered on each listed side with a **stable id equal to\n * the side name** (`\"top\" | \"right\" | \"bottom\" | \"left\"`), addressable per\n * handle type — so edges (e.g. `FlowSmartEdge`) can pick a specific anchor.\n */\nexport interface FlowNodeHandles {\n /** Sides exposing a source handle (handle `id` === side). */\n source?: FlowHandleSide[];\n /** Sides exposing a target handle (handle `id` === side). */\n target?: FlowHandleSide[];\n}\n\n/**\n * A `FlowNodeHandles` config exposing source **and** target anchors on all four\n * sides. The recommended setup for `FlowSmartEdge` / `FlowFloatingEdge`: with\n * anchors on every side, an edge connects on whichever side **faces** the other\n * node (left/right when side-by-side, top/bottom when stacked) instead of always\n * top/bottom. Spread onto a node's `data.handles`.\n */\nexport const FLOW_ALL_SIDE_HANDLES: FlowNodeHandles = {\n source: [\"top\", \"right\", \"bottom\", \"left\"],\n target: [\"top\", \"right\", \"bottom\", \"left\"],\n};\n\nexport interface FlowNodeData extends Record<string, unknown> {\n title: string;\n subtitle?: string;\n /** Short type label shown as an eyebrow, e.g. \"Source\", \"Transform\". */\n kind?: string;\n icon?: ReactNode;\n tone?: \"default\" | \"accent\" | \"success\" | \"warning\" | \"destructive\";\n /**\n * Optional multi-side handle configuration. Absent → default top-target /\n * bottom-source (unchanged, backward-compatible).\n */\n handles?: FlowNodeHandles;\n}\n\nexport type BrandFlowNode = Node<FlowNodeData, \"brand\">;\n\nconst toneRing: Record<NonNullable<FlowNodeData[\"tone\"]>, string> = {\n default: \"border-border\",\n accent: \"border-primary\",\n success: \"border-success\",\n warning: \"border-warning\",\n destructive: \"border-destructive\",\n};\n\n/**\n * `tone` used to be encoded in colour ALONE — a 1px border and nothing else\n * (WCAG 1.4.1, #387). Every non-default tone now also gets a leading Lucide\n * glyph (`aria-hidden`, decorative — the sr-only text below carries the\n * meaning) and a distinct accessible name. `success`/`warning`/`destructive`\n * reuse `@elabs-ai/components-ui`'s `STATUS_TONE_ICONS` (the SAME glyph\n * `StatusBadge`/`StatusIcon` already pair with that tone) rather than\n * inventing a second icon vocabulary; `accent` has no canonical `StatusTone`\n * counterpart to reuse (the closed `StatusTone` enum is deliberately\n * CALM-ONLY and excludes a \"primary/highlighted\" bucket — see\n * `status-badge.tsx`'s docblock), so it gets the one net-new pairing.\n * `default` keeps neither — reaching for a glyph/label on every ordinary node\n * would be visual and assistive-tech noise on the common case.\n */\nconst toneIcon: Partial<Record<NonNullable<FlowNodeData[\"tone\"]>, LucideIcon>> = {\n accent: Star,\n success: STATUS_TONE_ICONS.success,\n warning: STATUS_TONE_ICONS.warning,\n destructive: STATUS_TONE_ICONS.destructive,\n};\n\n/** Ink for the tone glyph — the coloured-text rung (`-text`), not the fill rung. */\nconst toneIconColor: Partial<Record<NonNullable<FlowNodeData[\"tone\"]>, string>> = {\n accent: \"text-primary\",\n success: \"text-success-text\",\n warning: \"text-warning-text\",\n destructive: \"text-destructive-text\",\n};\n\n/** The accessible name the tone glyph stands in for (`sr-only`, #387). */\nconst toneLabel: Partial<Record<NonNullable<FlowNodeData[\"tone\"]>, string>> = {\n accent: \"Highlighted\",\n success: \"Success\",\n warning: \"Warning\",\n destructive: \"Destructive\",\n};\n\nconst sidePosition: Record<FlowHandleSide, Position> = {\n top: Position.Top,\n right: Position.Right,\n bottom: Position.Bottom,\n left: Position.Left,\n};\n\nconst handleClassName = \"!size-2 !border-2 !border-flow-edge !bg-flow-node\";\n\n/**\n * Branded custom node. Register it in `nodeTypes={{ brand: FlowNode }}` and\n * create nodes with `type: \"brand\"` and `data: FlowNodeData`.\n */\nexport function FlowNode({\n data,\n selected,\n sourcePosition,\n targetPosition,\n}: NodeProps<BrandFlowNode>) {\n const tone = data.tone ?? \"default\";\n const ToneIcon = toneIcon[tone];\n return (\n <div\n data-tone={tone}\n className={cn(\n \"min-w-44 rounded-lg border bg-flow-node px-3 py-2 text-flow-node-foreground shadow-sm transition-[box-shadow,border-color] duration-fast ease-standard\",\n toneRing[tone],\n selected && \"ring-2 ring-ring\",\n )}\n >\n {data.handles ? (\n <>\n {(data.handles.target ?? []).map((side) => (\n <Handle\n key={`target-${side}`}\n id={side}\n type=\"target\"\n position={sidePosition[side]}\n className={handleClassName}\n />\n ))}\n {(data.handles.source ?? []).map((side) => (\n <Handle\n key={`source-${side}`}\n id={side}\n type=\"source\"\n position={sidePosition[side]}\n className={handleClassName}\n />\n ))}\n </>\n ) : (\n <>\n {/* Default single target + source. Positions follow the layout\n direction: React Flow passes `sourcePosition`/`targetPosition`\n (which `layoutFlow` sets per direction), so an LR layout puts the\n target on the LEFT and the source on the RIGHT. Falls back to\n top-in / bottom-out when unset. Handle placement is by `position`,\n not DOM order. */}\n <Handle\n type=\"target\"\n position={targetPosition ?? Position.Top}\n className={handleClassName}\n />\n <Handle\n type=\"source\"\n position={sourcePosition ?? Position.Bottom}\n className={handleClassName}\n />\n </>\n )}\n <div className=\"flex items-center gap-2\">\n {data.icon ? (\n <span className=\"[&_svg]:size-4 text-muted-foreground\">{data.icon}</span>\n ) : null}\n <div className=\"min-w-0 flex-1\">\n {data.kind ? (\n <div className=\"text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n {data.kind}\n </div>\n ) : null}\n <div className=\"truncate text-sm font-medium\">{data.title}</div>\n {data.subtitle ? (\n <div className=\"truncate text-xs text-muted-foreground\">{data.subtitle}</div>\n ) : null}\n </div>\n {ToneIcon ? (\n <ToneIcon aria-hidden=\"true\" className={cn(\"size-3.5 shrink-0\", toneIconColor[tone])} />\n ) : null}\n </div>\n {toneLabel[tone] ? <span className=\"sr-only\">{toneLabel[tone]}</span> : null}\n </div>\n );\n}\n","import { BaseEdge, getBezierPath, type EdgeProps } from \"@xyflow/react\";\n\n/**\n * Branded bezier edge using the `--flow-edge` token. Register it in\n * `edgeTypes={{ brand: FlowEdge }}` and create edges with `type: \"brand\"`.\n */\nexport function FlowEdge({\n id,\n sourceX,\n sourceY,\n targetX,\n targetY,\n sourcePosition,\n targetPosition,\n markerEnd,\n style,\n}: EdgeProps) {\n const [edgePath] = getBezierPath({\n sourceX,\n sourceY,\n targetX,\n targetY,\n sourcePosition,\n targetPosition,\n });\n return (\n <BaseEdge\n id={id}\n path={edgePath}\n markerEnd={markerEnd}\n style={{ stroke: \"var(--flow-edge)\", strokeWidth: 1.5, ...style }}\n />\n );\n}\n","import {\n BaseEdge,\n getBezierPath,\n useInternalNode,\n type EdgeProps,\n type InternalNode,\n} from \"@xyflow/react\";\nimport type { FlowNodeData } from \"../flow-node\";\nimport {\n pickClosestHandles,\n rectCenter,\n sideToPosition,\n slideAnchor,\n type NodeRect,\n} from \"./smart-edge-geometry\";\n\nfunction toRect(node: InternalNode): NodeRect {\n return {\n x: node.internals.positionAbsolute.x,\n y: node.internals.positionAbsolute.y,\n width: node.measured.width ?? 0,\n height: node.measured.height ?? 0,\n };\n}\n\n/**\n * Branded edge that picks the closest source/target handle pair (from a node's\n * multi-side `FlowNodeHandles` config) and routes a bezier between them. Anchors\n * are recomputed every render, so they flip as nodes are dragged. Register it in\n * `edgeTypes={{ smart: FlowSmartEdge }}` and give the connected nodes a\n * `data.handles` config. Uses the `--flow-edge` token, matching `FlowEdge`.\n */\nexport function FlowSmartEdge({ id, source, target, markerEnd, style }: EdgeProps) {\n const sourceNode = useInternalNode(source);\n const targetNode = useInternalNode(target);\n if (!sourceNode || !targetNode) return null;\n\n const sourceData = sourceNode.internals.userNode.data as FlowNodeData | undefined;\n const targetData = targetNode.internals.userNode.data as FlowNodeData | undefined;\n\n const sourceRect = toRect(sourceNode);\n const targetRect = toRect(targetNode);\n\n // Pick which side of each node the edge exits/enters (the facing handle side).\n const { sourceSide, targetSide } = pickClosestHandles(\n sourceRect,\n sourceData?.handles?.source ?? [],\n targetRect,\n targetData?.handles?.target ?? [],\n );\n\n // Then slide each anchor along its chosen side toward the OTHER node, so\n // edges sharing a side fan out to distinct, target-facing points instead of\n // stacking on the side midpoint.\n const sourceAnchor = slideAnchor(sourceRect, sourceSide, rectCenter(targetRect));\n const targetAnchor = slideAnchor(targetRect, targetSide, rectCenter(sourceRect));\n\n const [edgePath] = getBezierPath({\n sourceX: sourceAnchor.x,\n sourceY: sourceAnchor.y,\n sourcePosition: sideToPosition[sourceSide],\n targetX: targetAnchor.x,\n targetY: targetAnchor.y,\n targetPosition: sideToPosition[targetSide],\n });\n\n return (\n <BaseEdge\n id={id}\n path={edgePath}\n markerEnd={markerEnd}\n style={{ stroke: \"var(--flow-edge)\", strokeWidth: 1.5, ...style }}\n />\n );\n}\n","import { Position } from \"@xyflow/react\";\nimport type { FlowHandleSide } from \"../flow-node\";\n\n/** Axis-aligned node rectangle in absolute flow coordinates. */\nexport interface NodeRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/** All four candidate handle sides, in a stable order. */\nexport const HANDLE_SIDES: FlowHandleSide[] = [\"top\", \"right\", \"bottom\", \"left\"];\n\n/** Maps a handle side to the React Flow `Position` used for bezier control. */\nexport const sideToPosition: Record<FlowHandleSide, Position> = {\n top: Position.Top,\n right: Position.Right,\n bottom: Position.Bottom,\n left: Position.Left,\n};\n\n/** Center point of a node rectangle. */\nexport function rectCenter(rect: NodeRect): { x: number; y: number } {\n return { x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 };\n}\n\n/**\n * The connection point on `side` of `rect`, slid along that side toward\n * `toward` and clamped `inset` px from the corners. This is what stops two\n * edges that both leave the same side (e.g. both targets sit to the right)\n * from piling onto the side's midpoint: each edge's anchor slides toward its\n * own target, so an up-going edge exits the upper part of the side and a\n * down-going edge the lower part — each visibly meeting the node on the side\n * that faces its target instead of stacking in the middle.\n */\nexport function slideAnchor(\n rect: NodeRect,\n side: FlowHandleSide,\n toward: { x: number; y: number },\n inset = 12,\n): { x: number; y: number } {\n const clamp = (v: number, lo: number, hi: number) => Math.max(lo, Math.min(hi, v));\n // Never let the inset invert the usable range on a small node.\n const ix = Math.min(inset, rect.width / 2);\n const iy = Math.min(inset, rect.height / 2);\n switch (side) {\n case \"top\":\n return { x: clamp(toward.x, rect.x + ix, rect.x + rect.width - ix), y: rect.y };\n case \"bottom\":\n return { x: clamp(toward.x, rect.x + ix, rect.x + rect.width - ix), y: rect.y + rect.height };\n case \"left\":\n return { x: rect.x, y: clamp(toward.y, rect.y + iy, rect.y + rect.height - iy) };\n case \"right\":\n return { x: rect.x + rect.width, y: clamp(toward.y, rect.y + iy, rect.y + rect.height - iy) };\n }\n}\n\n/** Absolute coordinate of a handle on the given side (the side's midpoint). */\nexport function handlePoint(rect: NodeRect, side: FlowHandleSide): { x: number; y: number } {\n switch (side) {\n case \"top\":\n return { x: rect.x + rect.width / 2, y: rect.y };\n case \"bottom\":\n return { x: rect.x + rect.width / 2, y: rect.y + rect.height };\n case \"left\":\n return { x: rect.x, y: rect.y + rect.height / 2 };\n case \"right\":\n return { x: rect.x + rect.width, y: rect.y + rect.height / 2 };\n }\n}\n\n/** The chosen source/target handle pair plus their absolute anchor points. */\nexport interface ClosestHandles {\n sourceSide: FlowHandleSide;\n targetSide: FlowHandleSide;\n sx: number;\n sy: number;\n tx: number;\n ty: number;\n}\n\n/**\n * Picks the source/target handle pair (one handle per node) with the shortest\n * straight-line distance between their anchor points. Empty side lists fall\n * back to all four sides so an edge always resolves to a pair.\n */\nexport function pickClosestHandles(\n source: NodeRect,\n sourceSides: FlowHandleSide[],\n target: NodeRect,\n targetSides: FlowHandleSide[],\n): ClosestHandles {\n const srcSides = sourceSides.length ? sourceSides : HANDLE_SIDES;\n const tgtSides = targetSides.length ? targetSides : HANDLE_SIDES;\n\n let best: ClosestHandles | undefined;\n let bestDist = Infinity;\n\n for (const s of srcSides) {\n const sp = handlePoint(source, s);\n for (const t of tgtSides) {\n const tp = handlePoint(target, t);\n const dx = tp.x - sp.x;\n const dy = tp.y - sp.y;\n const dist = dx * dx + dy * dy;\n if (dist < bestDist) {\n bestDist = dist;\n best = { sourceSide: s, targetSide: t, sx: sp.x, sy: sp.y, tx: tp.x, ty: tp.y };\n }\n }\n }\n\n // srcSides/tgtSides are non-empty (fallback above), so best is always set.\n return best as ClosestHandles;\n}\n","import { BaseEdge, getBezierPath, useInternalNode, type EdgeProps } from \"@xyflow/react\";\nimport { getEdgeParams } from \"./floating-edge-geometry\";\n\n/** Optional per-edge `data` for {@link FlowFloatingEdge}. */\nexport interface FloatingEdgeData {\n /**\n * Show a small anchor dot at each border connection point. On by default so the\n * edge visibly terminates on the node's closest side (rather than a bare line\n * touching the border with no anchor). Set `false` to hide.\n */\n anchors?: boolean;\n [key: string]: unknown;\n}\n\n/** Radius of the connection anchor dot (matches the `FlowNode` handle size). */\nconst ANCHOR_RADIUS = 4;\n\n/**\n * Branded floating edge: it attaches to the node **border** at the point facing\n * the other node (no fixed handle), recomputed as nodes drag. Register it in\n * `edgeTypes={{ floating: FlowFloatingEdge }}`; the connected nodes need no\n * handle config. Uses the `--flow-edge` token, matching `FlowEdge`.\n *\n * A small **anchor dot** is drawn at each connection point (on by default) so the\n * line clearly terminates on the node's closest border side — not at a bare,\n * unanchored spot. Toggle per edge with `data.anchors: false`.\n */\nexport function FlowFloatingEdge({ id, source, target, markerEnd, style, data }: EdgeProps) {\n const sourceNode = useInternalNode(source);\n const targetNode = useInternalNode(target);\n if (!sourceNode || !targetNode) return null;\n\n const { sx, sy, tx, ty, sourcePos, targetPos } = getEdgeParams(sourceNode, targetNode);\n\n const [edgePath] = getBezierPath({\n sourceX: sx,\n sourceY: sy,\n sourcePosition: sourcePos,\n targetX: tx,\n targetY: ty,\n targetPosition: targetPos,\n });\n\n const showAnchors = (data as FloatingEdgeData | undefined)?.anchors !== false;\n\n return (\n <>\n <BaseEdge\n id={id}\n path={edgePath}\n markerEnd={markerEnd}\n style={{ stroke: \"var(--flow-edge)\", strokeWidth: 1.5, ...style }}\n />\n {showAnchors ? (\n <g\n className=\"brand-floating-edge__anchors\"\n fill=\"var(--flow-node)\"\n stroke=\"var(--flow-edge)\"\n >\n {/* Decorative rings matching the FlowNode handle look; never intercept\n pointer events so nodes stay draggable through them. */}\n <circle\n cx={sx}\n cy={sy}\n r={ANCHOR_RADIUS}\n strokeWidth={2}\n style={{ pointerEvents: \"none\" }}\n aria-hidden=\"true\"\n />\n <circle\n cx={tx}\n cy={ty}\n r={ANCHOR_RADIUS}\n strokeWidth={2}\n style={{ pointerEvents: \"none\" }}\n aria-hidden=\"true\"\n />\n </g>\n ) : null}\n </>\n );\n}\n","import { Position } from \"@xyflow/react\";\n\n/**\n * The minimal live node geometry `getEdgeParams` needs. A React Flow\n * `InternalNode` satisfies this structurally (`internals.positionAbsolute` +\n * `measured`), so you can pass one directly; tests can pass a plain fake.\n */\nexport interface FloatingNodeGeometry {\n internals: { positionAbsolute: { x: number; y: number } };\n measured: { width?: number; height?: number };\n}\n\ninterface Point {\n x: number;\n y: number;\n}\n\n/**\n * The point where the line from `intersectionNode`'s center to `targetNode`'s\n * center crosses `intersectionNode`'s rectangle border. Standard React Flow\n * floating-edge math (ellipse-normalized).\n */\nfunction getNodeIntersection(\n intersectionNode: FloatingNodeGeometry,\n targetNode: FloatingNodeGeometry,\n): Point {\n const w = (intersectionNode.measured.width ?? 0) / 2;\n const h = (intersectionNode.measured.height ?? 0) / 2;\n const pos = intersectionNode.internals.positionAbsolute;\n const targetPos = targetNode.internals.positionAbsolute;\n\n const x2 = pos.x + w;\n const y2 = pos.y + h;\n const x1 = targetPos.x + (targetNode.measured.width ?? 0) / 2;\n const y1 = targetPos.y + (targetNode.measured.height ?? 0) / 2;\n\n // A not-yet-measured node has w/h = 0; guard the divisions so we return the\n // node's position (finite) instead of NaN (which would break the edge path and\n // make the line appear to end at a random spot until measurement lands).\n const safeW = w || 1;\n const safeH = h || 1;\n\n const xx1 = (x1 - x2) / (2 * safeW) - (y1 - y2) / (2 * safeH);\n const yy1 = (x1 - x2) / (2 * safeW) + (y1 - y2) / (2 * safeH);\n const a = 1 / (Math.abs(xx1) + Math.abs(yy1) || 1);\n const xx3 = a * xx1;\n const yy3 = a * yy1;\n const x = safeW * (xx3 + yy3) + x2;\n const y = safeH * (-xx3 + yy3) + y2;\n\n return { x, y };\n}\n\n/**\n * Snap `point` exactly onto `node`'s nearest border side. The intersection math\n * already lands on the border for a measured node; this guarantees it (removing\n * any sub-pixel drift that would leave the anchor a hair inside the node) and\n * gives a sensible on-border fallback if a point ever lands inside. Returns the\n * point unchanged when the node has no measured size yet.\n */\nfunction clampToBorder(point: Point, node: FloatingNodeGeometry): Point {\n const x0 = node.internals.positionAbsolute.x;\n const y0 = node.internals.positionAbsolute.y;\n const w = node.measured.width ?? 0;\n const h = node.measured.height ?? 0;\n if (w <= 0 || h <= 0) return point;\n\n const cx = Math.min(Math.max(point.x, x0), x0 + w);\n const cy = Math.min(Math.max(point.y, y0), y0 + h);\n // Distance from the (clamped-inside) point to each side; snap to the nearest.\n const dLeft = cx - x0;\n const dRight = x0 + w - cx;\n const dTop = cy - y0;\n const dBottom = y0 + h - cy;\n const nearest = Math.min(dLeft, dRight, dTop, dBottom);\n if (nearest === dLeft) return { x: x0, y: cy };\n if (nearest === dRight) return { x: x0 + w, y: cy };\n if (nearest === dTop) return { x: cx, y: y0 };\n return { x: cx, y: y0 + h };\n}\n\n/** Which border of `node` the intersection `point` sits on. */\nfunction getEdgeBorderPosition(node: FloatingNodeGeometry, point: Point): Position {\n const nx = Math.round(node.internals.positionAbsolute.x);\n const ny = Math.round(node.internals.positionAbsolute.y);\n const px = Math.round(point.x);\n const py = Math.round(point.y);\n const w = node.measured.width ?? 0;\n const h = node.measured.height ?? 0;\n\n if (px <= nx + 1) return Position.Left;\n if (px >= nx + w - 1) return Position.Right;\n if (py <= ny + 1) return Position.Top;\n if (py >= ny + h - 1) return Position.Bottom;\n return Position.Top;\n}\n\n/** Border anchor points + `Position`s for a floating edge between two nodes. */\nexport interface EdgeParams {\n sx: number;\n sy: number;\n tx: number;\n ty: number;\n sourcePos: Position;\n targetPos: Position;\n}\n\n/**\n * Computes where a floating edge attaches to each node's border — the\n * intersection of the center-to-center line with each rectangle — plus the\n * border `Position`. Recompute per render so anchors track dragging nodes.\n */\nexport function getEdgeParams(\n source: FloatingNodeGeometry,\n target: FloatingNodeGeometry,\n): EdgeParams {\n const sourceIntersection = clampToBorder(getNodeIntersection(source, target), source);\n const targetIntersection = clampToBorder(getNodeIntersection(target, source), target);\n\n return {\n sx: sourceIntersection.x,\n sy: sourceIntersection.y,\n tx: targetIntersection.x,\n ty: targetIntersection.y,\n sourcePos: getEdgeBorderPosition(source, sourceIntersection),\n targetPos: getEdgeBorderPosition(target, targetIntersection),\n };\n}\n","import { Handle, Position, type Node, type NodeProps } from \"@xyflow/react\";\nimport { Plus } from \"lucide-react\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\n\nexport interface FlowPlaceholderNodeData extends Record<string, unknown> {\n /** Label rendered inside the placeholder. @default \"Add node\" */\n label?: string;\n /** Called when the placeholder is activated (click, Enter, or Space). */\n onActivate?: () => void;\n}\n\nexport type BrandFlowPlaceholderNode = Node<FlowPlaceholderNodeData, \"placeholder\">;\n\n/**\n * Dashed, muted \"add here\" node — a clickable affordance that grows the\n * graph. Register it in `nodeTypes={{ placeholder: FlowPlaceholderNode }}`\n * and create nodes with `type: \"placeholder\"` and `data: FlowPlaceholderNodeData`.\n *\n * Renders a real `<button>` (keyboard-activatable via native Enter/Space) with\n * an `aria-label`, and a single **target** `<Handle>` (top) so an existing\n * edge can point at it. Fires `data.onActivate?.()` on activation — the\n * typical handler converts the placeholder into a real `FlowNode` and grows a\n * fresh placeholder beneath it (see the \"Placeholder tail\" story).\n */\nexport function FlowPlaceholderNode({ data }: NodeProps<BrandFlowPlaceholderNode>) {\n const label = data.label ?? \"Add node\";\n return (\n <div className=\"relative\">\n <Handle\n type=\"target\"\n position={Position.Top}\n className=\"!size-2 !border-2 !border-flow-edge !bg-flow-node\"\n />\n <button\n type=\"button\"\n aria-label={label}\n onClick={() => data.onActivate?.()}\n className={cn(\n \"flex min-w-44 items-center justify-center gap-1.5 rounded-lg border border-dashed border-flow-group-border bg-flow-group px-3 py-2 text-muted-foreground\",\n \"transition-colors duration-fast ease-standard hover:bg-accent hover:text-accent-foreground\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n )}\n >\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n <span className=\"text-body\">{label}</span>\n </button>\n </div>\n );\n}\n","import {\n BaseEdge,\n EdgeLabelRenderer,\n getBezierPath,\n type Edge,\n type EdgeProps,\n} from \"@xyflow/react\";\nimport { Plus } from \"lucide-react\";\n\nexport interface FlowButtonEdgeData extends Record<string, unknown> {\n /** aria-label for the insert button. @default \"Insert node on edge\" */\n label?: string;\n /** Called when the edge's \"+\" button is activated (click, Enter, or Space). */\n onInsert?: () => void;\n}\n\nexport type BrandFlowButtonEdge = Edge<FlowButtonEdgeData, \"button\">;\n\n/**\n * Branded bezier edge (matching `FlowEdge`'s `--flow-edge` token) with a\n * centered \"+\" button rendered via `EdgeLabelRenderer` at the edge midpoint.\n * Register it in `edgeTypes={{ button: FlowButtonEdge }}` and create edges\n * with `type: \"button\"` and `data: FlowButtonEdgeData`.\n *\n * The button is a real `<button>` with an `aria-label`, keyboard-activatable\n * via native Enter/Space. Fires `data.onInsert?.()` on activation — the\n * typical handler splits the edge: insert a new node between source and\n * target and rewire the two edges (see the \"Insert between\" story).\n */\nexport function FlowButtonEdge({\n id,\n sourceX,\n sourceY,\n targetX,\n targetY,\n sourcePosition,\n targetPosition,\n markerEnd,\n style,\n data,\n}: EdgeProps<BrandFlowButtonEdge>) {\n const [edgePath, labelX, labelY] = getBezierPath({\n sourceX,\n sourceY,\n sourcePosition,\n targetX,\n targetY,\n targetPosition,\n });\n const label = data?.label ?? \"Insert node on edge\";\n\n return (\n <>\n <BaseEdge\n id={id}\n path={edgePath}\n markerEnd={markerEnd}\n style={{ stroke: \"var(--flow-edge)\", strokeWidth: 1.5, ...style }}\n />\n <EdgeLabelRenderer>\n <div\n style={{\n position: \"absolute\",\n transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,\n }}\n className=\"nodrag nopan pointer-events-auto\"\n >\n <button\n type=\"button\"\n aria-label={label}\n onClick={() => data?.onInsert?.()}\n className=\"flex size-5 items-center justify-center rounded-full border border-flow-group-border bg-flow-node text-flow-node-foreground shadow-sm transition-colors duration-fast ease-standard hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <Plus className=\"size-3\" aria-hidden=\"true\" />\n </button>\n </div>\n </EdgeLabelRenderer>\n </>\n );\n}\n","import dagre from \"@dagrejs/dagre\";\nimport { Position } from \"@xyflow/react\";\nimport type { Edge, Node } from \"@xyflow/react\";\n\n/** Direction dagre lays the graph out in — top-to-bottom, left-to-right, etc. */\nexport type FlowLayoutDirection = \"TB\" | \"LR\" | \"BT\" | \"RL\";\n\n/**\n * The handle sides an edge should attach to for each layout direction. A\n * directional layout must move the anchors with it: a left-to-right flow exits\n * the source's RIGHT and enters the target's LEFT; top-to-bottom uses\n * bottom/top. `layoutFlow` stamps these onto every node so `FlowNode` renders\n * its default handles on the correct sides (and `FlowEdge` routes accordingly)\n * — otherwise every layout is stuck with top/bottom handles.\n */\nexport const HANDLE_BY_DIRECTION: Record<\n FlowLayoutDirection,\n { source: Position; target: Position }\n> = {\n TB: { source: Position.Bottom, target: Position.Top },\n BT: { source: Position.Top, target: Position.Bottom },\n LR: { source: Position.Right, target: Position.Left },\n RL: { source: Position.Left, target: Position.Right },\n};\n\nexport interface FlowLayoutOptions {\n /** Layout direction. @default \"TB\" */\n direction?: FlowLayoutDirection;\n /** Gap between nodes in the same rank (dagre `nodesep`). @default 48 */\n nodeSpacing?: number;\n /** Gap between ranks (dagre `ranksep`). @default 72 */\n rankSpacing?: number;\n}\n\nexport interface FlowLayoutResult<NodeType extends Node = Node, EdgeType extends Edge = Edge> {\n nodes: NodeType[];\n edges: EdgeType[];\n}\n\n/** Fallback size used when a node hasn't been measured yet (React Flow's own default node width). */\nconst DEFAULT_NODE_WIDTH = 172;\nconst DEFAULT_NODE_HEIGHT = 40;\n\nfunction nodeSize(node: Node): { width: number; height: number } {\n return {\n width: node.measured?.width ?? node.width ?? DEFAULT_NODE_WIDTH,\n height: node.measured?.height ?? node.height ?? DEFAULT_NODE_HEIGHT,\n };\n}\n\n/**\n * Pure dagre-powered auto layout. Computes new `position`s for `nodes` given\n * `edges` — no React, no side effects, safe to call anywhere (including\n * outside a component or in a test).\n *\n * Respects each node's **measured** size (`node.measured` — how React Flow\n * v12 reports actual rendered dimensions), falling back to `node.width`/\n * `node.height`, then a sensible default. Node identity and `data` are left\n * untouched — only `position` changes.\n *\n * Pair with `useFlowLayout` to apply the result to a live canvas.\n */\nexport function layoutFlow<NodeType extends Node = Node, EdgeType extends Edge = Edge>(\n nodes: NodeType[],\n edges: EdgeType[],\n options: FlowLayoutOptions = {},\n): FlowLayoutResult<NodeType, EdgeType> {\n const { direction = \"TB\", nodeSpacing = 48, rankSpacing = 72 } = options;\n\n const graph = new dagre.graphlib.Graph();\n graph.setDefaultEdgeLabel(() => ({}));\n graph.setGraph({ rankdir: direction, nodesep: nodeSpacing, ranksep: rankSpacing });\n\n for (const node of nodes) {\n const { width, height } = nodeSize(node);\n graph.setNode(node.id, { width, height });\n }\n\n for (const edge of edges) {\n graph.setEdge(edge.source, edge.target);\n }\n\n dagre.layout(graph);\n\n const handles = HANDLE_BY_DIRECTION[direction];\n const layoutedNodes = nodes.map((node) => {\n const dagreNode = graph.node(node.id);\n const { width, height } = nodeSize(node);\n // dagre positions nodes by their center; React Flow positions by top-left.\n return {\n ...node,\n // Move the anchors with the layout direction so `FlowNode` puts its\n // handles on the sides the edges actually leave/enter.\n sourcePosition: handles.source,\n targetPosition: handles.target,\n position: {\n x: dagreNode.x - width / 2,\n y: dagreNode.y - height / 2,\n },\n };\n });\n\n return { nodes: layoutedNodes, edges };\n}\n","import { useCallback, useRef, useState } from \"react\";\nimport { useNodesInitialized, useReactFlow, type Edge, type Node } from \"@xyflow/react\";\nimport { layoutFlow, type FlowLayoutDirection, type FlowLayoutOptions } from \"./flow-layout\";\n\nexport interface UseFlowLayoutResult {\n /**\n * Auto-lay-out the current nodes/edges with dagre and fit the view. Safe to\n * call repeatedly (e.g. from a toolbar button on every click). No-ops until\n * React Flow has measured every node.\n */\n layout: (direction?: FlowLayoutDirection, options?: Omit<FlowLayoutOptions, \"direction\">) => void;\n /** Whether nodes are measured and ready to be laid out. */\n ready: boolean;\n /** Whether a layout pass is currently running (true only for the duration of `layout()`). */\n layouting: boolean;\n}\n\n/**\n * Hook form of `layoutFlow`. Reads the live nodes/edges from `useReactFlow()`,\n * waits for `useNodesInitialized()` so real (measured) node sizes are used —\n * never lays out against 0×0 unmeasured nodes — applies the computed\n * positions via `setNodes`, then calls `fitView()`.\n *\n * Must be called from a component rendered inside a `<CanvasShell>` /\n * `<ReactFlow>` (React Flow context).\n */\nexport function useFlowLayout<\n NodeType extends Node = Node,\n EdgeType extends Edge = Edge,\n>(): UseFlowLayoutResult {\n const { getNodes, getEdges, setNodes, fitView } = useReactFlow<NodeType, EdgeType>();\n const nodesInitialized = useNodesInitialized();\n const [layouting, setLayouting] = useState(false);\n // Avoid overlapping layout passes if `layout()` is invoked in quick succession.\n const runningRef = useRef(false);\n\n const layout = useCallback(\n (direction?: FlowLayoutDirection, options?: Omit<FlowLayoutOptions, \"direction\">) => {\n if (!nodesInitialized || runningRef.current) return;\n\n runningRef.current = true;\n setLayouting(true);\n try {\n const { nodes: layoutedNodes } = layoutFlow<NodeType, EdgeType>(getNodes(), getEdges(), {\n ...options,\n direction,\n });\n setNodes(layoutedNodes);\n // Defer fitView one frame so React Flow has committed the new positions first.\n requestAnimationFrame(() => fitView());\n } finally {\n runningRef.current = false;\n setLayouting(false);\n }\n },\n [nodesInitialized, getNodes, getEdges, setNodes, fitView],\n );\n\n return { layout, ready: nodesInitialized, layouting };\n}\n","import {\n forceCenter,\n forceCollide,\n forceLink,\n forceManyBody,\n forceSimulation,\n type SimulationLinkDatum,\n type SimulationNodeDatum,\n} from \"d3-force\";\nimport type { Edge, Node } from \"@xyflow/react\";\nimport { HANDLE_BY_DIRECTION, layoutFlow } from \"./flow-layout\";\n\n/** Which generic graph-geometry algorithm `layoutGraph` should run. */\nexport type LayoutAlgorithm = \"concentric\" | \"force\" | \"layered-lr\" | \"grid\";\n\n/** Horizontal/vertical gap used by the `\"grid\"` and `\"layered-lr\"` algorithms. */\nexport interface LayoutSpacing {\n x: number;\n y: number;\n}\n\nexport interface LayoutOptions {\n /** Which layout algorithm to run. */\n algorithm: LayoutAlgorithm;\n /**\n * `\"concentric\"` only — the focal node placed at the origin. Defaults to the\n * highest-degree node when omitted (or when the id doesn't match a node).\n */\n centerId?: string;\n /** `\"concentric\"` only — radius of ring 1; ring N sits at `N × ringRadius`. @default 180 */\n ringRadius?: number;\n /** `\"force\"` only — number of synchronous simulation ticks to run. @default 300 */\n iterations?: number;\n /** `\"grid\"` / `\"layered-lr\"` — gap between nodes. @default {x:200,y:120} for grid. */\n spacing?: LayoutSpacing;\n /** Resolve a node's size by id. Falls back to `measured`/`width`/`height`/a sensible default. */\n nodeSize?: (id: string) => { width: number; height: number };\n}\n\nconst DEFAULT_NODE_WIDTH = 172;\nconst DEFAULT_NODE_HEIGHT = 40;\nconst DEFAULT_RING_RADIUS = 180;\nconst DEFAULT_FORCE_ITERATIONS = 300;\nconst DEFAULT_GRID_SPACING: LayoutSpacing = { x: 200, y: 120 };\n/** Base charge/link/collide tuning — chosen to read well at brand-ui's default node size. */\nconst FORCE_CHARGE_STRENGTH = -300;\nconst FORCE_LINK_DISTANCE = 150;\n\nfunction resolveNodeSize(\n node: Node,\n nodeSize?: (id: string) => { width: number; height: number },\n): { width: number; height: number } {\n if (nodeSize) return nodeSize(node.id);\n return {\n width: node.measured?.width ?? node.width ?? DEFAULT_NODE_WIDTH,\n height: node.measured?.height ?? node.height ?? DEFAULT_NODE_HEIGHT,\n };\n}\n\n/**\n * Pure graph-geometry auto layout. Computes new `position`s for `nodes` given\n * `edges` and a `LayoutAlgorithm` — no React, no DOM, no side effects, safe to\n * call anywhere (including outside a component or in a test). Deterministic:\n * identical `nodes`/`edges`/`options` always produce identical positions.\n *\n * Node identity and `data` are left untouched — only `position` changes.\n * Pair with `useAutoLayout` for a memoized hook form.\n *\n * - `\"layered-lr\"` delegates to the dagre-powered `layoutFlow` (direction `\"LR\"`).\n * - `\"concentric\"` places `centerId` (or the highest-degree node) at the\n * origin, with BFS shells at `ring × ringRadius`; disconnected nodes land in\n * one extra outer ring so positions are never `NaN`.\n * - `\"force\"` runs `d3-force` synchronously for a fixed number of `iterations`,\n * seeded on a circle by index (no randomness) so runs are reproducible.\n * - `\"grid\"` lays nodes out row-major using `spacing`.\n */\nexport function layoutGraph<NodeType extends Node = Node, EdgeType extends Edge = Edge>(\n nodes: NodeType[],\n edges: EdgeType[],\n options: LayoutOptions,\n): NodeType[] {\n if (nodes.length === 0) return [];\n\n switch (options.algorithm) {\n case \"layered-lr\":\n return layoutLayeredLr(nodes, edges, options);\n case \"concentric\":\n return layoutConcentric(nodes, edges, options);\n case \"force\":\n return layoutForce(nodes, edges, options);\n case \"grid\":\n return layoutGrid(nodes, options);\n default: {\n const exhaustiveCheck: never = options.algorithm;\n throw new Error(`layoutGraph: unknown algorithm \"${String(exhaustiveCheck)}\"`);\n }\n }\n}\n\nfunction layoutLayeredLr<NodeType extends Node, EdgeType extends Edge>(\n nodes: NodeType[],\n edges: EdgeType[],\n options: LayoutOptions,\n): NodeType[] {\n const { spacing, nodeSize } = options;\n\n // dagre (via layoutFlow) reads sizes off `measured`/`width`/`height` — feed it\n // resolved sizes on a throwaway copy so a caller-supplied `nodeSize` is honored\n // without mutating (or permanently resizing) the nodes we return.\n const sizedNodes = nodeSize\n ? nodes.map((node) => {\n const { width, height } = nodeSize(node.id);\n return { ...node, width, height, measured: { width, height } };\n })\n : nodes;\n\n const { nodes: laidOut } = layoutFlow(sizedNodes, edges, {\n direction: \"LR\",\n // For an LR flow, ranks progress horizontally (x) and nodes within a rank\n // stack vertically (y) — map spacing accordingly onto dagre's rank/node sep.\n nodeSpacing: spacing?.y,\n rankSpacing: spacing?.x,\n });\n\n // Carry the LR handle sides (right-out / left-in) that layoutFlow stamped on,\n // not just the position — otherwise the layout flips positions but leaves the\n // anchors on top/bottom.\n return nodes.map((node, i) => ({\n ...node,\n sourcePosition: laidOut[i]!.sourcePosition,\n targetPosition: laidOut[i]!.targetPosition,\n position: laidOut[i]!.position,\n }));\n}\n\nfunction resolveCenterId(\n centerId: string | undefined,\n nodeIds: string[],\n adjacency: Map<string, Set<string>>,\n): string {\n if (centerId && nodeIds.includes(centerId)) return centerId;\n\n let best = nodeIds[0]!;\n let bestDegree = -1;\n for (const id of nodeIds) {\n const degree = adjacency.get(id)?.size ?? 0;\n if (degree > bestDegree) {\n bestDegree = degree;\n best = id;\n }\n }\n return best;\n}\n\nfunction layoutConcentric<NodeType extends Node, EdgeType extends Edge>(\n nodes: NodeType[],\n edges: EdgeType[],\n options: LayoutOptions,\n): NodeType[] {\n const ringRadius = options.ringRadius ?? DEFAULT_RING_RADIUS;\n const nodeIds = nodes.map((node) => node.id);\n const idSet = new Set(nodeIds);\n\n const adjacency = new Map<string, Set<string>>();\n for (const id of nodeIds) adjacency.set(id, new Set());\n for (const edge of edges) {\n if (!idSet.has(edge.source) || !idSet.has(edge.target)) continue;\n adjacency.get(edge.source)!.add(edge.target);\n adjacency.get(edge.target)!.add(edge.source);\n }\n\n const centerId = resolveCenterId(options.centerId, nodeIds, adjacency);\n\n // BFS shells from the center — hop distance doubles as the ring index.\n const hopOf = new Map<string, number>();\n hopOf.set(centerId, 0);\n const queue: string[] = [centerId];\n let maxHop = 0;\n while (queue.length > 0) {\n const current = queue.shift()!;\n const currentHop = hopOf.get(current)!;\n for (const neighbor of adjacency.get(current) ?? []) {\n if (hopOf.has(neighbor)) continue;\n const hop = currentHop + 1;\n hopOf.set(neighbor, hop);\n maxHop = Math.max(maxHop, hop);\n queue.push(neighbor);\n }\n }\n\n // Disconnected nodes (unreachable from the center) get one extra ring beyond\n // the farthest reached hop — never left with a NaN/undefined position.\n const leftoverHop = maxHop + 1;\n for (const id of nodeIds) {\n if (!hopOf.has(id)) hopOf.set(id, leftoverHop);\n }\n\n const rings = new Map<number, string[]>();\n for (const id of nodeIds) {\n const hop = hopOf.get(id)!;\n if (hop === 0) continue;\n if (!rings.has(hop)) rings.set(hop, []);\n rings.get(hop)!.push(id);\n }\n\n const positionById = new Map<string, { x: number; y: number }>();\n positionById.set(centerId, { x: 0, y: 0 });\n\n for (const [hop, ids] of rings) {\n const count = ids.length;\n const angleStep = (2 * Math.PI) / count;\n // Stagger successive rings by a growing half-step so radial edges don't\n // trivially line up with the ring behind/ahead of them.\n const ringOffset = (hop - 1) * (angleStep / 2);\n const radius = hop * ringRadius;\n ids.forEach((id, i) => {\n const angle = ringOffset + i * angleStep;\n positionById.set(id, { x: radius * Math.cos(angle), y: radius * Math.sin(angle) });\n });\n }\n\n return nodes.map((node) => ({\n ...node,\n position: positionById.get(node.id) ?? { x: 0, y: 0 },\n }));\n}\n\ninterface ForceSimNode extends SimulationNodeDatum {\n id: string;\n}\n\nfunction layoutForce<NodeType extends Node, EdgeType extends Edge>(\n nodes: NodeType[],\n edges: EdgeType[],\n options: LayoutOptions,\n): NodeType[] {\n const iterations = options.iterations ?? DEFAULT_FORCE_ITERATIONS;\n const nodeIds = new Set(nodes.map((node) => node.id));\n\n // Seed initial positions deterministically on a circle by index — never\n // `Math.random()` — so two runs with the same input converge identically.\n const seedRadius = Math.max(nodes.length * 20, 100);\n const simNodes: ForceSimNode[] = nodes.map((node, i) => {\n const angle = (2 * Math.PI * i) / Math.max(nodes.length, 1);\n return { id: node.id, x: seedRadius * Math.cos(angle), y: seedRadius * Math.sin(angle) };\n });\n\n const simLinks: SimulationLinkDatum<ForceSimNode>[] = edges\n .filter((edge) => nodeIds.has(edge.source) && nodeIds.has(edge.target))\n .map((edge) => ({ source: edge.source, target: edge.target }));\n\n const collisionById = new Map(\n nodes.map((node) => {\n const { width, height } = resolveNodeSize(node, options.nodeSize);\n return [node.id, Math.max(width, height) / 2];\n }),\n );\n\n const simulation = forceSimulation(simNodes)\n .force(\"charge\", forceManyBody().strength(FORCE_CHARGE_STRENGTH))\n .force(\n \"link\",\n forceLink<ForceSimNode, SimulationLinkDatum<ForceSimNode>>(simLinks)\n .id((d) => d.id)\n .distance(FORCE_LINK_DISTANCE),\n )\n .force(\"center\", forceCenter(0, 0))\n .force(\n \"collide\",\n forceCollide<ForceSimNode>((d) => collisionById.get(d.id) ?? DEFAULT_NODE_WIDTH / 2),\n )\n .stop();\n\n for (let i = 0; i < iterations; i++) {\n simulation.tick();\n }\n\n const positionById = new Map(simNodes.map((n) => [n.id, { x: n.x ?? 0, y: n.y ?? 0 }]));\n\n return nodes.map((node) => ({\n ...node,\n position: positionById.get(node.id) ?? { x: 0, y: 0 },\n }));\n}\n\nfunction layoutGrid<NodeType extends Node>(nodes: NodeType[], options: LayoutOptions): NodeType[] {\n const spacing = options.spacing ?? DEFAULT_GRID_SPACING;\n const cols = Math.max(1, Math.ceil(Math.sqrt(nodes.length)));\n // A grid reads top-to-bottom; anchor edges bottom-out / top-in to match.\n const handles = HANDLE_BY_DIRECTION.TB;\n\n return nodes.map((node, i) => {\n const row = Math.floor(i / cols);\n const col = i % cols;\n return {\n ...node,\n sourcePosition: handles.source,\n targetPosition: handles.target,\n position: { x: col * spacing.x, y: row * spacing.y },\n };\n });\n}\n","import { useMemo } from \"react\";\nimport type { Edge, Node } from \"@xyflow/react\";\nimport { layoutGraph, type LayoutOptions } from \"./layout-graph\";\n\n/**\n * Memoized `layoutGraph` — pure graph-geometry layout (no live canvas access,\n * no side effects). Recomputes only when `nodes`, `edges`, or `options`\n * change; pass stable references (e.g. from `useNodesState`/`useEdgesState`\n * and a memoized `options` object) to avoid recomputing every render.\n *\n * Use this when you want laid-out nodes to render with (e.g. feeding\n * `useNodesState`'s initial value, or a derived/read-only view). To apply a\n * layout to a *live* React Flow instance (read current nodes, set them back,\n * fit the view), call `layoutGraph` directly from an event handler instead —\n * see the `LayoutGraph` story.\n */\nexport function useAutoLayout<NodeType extends Node = Node, EdgeType extends Edge = Edge>(\n nodes: NodeType[],\n edges: EdgeType[],\n options: LayoutOptions,\n): NodeType[] {\n return useMemo(\n () => layoutGraph<NodeType, EdgeType>(nodes, edges, options),\n [nodes, edges, options],\n );\n}\n","import { MiniMap, type MiniMapProps, type Node } from \"@xyflow/react\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\n\nexport type FlowMiniMapProps<NodeType extends Node = Node> = MiniMapProps<NodeType>;\n\n/**\n * Branded preset over React Flow's `<MiniMap>` — sources node/mask colors from\n * the `--flow-minimap-*` tokens so the overview stays theme-aware (the raw\n * `MiniMap` re-export defaults to library colors and renders theme-blind,\n * especially under a low-chroma theme). Every prop can still be overridden; render\n * inside a `<CanvasShell>` (it needs the React Flow context via `useStore`).\n */\nexport function FlowMiniMap<NodeType extends Node = Node>({\n className,\n nodeColor = \"var(--flow-minimap-node)\",\n nodeStrokeColor = \"var(--flow-minimap-node)\",\n maskColor = \"var(--flow-minimap-mask)\",\n ...props\n}: FlowMiniMapProps<NodeType>) {\n return (\n <MiniMap\n className={cn(\"!rounded-lg !bg-surface-elevated !shadow-ring-sm\", className)}\n nodeColor={nodeColor}\n nodeStrokeColor={nodeStrokeColor}\n maskColor={maskColor}\n {...props}\n />\n );\n}\n","import { type CSSProperties, type ReactNode } from \"react\";\nimport { Reveal, useCollapsiblePanel } from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\n\nconst INSPECTOR_PANEL_WIDTH = \"18rem\";\n\nexport interface InspectorPanelProps {\n title?: ReactNode;\n children: ReactNode;\n onClose?: () => void;\n /** Empty-state shown when nothing is selected. */\n emptyMessage?: ReactNode;\n /** When false, render the empty state instead of children. */\n hasSelection?: boolean;\n /**\n * Stable identity of the current selection (e.g. the selected node's id).\n * When it changes, the body replays a quick fade so it reads as \"details for\n * THIS node\". Pass the selected entity's id (or any stable key derived from\n * the selected data).\n */\n selectionKey?: string | number;\n /**\n * Collapse (research 09 step 3 — converged on the shared `@elabs-ai/components-ui`\n * `useCollapsiblePanel` base; the panel stays single-view, drill-in is\n * ContextPanel-only). Controlled `open` / uncontrolled `defaultOpen`.\n */\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n /** Which edge the panel sits on (collapse slide direction). Default `\"right\"`. */\n side?: \"left\" | \"right\";\n /** Expanded width (any CSS length). Default `\"18rem\"` (the original `w-72`). */\n width?: string;\n className?: string;\n}\n\n/**\n * Properties panel for the selected node/edge. Reusable beside a canvas (e.g.\n * inside a <SplitPanel>) or as a React Flow <Panel>. Always mounted; collapses\n * via the canonical `useCollapsiblePanel` width tween (CSS-gated, reduced-\n * motion safe) when `open`/`defaultOpen` drive it.\n */\nexport function InspectorPanel({\n title = \"Inspector\",\n children,\n onClose,\n emptyMessage = \"Select a node to see its details.\",\n hasSelection = true,\n selectionKey,\n open,\n defaultOpen = true,\n onOpenChange,\n side = \"right\",\n width = INSPECTOR_PANEL_WIDTH,\n className,\n}: InspectorPanelProps) {\n const panel = useCollapsiblePanel({\n side,\n open,\n defaultOpen,\n onOpenChange,\n widthClassName: \"w-(--inspector-panel-width)\",\n spacerCollapsedClassName: \"group-data-[state=collapsed]:w-0\",\n containerSlideClassNames: {\n left: \"left-0 group-data-[state=collapsed]:left-[calc(var(--inspector-panel-width)*-1)]\",\n right: \"right-0 group-data-[state=collapsed]:right-[calc(var(--inspector-panel-width)*-1)]\",\n },\n });\n\n return (\n <div\n className={cn(\"group relative h-full overflow-hidden\", className)}\n style={{ \"--inspector-panel-width\": width } as CSSProperties}\n inert={panel.open ? undefined : true}\n {...panel.attrs}\n >\n <div data-slot=\"inspector-panel-gap\" className={panel.spacerClassName} />\n <div\n data-slot=\"inspector-panel-container\"\n // Bounded to the host (a SplitPanel / canvas edge), not the viewport:\n // absolute within the relative group, full height (research 09 §B.2).\n className={cn(panel.containerClassName, \"absolute inset-y-0 flex h-full border-s\")}\n >\n {/* Detail-tier surface: raised `card` — robust across themes (#193). */}\n <div className=\"flex h-full w-full flex-col bg-card\">\n <div className=\"flex items-center justify-between border-b px-4 py-3\">\n <h3 className=\"text-body font-semibold text-foreground\">{title}</h3>\n {onClose ? (\n <button\n type=\"button\"\n aria-label=\"Close inspector\"\n onClick={onClose}\n className=\"rounded-sm p-1 text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n </button>\n ) : null}\n </div>\n {hasSelection ? (\n <Reveal\n key={selectionKey}\n appear=\"fade\"\n speed=\"fast\"\n className=\"flex-1 overflow-y-auto p-4 text-body\"\n >\n {children}\n </Reveal>\n ) : (\n <div className=\"flex-1 overflow-y-auto p-4 text-body\">\n <p className=\"text-muted-foreground\">{emptyMessage}</p>\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n","import { cn } from \"@elabs-ai/components-ui/lib/cn\";\n\nexport interface LegendItem {\n label: string;\n /** Any CSS color or token reference, e.g. \"var(--chart-1)\". */\n color: string;\n}\n\nexport interface LegendProps {\n items: LegendItem[];\n title?: string;\n className?: string;\n}\n\n/** Small legend mapping colors/types to labels for a canvas or chart. */\nexport function Legend({ items, title, className }: LegendProps) {\n return (\n <div\n className={cn(\n \"rounded-lg bg-surface-elevated/90 p-3 text-xs shadow-ring-sm backdrop-blur\",\n className,\n )}\n >\n {title ? <div className=\"mb-1.5 font-medium text-foreground\">{title}</div> : null}\n <ul className=\"space-y-1\">\n {items.map((item) => (\n <li key={item.label} className=\"flex items-center gap-2 text-muted-foreground\">\n <span\n aria-hidden=\"true\"\n className=\"size-2.5 shrink-0 rounded-full\"\n style={{ backgroundColor: item.color }}\n />\n {item.label}\n </li>\n ))}\n </ul>\n </div>\n );\n}\n","import { Panel, useReactFlow, type PanelPosition } from \"@xyflow/react\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\n\nexport interface ZoomControlsProps {\n position?: PanelPosition;\n className?: string;\n}\n\nfunction ControlButton({\n label,\n onClick,\n children,\n}: {\n label: string;\n onClick: () => void;\n children: React.ReactNode;\n}) {\n return (\n <button\n type=\"button\"\n aria-label={label}\n onClick={onClick}\n className=\"flex size-8 items-center justify-center text-foreground transition-colors duration-fast hover:bg-surface-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring [&_svg]:size-4\"\n >\n {children}\n </button>\n );\n}\n\n/** Branded zoom in / out / fit controls. Render inside <CanvasShell>. */\nexport function ZoomControls({ position = \"bottom-right\", className }: ZoomControlsProps) {\n const { zoomIn, zoomOut, fitView } = useReactFlow();\n return (\n <Panel position={position}>\n <div\n className={cn(\n \"flex divide-x overflow-hidden rounded-lg bg-surface-elevated shadow-ring-sm\",\n className,\n )}\n >\n <ControlButton label=\"Zoom in\" onClick={() => zoomIn()}>\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n >\n <path d=\"M12 5v14M5 12h14\" />\n </svg>\n </ControlButton>\n <ControlButton label=\"Zoom out\" onClick={() => zoomOut()}>\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n >\n <path d=\"M5 12h14\" />\n </svg>\n </ControlButton>\n <ControlButton label=\"Fit view\" onClick={() => fitView()}>\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M8 3H5a2 2 0 0 0-2 2v3M21 8V5a2 2 0 0 0-2-2h-3M16 21h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3\" />\n </svg>\n </ControlButton>\n </div>\n </Panel>\n );\n}\n","import { type ReactNode } from \"react\";\nimport { Handle, NodeResizer, Position, useNodes, type Node, type NodeProps } from \"@xyflow/react\";\nimport { ChevronDown, ChevronRight } from \"lucide-react\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport { useFlowGroups } from \"../use-flow-groups\";\n\n/** Visual accent for a group container. */\nexport type FlowGroupTone = \"default\" | \"accent\" | \"success\" | \"warning\" | \"destructive\";\n\nexport interface FlowGroupNodeData extends Record<string, unknown> {\n /** Header label for the group. */\n title: string;\n /** Optional leading glyph (a Lucide icon element, etc.). */\n icon?: ReactNode;\n /**\n * Whether the group is collapsed to an overview chip. Managed by\n * `useFlowGroups().collapseGroup` / `expandGroup`; the header toggle flips it.\n */\n collapsed?: boolean;\n /**\n * Fallback count shown next to the title. When omitted, the node counts its\n * live direct children from the store, so the badge stays accurate as nodes\n * are added or removed.\n */\n childCount?: number;\n /** Accent tone for the header rule + count badge. */\n tone?: FlowGroupTone;\n}\n\nexport type BrandFlowGroupNode = Node<FlowGroupNodeData, \"group\">;\n\nconst toneAccent: Record<FlowGroupTone, string> = {\n default: \"text-muted-foreground\",\n // #399 — the group LABEL is text on the canvas, so it takes the on-surface\n // `-text` rung. (The status rows below stay on their fill rungs; those are a\n // separate, still-open question, not part of #399's enumerated call sites.)\n accent: \"text-primary-text\",\n success: \"text-success\",\n warning: \"text-warning\",\n destructive: \"text-destructive\",\n};\n\nconst handleClassName = \"!size-2 !border-2 !border-flow-group-border !bg-flow-group\";\n\n/**\n * Branded group container node. Register it as `nodeTypes={{ group: FlowGroupNode }}`\n * and create nodes with `type: \"group\"` + typed `data: FlowGroupNodeData`. Children\n * are re-parented onto it (`parentId`, `extent: \"parent\"`) — see `useFlowGroups`.\n *\n * The header carries the title, a live child-count badge and a collapse/expand\n * toggle (a real `<button>` with `aria-expanded`) that drives\n * `useFlowGroups().collapseGroup` / `expandGroup`. When selected (and expanded)\n * a `<NodeResizer>` lets the group be resized.\n *\n * The surface uses the FL-01 tokens `--flow-group` (fill) + `--flow-group-border`\n * (border). Under a theme whose `--flow-group` is near-transparent, the\n * drawn border does the work.\n */\nexport function FlowGroupNode({ id, data, selected }: NodeProps<BrandFlowGroupNode>) {\n const { toggleCollapse } = useFlowGroups();\n const nodes = useNodes();\n const collapsed = data.collapsed ?? false;\n const tone = data.tone ?? \"default\";\n\n // Live count of direct children; fall back to the stored count before the\n // store settles (or in a non-interactive preview).\n const liveChildCount = nodes.filter((n) => n.parentId === id).length;\n const childCount = liveChildCount || data.childCount || 0;\n\n return (\n <div\n className={cn(\n \"flex h-full w-full flex-col rounded-lg border bg-flow-group/60 text-foreground\",\n \"border-flow-group-border transition-[box-shadow,border-color] duration-fast ease-standard motion-reduce:transition-none\",\n collapsed ? \"shadow-sm\" : \"shadow-none\",\n selected && \"ring-2 ring-ring\",\n )}\n >\n {!collapsed && selected ? (\n <NodeResizer\n minWidth={160}\n minHeight={96}\n lineClassName=\"!border-flow-group-border\"\n handleClassName={handleClassName}\n />\n ) : null}\n\n {/* Handles so proxy edges can attach to the group when collapsed. */}\n <Handle type=\"target\" position={Position.Top} className={handleClassName} />\n <Handle type=\"source\" position={Position.Bottom} className={handleClassName} />\n\n <div\n className={cn(\n \"flex items-center gap-2 px-3 py-2\",\n !collapsed && \"border-b border-flow-group-border\",\n )}\n >\n <button\n type=\"button\"\n onClick={() => toggleCollapse(id)}\n aria-label={collapsed ? `Expand group ${data.title}` : `Collapse group ${data.title}`}\n aria-expanded={!collapsed}\n className={cn(\n \"-ml-1 grid size-5 shrink-0 place-items-center rounded\",\n \"text-muted-foreground hover:bg-muted hover:text-foreground\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n )}\n >\n {collapsed ? (\n <ChevronRight aria-hidden=\"true\" className=\"size-4\" />\n ) : (\n <ChevronDown aria-hidden=\"true\" className=\"size-4\" />\n )}\n </button>\n\n {data.icon ? (\n <span className={cn(\"[&_svg]:size-4\", toneAccent[tone])}>{data.icon}</span>\n ) : null}\n\n <span className=\"min-w-0 flex-1 truncate text-body font-medium\">{data.title}</span>\n\n <span\n className={cn(\n \"shrink-0 rounded-full bg-muted px-1.5 py-0.5 text-meta font-medium tabular-nums\",\n toneAccent[tone],\n )}\n aria-label={`${childCount} ${childCount === 1 ? \"node\" : \"nodes\"}`}\n >\n {childCount}\n </span>\n </div>\n </div>\n );\n}\n","import { useCallback } from \"react\";\nimport { useReactFlow, type Edge, type Node } from \"@xyflow/react\";\nimport {\n collapseGroup as collapseGroupOp,\n expandGroup as expandGroupOp,\n groupNodes as groupNodesOp,\n toggleGroupCollapsed as toggleGroupCollapsedOp,\n ungroup as ungroupOp,\n type GroupNodesOptions,\n} from \"./group-operations\";\n\n/** Options for `groupNodes`/`groupSelection` with an optional generated id. */\nexport type GroupOptions = Partial<Pick<GroupNodesOptions, \"groupId\">> &\n Omit<GroupNodesOptions, \"groupId\">;\n\nexport interface UseFlowGroupsResult {\n /**\n * Wrap the given (top-level) node ids in a new `FlowGroupNode`. Returns the new\n * group id, or `undefined` if nothing was grouped. Pass `options.groupId` for a\n * deterministic id; otherwise one is generated.\n */\n groupNodes: (nodeIds: string[], options?: GroupOptions) => string | undefined;\n /** Group the currently-selected nodes. Returns the new group id (or `undefined`). */\n groupSelection: (options?: GroupOptions) => string | undefined;\n /** Dissolve a group, restoring its children to absolute positions. */\n ungroup: (groupId: string) => void;\n /** Collapse a group to an overview chip (hides subtree, reroutes boundary edges). */\n collapseGroup: (groupId: string) => void;\n /** Expand a collapsed group (exact inverse of collapse). */\n expandGroup: (groupId: string) => void;\n /** Collapse if expanded, expand if collapsed. */\n toggleCollapse: (groupId: string) => void;\n}\n\nfunction generateGroupId(): string {\n return `flow-group-${Math.random().toString(36).slice(2, 9)}`;\n}\n\n/**\n * Thin hook over the pure grouping operations in `group-operations.ts`. Reads and\n * writes the live graph via `useReactFlow()`; all the logic lives in the pure,\n * unit-tested core. Must be called inside a React Flow context (e.g. from a\n * `<Panel>` child of `<CanvasShell>` or from within a custom node).\n */\nexport function useFlowGroups<\n NodeType extends Node = Node,\n EdgeType extends Edge = Edge,\n>(): UseFlowGroupsResult {\n const { getNodes, getEdges, setNodes, setEdges } = useReactFlow<NodeType, EdgeType>();\n\n const apply = useCallback(\n (result: { nodes: NodeType[]; edges: EdgeType[] }) => {\n setNodes(result.nodes);\n setEdges(result.edges);\n },\n [setNodes, setEdges],\n );\n\n const groupNodes = useCallback(\n (nodeIds: string[], options?: GroupOptions): string | undefined => {\n if (nodeIds.length === 0) return undefined;\n const groupId = options?.groupId ?? generateGroupId();\n const result = groupNodesOp<NodeType, EdgeType>(getNodes(), getEdges(), nodeIds, {\n ...options,\n groupId,\n });\n apply(result);\n return groupId;\n },\n [getNodes, getEdges, apply],\n );\n\n const groupSelection = useCallback(\n (options?: GroupOptions): string | undefined => {\n const selectedIds = getNodes()\n .filter((n) => n.selected && !n.parentId)\n .map((n) => n.id);\n return groupNodes(selectedIds, options);\n },\n [getNodes, groupNodes],\n );\n\n const ungroup = useCallback(\n (groupId: string) => apply(ungroupOp<NodeType, EdgeType>(getNodes(), getEdges(), groupId)),\n [getNodes, getEdges, apply],\n );\n\n const collapseGroup = useCallback(\n (groupId: string) =>\n apply(collapseGroupOp<NodeType, EdgeType>(getNodes(), getEdges(), groupId)),\n [getNodes, getEdges, apply],\n );\n\n const expandGroup = useCallback(\n (groupId: string) => apply(expandGroupOp<NodeType, EdgeType>(getNodes(), getEdges(), groupId)),\n [getNodes, getEdges, apply],\n );\n\n const toggleCollapse = useCallback(\n (groupId: string) =>\n apply(toggleGroupCollapsedOp<NodeType, EdgeType>(getNodes(), getEdges(), groupId)),\n [getNodes, getEdges, apply],\n );\n\n return {\n groupNodes,\n groupSelection,\n ungroup,\n collapseGroup,\n expandGroup,\n toggleCollapse,\n };\n}\n","import { getNodesBounds, type Edge, type Node } from \"@xyflow/react\";\nimport type { FlowGroupNodeData, FlowGroupTone } from \"../flow-group-node\";\n\n/**\n * Pure, framework-agnostic grouping operations over `nodes`/`edges` arrays.\n *\n * Every function is a **pure transform** — it never mutates its inputs and\n * returns a fresh `{ nodes, edges }`. This is the testable core; `useFlowGroups`\n * is a thin hook that wires these to a live canvas via `useReactFlow()`.\n */\n\n/** Node type used for the group container (matches `nodeTypes={{ group: FlowGroupNode }}`). */\nexport const FLOW_GROUP_NODE_TYPE = \"group\";\n\n/** Padding (px) left around the child bounds when a group is created. */\nconst DEFAULT_GROUP_PADDING = 28;\n/** Extra top space reserved for the group header so children don't sit under it. */\nconst GROUP_HEADER_OFFSET = 44;\n/** Fixed size of the collapsed \"overview chip\". */\nconst OVERVIEW_WIDTH = 220;\nconst OVERVIEW_HEIGHT = 48;\n\nexport interface GroupNodesOptions {\n /** Id for the new group node. Required so the operation stays deterministic/testable. */\n groupId: string;\n /** Header title for the group. @default \"Group\" */\n title?: string;\n /** Accent tone for the group header. */\n tone?: FlowGroupTone;\n /** Padding (px) around the child bounds. @default 28 */\n padding?: number;\n}\n\nexport interface FlowGroupOperationResult<\n NodeType extends Node = Node,\n EdgeType extends Edge = Edge,\n> {\n nodes: NodeType[];\n edges: EdgeType[];\n}\n\n/**\n * Snapshot stashed on a collapsed group's `data` so `expandGroup` can restore the\n * exact pre-collapse graph (an exact inverse). Holds original node/edge object\n * references — never mutated — so restoration is object-exact.\n */\ninterface FlowGroupCollapsedState {\n /** The group node exactly as it was before collapse (no collapsed state on it). */\n groupSnapshot: Node;\n /** Every descendant node exactly as it was before collapse. */\n descendantSnapshots: Node[];\n /** The boundary-crossing edges exactly as they were before rerouting. */\n reroutedEdges: Edge[];\n}\n\nconst COLLAPSED_STATE_KEY = \"__flowGroupCollapsedState\";\n\n/** Marker + metadata written onto a proxy edge's `data`. */\nexport interface FlowGroupProxyEdgeData extends Record<string, unknown> {\n __flowGroupProxy: true;\n /** Id of the collapsed group this proxy edge stands in for. */\n groupId: string;\n}\n\n/** True if `edge` is a proxy edge synthesized for a collapsed group. */\nexport function isFlowGroupProxyEdge(edge: Edge): boolean {\n return Boolean((edge.data as Partial<FlowGroupProxyEdgeData> | undefined)?.__flowGroupProxy);\n}\n\nfunction proxyEdgeId(groupId: string, originalEdgeId: string): string {\n return `flow-group-proxy__${groupId}__${originalEdgeId}`;\n}\n\nfunction readCollapsedState(node: Node): FlowGroupCollapsedState | undefined {\n return (node.data as Record<string, unknown> | undefined)?.[COLLAPSED_STATE_KEY] as\n | FlowGroupCollapsedState\n | undefined;\n}\n\n/** All node ids whose parent chain reaches `groupId` (children, grandchildren, …). */\nfunction collectDescendantIds(nodes: Node[], groupId: string): Set<string> {\n const childrenByParent = new Map<string, Node[]>();\n for (const n of nodes) {\n if (n.parentId) {\n const arr = childrenByParent.get(n.parentId);\n if (arr) arr.push(n);\n else childrenByParent.set(n.parentId, [n]);\n }\n }\n\n const out = new Set<string>();\n const stack = [...(childrenByParent.get(groupId) ?? [])];\n while (stack.length > 0) {\n const n = stack.pop()!;\n if (out.has(n.id)) continue;\n out.add(n.id);\n const kids = childrenByParent.get(n.id);\n if (kids) stack.push(...kids);\n }\n return out;\n}\n\n/**\n * Wrap `nodeIds` (assumed top-level) in a new `FlowGroupNode` parent.\n *\n * - Computes the child bounds with `getNodesBounds`.\n * - Inserts the group node **before** its children in the array (React Flow\n * requires a parent to precede its children).\n * - Re-parents each child (`parentId`, `extent: \"parent\"`) and rewrites its\n * position to be **relative to the group origin**.\n */\nexport function groupNodes<NodeType extends Node = Node, EdgeType extends Edge = Edge>(\n nodes: NodeType[],\n edges: EdgeType[],\n nodeIds: string[],\n options: GroupNodesOptions,\n): FlowGroupOperationResult<NodeType, EdgeType> {\n const idSet = new Set(nodeIds);\n const children = nodes.filter((n) => idSet.has(n.id));\n if (children.length === 0) return { nodes, edges };\n\n const bounds = getNodesBounds(children);\n const padding = options.padding ?? DEFAULT_GROUP_PADDING;\n const originX = bounds.x - padding;\n const originY = bounds.y - padding - GROUP_HEADER_OFFSET;\n\n const data: FlowGroupNodeData = {\n title: options.title ?? \"Group\",\n childCount: children.length,\n ...(options.tone ? { tone: options.tone } : {}),\n };\n\n const groupNode = {\n id: options.groupId,\n type: FLOW_GROUP_NODE_TYPE,\n position: { x: originX, y: originY },\n width: bounds.width + padding * 2,\n height: bounds.height + padding * 2 + GROUP_HEADER_OFFSET,\n data,\n } as unknown as NodeType;\n\n const reparented = new Map<string, NodeType>();\n for (const child of children) {\n reparented.set(child.id, {\n ...child,\n parentId: options.groupId,\n extent: \"parent\",\n position: { x: child.position.x - originX, y: child.position.y - originY },\n } as NodeType);\n }\n\n let earliest = nodes.length;\n for (let i = 0; i < nodes.length; i++) {\n if (idSet.has(nodes[i]!.id)) {\n earliest = i;\n break;\n }\n }\n\n const mapped = nodes.map((n) => reparented.get(n.id) ?? n);\n const resultNodes = [...mapped.slice(0, earliest), groupNode, ...mapped.slice(earliest)];\n return { nodes: resultNodes, edges };\n}\n\n/**\n * Dissolve a group: remove the group node and restore its **direct** children to\n * absolute positions (dropping `parentId`/`extent`). If the group is currently\n * collapsed it is expanded first, so ungrouping a collapsed group is safe.\n */\nexport function ungroup<NodeType extends Node = Node, EdgeType extends Edge = Edge>(\n nodes: NodeType[],\n edges: EdgeType[],\n groupId: string,\n): FlowGroupOperationResult<NodeType, EdgeType> {\n const group = nodes.find((n) => n.id === groupId);\n if (!group) return { nodes, edges };\n\n const base = (group.data as FlowGroupNodeData | undefined)?.collapsed\n ? expandGroup(nodes, edges, groupId)\n : { nodes, edges };\n\n const liveGroup = base.nodes.find((n) => n.id === groupId)!;\n const gx = liveGroup.position.x;\n const gy = liveGroup.position.y;\n\n const resultNodes = base.nodes\n .filter((n) => n.id !== groupId)\n .map((n) => {\n if (n.parentId === groupId) {\n const {\n parentId: _parentId,\n extent: _extent,\n ...rest\n } = n as NodeType & {\n parentId?: string;\n extent?: unknown;\n };\n return {\n ...rest,\n position: { x: n.position.x + gx, y: n.position.y + gy },\n } as NodeType;\n }\n return n;\n });\n\n return { nodes: resultNodes, edges: base.edges };\n}\n\n/**\n * Collapse a group to an overview chip.\n *\n * - Hides every descendant (including nested groups and their subtrees).\n * - Shrinks the group to a fixed overview size and marks `data.collapsed`.\n * - Re-routes every edge that **crosses the collapse boundary** (exactly one\n * endpoint inside the collapsed subtree) to a **proxy edge** targeting the\n * group node; the original edge is hidden and stashed for restoration.\n * - Edges fully inside or fully outside the subtree are left untouched.\n *\n * The full pre-collapse graph is stashed on the group's `data` so `expandGroup`\n * is an exact inverse. Nested collapse is handled by snapshotting each\n * descendant's exact prior state (a subgroup that was already collapsed stays\n * collapsed on expand — no double-unhide).\n */\nexport function collapseGroup<NodeType extends Node = Node, EdgeType extends Edge = Edge>(\n nodes: NodeType[],\n edges: EdgeType[],\n groupId: string,\n): FlowGroupOperationResult<NodeType, EdgeType> {\n const group = nodes.find((n) => n.id === groupId);\n if (!group) return { nodes, edges };\n if ((group.data as FlowGroupNodeData | undefined)?.collapsed) return { nodes, edges };\n\n const descendantIds = collectDescendantIds(nodes, groupId);\n\n const descendantSnapshots = nodes.filter((n) => descendantIds.has(n.id));\n const directChildCount = nodes.filter((n) => n.parentId === groupId).length;\n\n const isInside = (id: string) => descendantIds.has(id);\n const reroutedEdges: EdgeType[] = [];\n const proxyEdges: EdgeType[] = [];\n for (const edge of edges) {\n const sourceInside = isInside(edge.source);\n const targetInside = isInside(edge.target);\n if (sourceInside === targetInside) continue; // fully inside or fully outside\n\n reroutedEdges.push(edge);\n const proxyData: FlowGroupProxyEdgeData = {\n ...(edge.data ?? {}),\n __flowGroupProxy: true,\n groupId,\n };\n proxyEdges.push({\n ...edge,\n id: proxyEdgeId(groupId, edge.id),\n source: sourceInside ? groupId : edge.source,\n target: targetInside ? groupId : edge.target,\n sourceHandle: undefined,\n targetHandle: undefined,\n data: proxyData,\n } as EdgeType);\n }\n\n const collapsedState: FlowGroupCollapsedState = {\n groupSnapshot: group,\n descendantSnapshots,\n reroutedEdges,\n };\n\n const resultNodes = nodes.map((n) => {\n if (n.id === groupId) {\n return {\n ...n,\n width: OVERVIEW_WIDTH,\n height: OVERVIEW_HEIGHT,\n data: {\n ...(n.data as Record<string, unknown>),\n collapsed: true,\n childCount: directChildCount,\n [COLLAPSED_STATE_KEY]: collapsedState,\n },\n } as NodeType;\n }\n if (descendantIds.has(n.id)) {\n return { ...n, hidden: true } as NodeType;\n }\n return n;\n });\n\n const reroutedIds = new Set(reroutedEdges.map((e) => e.id));\n const resultEdges = edges.map((e) =>\n reroutedIds.has(e.id) ? ({ ...e, hidden: true } as EdgeType) : e,\n );\n resultEdges.push(...proxyEdges);\n\n return { nodes: resultNodes, edges: resultEdges };\n}\n\n/**\n * Expand a collapsed group — the exact inverse of `collapseGroup`. Restores the\n * group node, un-hides the descendant subtree (to each node's exact prior\n * state), removes this group's proxy edges and restores the original edges.\n * No-op if the group isn't collapsed.\n */\nexport function expandGroup<NodeType extends Node = Node, EdgeType extends Edge = Edge>(\n nodes: NodeType[],\n edges: EdgeType[],\n groupId: string,\n): FlowGroupOperationResult<NodeType, EdgeType> {\n const group = nodes.find((n) => n.id === groupId);\n const state = group ? readCollapsedState(group) : undefined;\n if (!group || !state) return { nodes, edges };\n\n const snapshotById = new Map(state.descendantSnapshots.map((n) => [n.id, n]));\n const reroutedById = new Map(state.reroutedEdges.map((e) => [e.id, e]));\n\n const resultNodes = nodes.map((n) => {\n if (n.id === groupId) return state.groupSnapshot as NodeType;\n const snap = snapshotById.get(n.id);\n return snap ? (snap as NodeType) : n;\n });\n\n const resultEdges = edges\n .filter(\n (e) =>\n !(\n isFlowGroupProxyEdge(e) &&\n (e.data as Partial<FlowGroupProxyEdgeData> | undefined)?.groupId === groupId\n ),\n )\n .map((e) => {\n const original = reroutedById.get(e.id);\n return original ? (original as EdgeType) : e;\n });\n\n return { nodes: resultNodes, edges: resultEdges };\n}\n\n/** Collapse if expanded, expand if collapsed. */\nexport function toggleGroupCollapsed<NodeType extends Node = Node, EdgeType extends Edge = Edge>(\n nodes: NodeType[],\n edges: EdgeType[],\n groupId: string,\n): FlowGroupOperationResult<NodeType, EdgeType> {\n const group = nodes.find((n) => n.id === groupId);\n if (!group) return { nodes, edges };\n return (group.data as FlowGroupNodeData | undefined)?.collapsed\n ? expandGroup(nodes, edges, groupId)\n : collapseGroup(nodes, edges, groupId);\n}\n","/**\n * @elabs-ai/components-flow — branded React Flow (@xyflow/react) building blocks.\n *\n * Import the React Flow stylesheet once at the app root:\n * import \"@xyflow/react/dist/style.css\";\n */\nexport * from \"./canvas-shell\";\nexport * from \"./flow-node\";\nexport * from \"./flow-edge\";\nexport * from \"./flow-smart-edge\";\nexport * from \"./flow-floating-edge\";\nexport * from \"./flow-placeholder-node\";\nexport * from \"./flow-button-edge\";\nexport * from \"./flow-layout\";\nexport * from \"./helper-lines\";\nexport * from \"./flow-mini-map\";\nexport * from \"./inspector-panel\";\nexport * from \"./legend\";\nexport * from \"./zoom-controls\";\nexport * from \"./flow-group-node\";\nexport * from \"./use-flow-groups\";\n\n// Convenience re-exports so consumers can build flows without a direct dep.\nexport {\n Background,\n Controls,\n MiniMap,\n Panel,\n Position,\n ReactFlow,\n ReactFlowProvider,\n useReactFlow,\n useNodesState,\n useEdgesState,\n addEdge,\n} from \"@xyflow/react\";\nexport type { Node, Edge, Connection, NodeProps, EdgeProps } from \"@xyflow/react\";\n"],"mappings":"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","names":["cn","HelperLines","jsx","jsxs","cn","cn","jsx","jsxs","jsx","BaseEdge","getBezierPath","Position","jsx","getBezierPath","BaseEdge","BaseEdge","getBezierPath","useInternalNode","Position","Fragment","jsx","jsxs","useInternalNode","getBezierPath","BaseEdge","Handle","Position","cn","jsx","jsxs","BaseEdge","getBezierPath","Plus","Fragment","jsx","jsxs","Position","useCallback","useState","useReactFlow","useReactFlow","useState","useCallback","DEFAULT_NODE_WIDTH","DEFAULT_NODE_HEIGHT","nodeSize","cn","jsx","cn","jsx","jsxs","cn","jsx","jsxs","useReactFlow","cn","jsx","jsxs","Handle","Position","cn","useCallback","useReactFlow","useReactFlow","useCallback","groupNodes","ungroup","collapseGroup","expandGroup","jsx","jsxs","handleClassName","cn","Handle","Position","Background","MiniMap","Panel","Position","ReactFlow","ReactFlowProvider","useReactFlow"]}
|
|
1
|
+
{"version":3,"sources":["../src/canvas-shell/canvas-shell.tsx","../src/helper-lines/helper-lines.tsx","../src/helper-lines/use-helper-lines.ts","../src/helper-lines/get-helper-lines.ts","../src/canvas-shell/use-measured-nodes.ts","../src/flow-handle/flow-handle-anchor.ts","../src/flow-node/flow-node.tsx","../src/flow-edge/flow-edge.tsx","../src/flow-edge-path/flow-edge-path.tsx","../src/flow-smart-edge/flow-smart-edge.tsx","../src/flow-smart-edge/smart-edge-geometry.ts","../src/flow-floating-edge/flow-floating-edge.tsx","../src/flow-floating-edge/floating-edge-geometry.ts","../src/flow-placeholder-node/flow-placeholder-node.tsx","../src/flow-button-edge/flow-button-edge.tsx","../src/flow-layout/flow-layout.ts","../src/flow-layout/use-flow-layout.ts","../src/flow-layout/layout-graph.ts","../src/flow-layout/use-auto-layout.ts","../src/flow-mini-map/flow-mini-map.tsx","../src/inspector-panel/inspector-panel.tsx","../src/legend/legend.tsx","../src/flow-weighted-edge/weight-scale.ts","../src/zoom-controls/zoom-controls.tsx","../src/flow-group-node/flow-group-node.tsx","../src/use-flow-groups/use-flow-groups.ts","../src/use-flow-groups/group-operations.ts","../src/index.ts","../src/flow-weighted-edge/flow-weighted-edge.tsx","../src/flow-weighted-edge/edge-label-pill.tsx","../src/flow-weighted-edge/back-edge-geometry.ts","../src/flow-weighted-edge/edge-aria.ts","../src/flow-self-loop-edge/flow-self-loop-edge.tsx","../src/flow-self-loop-edge/self-loop-geometry.ts"],"sourcesContent":["import { useEffect, type ReactNode } from \"react\";\nimport {\n Background,\n ReactFlow,\n ReactFlowProvider,\n useNodesInitialized,\n useReactFlow,\n useStoreApi,\n type AriaLabelConfig,\n type Edge,\n type FitViewOptions,\n type Node,\n type ReactFlowProps,\n} from \"@xyflow/react\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport { HelperLines } from \"../helper-lines/helper-lines\";\nimport { useHelperLines } from \"../helper-lines/use-helper-lines\";\nimport { useMeasuredNodes } from \"./use-measured-nodes\";\n\nexport interface CanvasShellProps<\n NodeType extends Node = Node,\n EdgeType extends Edge = Edge,\n> extends ReactFlowProps<NodeType, EdgeType> {\n /** Render a branded dotted background grid. Defaults to true. */\n background?: boolean;\n /**\n * Enable alignment guides + snapping while a single node is dragged. Off by\n * default; when false, CanvasShell behaves exactly as before. When true,\n * CanvasShell wires `useHelperLines` around your `onNodesChange` and renders\n * the `<HelperLines>` overlay internally — pass `nodes` + `onNodesChange`\n * (the standard controlled setup, e.g. via `useNodesState`). For full control\n * use the exported `useHelperLines` + `<HelperLines>` directly.\n */\n helperLines?: boolean;\n /**\n * Re-fit the viewport whenever this value changes.\n *\n * React Flow's own `fitView` prop fits ONCE, as soon as the nodes are first measured,\n * and then never again. A canvas whose positions arrive AFTER that first paint —\n * anything laid out asynchronously, e.g. a debounced `layoutFlow` pass — therefore gets\n * fitted to the pre-layout positions (typically every node still stacked at the origin)\n * and stays there. Measured on the process map: the viewport pinned at React Flow's\n * `maxZoom` of 2 with 2 of 11 nodes on screen, while the layout underneath was correct\n * the whole time.\n *\n * Pass the key the layout is cached on (structure + direction), NOT something that\n * changes on every render and not a metric: a re-fit is a viewport jump, so it must\n * happen when the picture genuinely moved and never while the reader is reading. The\n * fit waits for `useNodesInitialized`, so it measures real node boxes rather than\n * fitting a degenerate bounding box a second time.\n */\n fitViewKey?: string | number;\n /** Options for the {@link fitViewKey} re-fit. Ignored without one. */\n fitViewKeyOptions?: FitViewOptions;\n /** Overlays rendered inside the flow (ZoomControls, Legend, Panels). */\n children?: ReactNode;\n className?: string;\n}\n\ntype CanvasShellInnerProps<NodeType extends Node, EdgeType extends Edge> = Omit<\n CanvasShellProps<NodeType, EdgeType>,\n \"helperLines\"\n>;\n\n/**\n * Branded defaults for React Flow's `ariaLabelConfig` (12.7+) — sentence-case\n * labels that match the rest of brand-ui's voice (see `ZoomControls`'\n * \"Zoom in\"/\"Zoom out\"/\"Fit view\"), instead of the library's Title Case\n * defaults. Callers can override any key; unset keys keep these defaults, and\n * React Flow itself fills in any key neither side sets.\n */\nconst DEFAULT_ARIA_LABEL_CONFIG: Partial<AriaLabelConfig> = {\n \"node.a11yDescription.default\":\n \"Press Enter or Space to select this node. Press Delete to remove it, Escape to cancel.\",\n \"node.a11yDescription.keyboardDisabled\":\n \"Press Enter or Space to select this node. Then use the arrow keys to move it. Press Delete to remove it, Escape to cancel.\",\n \"edge.a11yDescription.default\":\n \"Press Enter or Space to select this edge. Press Delete to remove it, Escape to cancel.\",\n \"controls.ariaLabel\": \"Canvas controls\",\n \"controls.zoomIn.ariaLabel\": \"Zoom in\",\n \"controls.zoomOut.ariaLabel\": \"Zoom out\",\n \"controls.fitView.ariaLabel\": \"Fit view\",\n \"controls.interactive.ariaLabel\": \"Toggle interactivity\",\n \"minimap.ariaLabel\": \"Minimap\",\n \"handle.ariaLabel\": \"Connection handle\",\n};\n\n/**\n * Branded React Flow canvas. Sets a token-driven background + sensible\n * defaults; pass nodes/edges/handlers through as normal React Flow props.\n *\n * Consumers MUST import the React Flow stylesheet once at the app root:\n * import \"@xyflow/react/dist/style.css\";\n *\n * Children render inside the flow context, so <ZoomControls /> and React Flow\n * <Panel>s work when placed here.\n */\nexport function CanvasShell<NodeType extends Node = Node, EdgeType extends Edge = Edge>({\n helperLines = false,\n ...props\n}: CanvasShellProps<NodeType, EdgeType>) {\n if (helperLines) {\n // A provider is needed so `useHelperLines` can read the live node store; the\n // ReactFlow rendered below connects to this same provider.\n return (\n <ReactFlowProvider>\n <CanvasShellWithHelperLines<NodeType, EdgeType> {...props} />\n </ReactFlowProvider>\n );\n }\n return <CanvasShellBase<NodeType, EdgeType> {...props} />;\n}\n\nfunction CanvasShellBase<NodeType extends Node, EdgeType extends Edge>({\n background = true,\n children,\n className,\n ariaLabelConfig,\n nodes,\n onNodesChange,\n fitViewKey,\n fitViewKeyOptions,\n ...props\n}: CanvasShellInnerProps<NodeType, EdgeType>) {\n const measured = useMeasuredNodes<NodeType>(nodes, onNodesChange);\n return (\n <div className={cn(\"h-full w-full bg-canvas\", className)}>\n <ReactFlow\n fitView\n proOptions={{ hideAttribution: true }}\n ariaLabelConfig={{ ...DEFAULT_ARIA_LABEL_CONFIG, ...ariaLabelConfig }}\n nodes={measured.nodes}\n onNodesChange={measured.onNodesChange}\n {...props}\n >\n {background ? <Background gap={20} size={1} color=\"var(--canvas-grid)\" /> : null}\n {fitViewKey === undefined ? null : (\n <FitViewOnKey fitViewKey={fitViewKey} options={fitViewKeyOptions} />\n )}\n {children}\n </ReactFlow>\n </div>\n );\n}\n\n/**\n * Re-fits the viewport when `fitViewKey` changes and the nodes have been measured.\n *\n * Rendered as a CHILD of `<ReactFlow>` because `useReactFlow`/`useNodesInitialized` need\n * the flow context, which only exists below it. It renders nothing.\n */\nfunction FitViewOnKey({\n fitViewKey,\n options,\n}: {\n fitViewKey: string | number;\n options?: FitViewOptions;\n}) {\n const { fitView, getNodes, getNodesBounds, setViewport } = useReactFlow();\n const store = useStoreApi();\n const nodesInitialized = useNodesInitialized();\n useEffect(() => {\n if (!nodesInitialized) return;\n void fitView(options).then(() =>\n anchorToStartWhenClamped(store, setViewport, getNodesBounds, getNodes(), options),\n );\n // `options` is deliberately absent: an inline object literal would re-fit on every\n // render, which is a viewport jump under the reader's cursor. The key is the trigger.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [fitViewKey, nodesInitialized, fitView, getNodes, getNodesBounds, setViewport, store]);\n return null;\n}\n\n/**\n * After a fit that could NOT show everything, show the BEGINNING rather than the middle.\n *\n * `fitView` always centres. That is right when the content fits — and wrong the moment a\n * `minZoom` floor stops it from shrinking far enough, because centring a graph that\n * overflows puts its first rank off the top of the pane and opens the canvas on the\n * middle of a process nobody has read the start of yet. Every layout this shell is used\n * with runs from an origin (top for `TB`, left for `LR`), so the content's own top-left\n * corner IS the beginning, in both.\n *\n * Applied PER AXIS, and only to an axis that actually overflows: a tall graph in a wide\n * pane is pinned to the top and stays horizontally centred, which is what a reader\n * expects. A canvas whose content fits is left exactly as `fitView` left it.\n */\nfunction anchorToStartWhenClamped(\n store: ReturnType<typeof useStoreApi>,\n setViewport: ReturnType<typeof useReactFlow>[\"setViewport\"],\n // The HOOK's `getNodesBounds`, never the top-level export: the standalone one warns in\n // development when it is handed no `nodeLookup` (\"Please use `getNodesBounds` from\n // `useReactFlow`…\") and measures a parent's own `position` instead of resolving a\n // nested node's absolute one, so a keyed re-fit on a canvas with sub-flows anchors to\n // the wrong corner.\n getNodesBounds: ReturnType<typeof useReactFlow>[\"getNodesBounds\"],\n nodes: Node[],\n options: FitViewOptions | undefined,\n): void {\n if (nodes.length === 0) return;\n const { width, height, transform, panZoom } = store.getState();\n const [x, y, zoom] = transform;\n if (!width || !height || !zoom) return;\n\n const bounds = getNodesBounds(nodes);\n // The same fraction-of-the-pane padding `fitView` itself applies, so the anchored\n // corner sits exactly where a fitted one would.\n const padding = typeof options?.padding === \"number\" ? options.padding : 0.1;\n const padX = width * padding;\n const padY = height * padding;\n\n const overflowsX = bounds.width * zoom > width - padX * 2 + 1;\n const overflowsY = bounds.height * zoom > height - padY * 2 + 1;\n if (!overflowsX && !overflowsY) return;\n\n const next = {\n x: overflowsX ? padX - bounds.x * zoom : x,\n y: overflowsY ? padY - bounds.y * zoom : y,\n zoom,\n };\n // `setViewport` is a no-op before the pan/zoom instance exists (the very first commit).\n if (!panZoom) return;\n void setViewport(next);\n}\n\nfunction CanvasShellWithHelperLines<NodeType extends Node, EdgeType extends Edge>({\n onNodesChange,\n children,\n ...props\n}: CanvasShellInnerProps<NodeType, EdgeType>) {\n const {\n onNodesChange: wrappedOnNodesChange,\n helperLineHorizontal,\n helperLineVertical,\n } = useHelperLines<NodeType>(onNodesChange);\n\n return (\n <CanvasShellBase<NodeType, EdgeType> {...props} onNodesChange={wrappedOnNodesChange}>\n {children}\n <HelperLines horizontal={helperLineHorizontal} vertical={helperLineVertical} />\n </CanvasShellBase>\n );\n}\n","\"use client\";\n\nimport { forwardRef, type SVGAttributes } from \"react\";\nimport { useViewport } from \"@xyflow/react\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\n\nexport interface HelperLinesProps extends SVGAttributes<SVGSVGElement> {\n /** Absolute flow-y of the horizontal guide (from `useHelperLines`). */\n horizontal?: number;\n /** Absolute flow-x of the vertical guide (from `useHelperLines`). */\n vertical?: number;\n}\n\n/**\n * Decorative alignment-guide overlay. Draws a full-width horizontal line at\n * `horizontal` and/or a full-height vertical line at `vertical`, coloured with\n * the `--flow-helper-line` token. It reads the live viewport transform\n * (zoom + pan) so the guides sit on the real alignment position when the canvas\n * is zoomed or panned.\n *\n * Render as a child of `<CanvasShell>` / `<ReactFlow>` (it must be inside the\n * flow context to read the viewport). Purely decorative — hidden from AT.\n */\nexport const HelperLines = forwardRef<SVGSVGElement, HelperLinesProps>(function HelperLines(\n { horizontal, vertical, className, ...props },\n ref,\n) {\n const { x, y, zoom } = useViewport();\n\n if (horizontal == null && vertical == null) return null;\n\n // flow coordinate → pane pixel: coord * zoom + pan offset.\n const screenY = horizontal == null ? null : y + horizontal * zoom;\n const screenX = vertical == null ? null : x + vertical * zoom;\n\n return (\n <svg\n ref={ref}\n aria-hidden=\"true\"\n width=\"100%\"\n height=\"100%\"\n className={cn(\"pointer-events-none absolute inset-0 size-full\", className)}\n {...props}\n >\n {screenY != null ? (\n <line\n x1={0}\n x2=\"100%\"\n y1={screenY}\n y2={screenY}\n stroke=\"var(--flow-helper-line, var(--primary))\"\n strokeWidth={1}\n />\n ) : null}\n {screenX != null ? (\n <line\n x1={screenX}\n x2={screenX}\n y1={0}\n y2=\"100%\"\n stroke=\"var(--flow-helper-line, var(--primary))\"\n strokeWidth={1}\n />\n ) : null}\n </svg>\n );\n});\n","\"use client\";\n\nimport { useCallback, useState } from \"react\";\nimport { useReactFlow, type Node, type NodeChange } from \"@xyflow/react\";\nimport { getHelperLines, type HelperLineRect } from \"./get-helper-lines\";\n\nexport interface UseHelperLinesOptions {\n /** Alignment threshold in flow coordinates. Defaults to 5. */\n threshold?: number;\n}\n\nexport interface UseHelperLinesResult<NodeType extends Node = Node> {\n /**\n * Drop-in replacement for the consumer's `onNodesChange`: it snaps a dragged\n * node onto any near-aligned edge/center, then forwards the (mutated) changes\n * to the wrapped handler.\n */\n onNodesChange: (changes: NodeChange<NodeType>[]) => void;\n /** Absolute flow-y of the active horizontal guide, or `undefined`. */\n helperLineHorizontal: number | undefined;\n /** Absolute flow-x of the active vertical guide, or `undefined`. */\n helperLineVertical: number | undefined;\n}\n\nfunction nodeRect(node: Node): HelperLineRect {\n return {\n x: node.position.x,\n y: node.position.y,\n width: node.measured?.width ?? node.width ?? 0,\n height: node.measured?.height ?? node.height ?? 0,\n };\n}\n\n/**\n * Decorate a consumer's `onNodesChange` with alignment guides + snapping.\n *\n * While a **single** node is dragged, the returned handler compares its\n * left/center/right and top/middle/bottom against every other node; when a pair\n * is within `threshold` flow px it (a) snaps the dragged node's position change\n * onto the guide and (b) exposes the guide coordinates so `<HelperLines>` can\n * draw them. Guides clear when the drag ends or nothing aligns.\n *\n * Must be called inside a React Flow context (a `<ReactFlowProvider>` or a\n * component rendered under `<ReactFlow>`), since it reads the live node store.\n *\n * @example\n * const [nodes, , onNodesChange] = useNodesState(initialNodes);\n * const { onNodesChange: handle, helperLineHorizontal, helperLineVertical } =\n * useHelperLines(onNodesChange);\n * // <ReactFlow onNodesChange={handle}>\n * // <HelperLines horizontal={helperLineHorizontal} vertical={helperLineVertical} />\n */\nexport function useHelperLines<NodeType extends Node = Node>(\n onNodesChange?: (changes: NodeChange<NodeType>[]) => void,\n options: UseHelperLinesOptions = {},\n): UseHelperLinesResult<NodeType> {\n const { threshold = 5 } = options;\n const { getNodes } = useReactFlow<NodeType>();\n const [helperLineHorizontal, setHelperLineHorizontal] = useState<number>();\n const [helperLineVertical, setHelperLineVertical] = useState<number>();\n\n const handleNodesChange = useCallback(\n (changes: NodeChange<NodeType>[]) => {\n let horizontal: number | undefined;\n let vertical: number | undefined;\n\n const dragChanges = changes.filter(\n (c): c is Extract<NodeChange<NodeType>, { type: \"position\" }> =>\n c.type === \"position\" && c.dragging === true && c.position != null,\n );\n\n // Only guide/snap a single dragged node — multi-select drags are ambiguous.\n if (dragChanges.length === 1) {\n const change = dragChanges[0];\n const nodes = getNodes();\n const dragged = change ? nodes.find((n) => n.id === change.id) : undefined;\n\n if (change && change.position && dragged) {\n const draggedRect: HelperLineRect = {\n x: change.position.x,\n y: change.position.y,\n width: dragged.measured?.width ?? dragged.width ?? 0,\n height: dragged.measured?.height ?? dragged.height ?? 0,\n };\n const others = nodes.filter((n) => n.id !== change.id).map(nodeRect);\n const {\n snapX,\n snapY,\n vertical: v,\n horizontal: h,\n } = getHelperLines(draggedRect, others, threshold);\n\n // Mutate the position change so the node lands exactly on the guide.\n if (snapX !== undefined) change.position.x = snapX;\n if (snapY !== undefined) change.position.y = snapY;\n vertical = v;\n horizontal = h;\n }\n }\n\n setHelperLineHorizontal(horizontal);\n setHelperLineVertical(vertical);\n onNodesChange?.(changes);\n },\n [getNodes, onNodesChange, threshold],\n );\n\n return { onNodesChange: handleNodesChange, helperLineHorizontal, helperLineVertical };\n}\n","/**\n * Pure alignment-guide geometry for the helper-lines feature.\n *\n * Implemented fresh from the documented \"helper lines\" behaviour (the React Flow\n * Pro example is paid — only the concept is reused): while a single node drags,\n * compare its left / horizontal-center / right against every other node's\n * left / center / right (and top / middle / bottom on the vertical axis). When a\n * pair falls within `threshold` flow-coordinate pixels, record the guide's\n * absolute coordinate and the snapped top-left position for the dragged node.\n *\n * Everything here is a pure function of plain rectangles so it is unit-testable\n * without a live React Flow instance.\n */\n\n/** A node's bounding box in absolute flow coordinates (top-left origin). */\nexport interface HelperLineRect {\n /** Left edge (top-left x) in flow coordinates. */\n x: number;\n /** Top edge (top-left y) in flow coordinates. */\n y: number;\n width: number;\n height: number;\n}\n\nexport interface HelperLinesResult {\n /** New top-left x for the dragged node once snapped to the vertical guide. */\n snapX?: number;\n /** New top-left y for the dragged node once snapped to the horizontal guide. */\n snapY?: number;\n /** Absolute flow-x of the active vertical guide line, if any. */\n vertical?: number;\n /** Absolute flow-y of the active horizontal guide line, if any. */\n horizontal?: number;\n}\n\n/**\n * Fractional anchor offsets along an axis: 0 = start edge (left/top),\n * 0.5 = center/middle, 1 = end edge (right/bottom). Comparing all three anchors\n * of the dragged node against all three of every other node yields\n * edge-to-edge, edge-to-center and center-to-center alignment.\n */\nconst ANCHORS = [0, 0.5, 1] as const;\n\n/**\n * Compute the closest vertical + horizontal alignment guides (and the snapped\n * position) for a dragged rectangle against the other rectangles.\n *\n * @param dragged the dragged node's proposed bounding box (flow coordinates)\n * @param others every other node's bounding box\n * @param threshold max distance (flow px) at which alignment engages; a pair at\n * exactly `threshold` does NOT match (strict boundary)\n */\nexport function getHelperLines(\n dragged: HelperLineRect,\n others: Iterable<HelperLineRect>,\n threshold = 5,\n): HelperLinesResult {\n const result: HelperLinesResult = {};\n // Track the closest match on each axis; initialising to `threshold` makes the\n // boundary strict (a distance equal to threshold never wins).\n let bestVerticalDist = threshold;\n let bestHorizontalDist = threshold;\n\n // Each anchor as { a: fractional offset, pos: absolute coordinate }.\n const draggedX = ANCHORS.map((a) => ({ a, pos: dragged.x + dragged.width * a }));\n const draggedY = ANCHORS.map((a) => ({ a, pos: dragged.y + dragged.height * a }));\n\n for (const other of others) {\n const otherX = ANCHORS.map((a) => other.x + other.width * a);\n const otherY = ANCHORS.map((a) => other.y + other.height * a);\n\n // Vertical guide (align on the X axis).\n for (const d of draggedX) {\n for (const oPos of otherX) {\n const dx = Math.abs(d.pos - oPos);\n if (dx < bestVerticalDist) {\n bestVerticalDist = dx;\n result.vertical = oPos;\n result.snapX = oPos - dragged.width * d.a;\n }\n }\n }\n\n // Horizontal guide (align on the Y axis).\n for (const d of draggedY) {\n for (const oPos of otherY) {\n const dy = Math.abs(d.pos - oPos);\n if (dy < bestHorizontalDist) {\n bestHorizontalDist = dy;\n result.horizontal = oPos;\n result.snapY = oPos - dragged.height * d.a;\n }\n }\n }\n }\n\n return result;\n}\n","import { useCallback, useMemo, useRef, useState } from \"react\";\nimport type { Node, NodeChange, OnNodesChange } from \"@xyflow/react\";\n\n/**\n * Feed React Flow's own measurements back onto the node objects a CONTROLLED canvas\n * renders.\n *\n * React Flow measures every node in the DOM and keeps the result on its INTERNAL node\n * record; the objects the consumer passed in `nodes` are never touched. That is fine for\n * the renderer — edges and the viewport read the internal record — but three shipped\n * surfaces read `node.measured` off the USER object instead, and each of them silently\n * degrades when it is absent:\n *\n * - **`<MiniMap>`** bails out per node in `nodeHasDimensions(internals.userNode)` and\n * renders NOTHING. Measured: a `CanvasShell` with 11 nodes drew 0 minimap rects while\n * the minimap's own `viewBox` (computed from the internal records) was correct — a\n * blank white panel, in the flow package's own `FlowMiniMap` story as much as in a\n * composing package's.\n * - **`layoutFlow`** falls back to `DEFAULT_NODE_WIDTH`/`DEFAULT_NODE_HEIGHT`\n * (172×40) and hands dagre a node half the height of the real card, so ranks are laid\n * out too close together and edge labels collide with the nodes below them.\n * - Any consumer arithmetic over `node.measured` — the same trap, one layer out.\n *\n * React Flow's own examples never hit this because they drive the canvas with\n * `useNodesState`, whose `onNodesChange` applies the `dimensions` change back into the\n * array. A canvas driven from derived data (a discovered process graph, a layout hook)\n * legitimately has no such setter, and gets a half-working canvas with no error. This\n * hook closes that gap centrally so no consumer has to know about it: it remembers the\n * dimensions React Flow reports and merges them into the nodes on the way past.\n *\n * It is deliberately a NO-OP in the two cases where React Flow already does the right\n * thing: an UNCONTROLLED canvas (`defaultNodes`, where `hasDefaultNodes` makes React Flow\n * apply the changes itself) never reaches here because `nodes` is undefined, and a\n * controlled canvas whose consumer already applies dimension changes finds `measured`\n * equal to the cached value and returns the very same array identity.\n */\nexport interface MeasuredNodesResult<NodeType extends Node> {\n /** `nodes`, with React Flow's measured dimensions merged in. Same identity when nothing changed. */\n nodes: NodeType[] | undefined;\n /** The handler to give React Flow — records dimensions, then calls the consumer's own. */\n onNodesChange: OnNodesChange<NodeType> | undefined;\n}\n\nexport function useMeasuredNodes<NodeType extends Node>(\n nodes: NodeType[] | undefined,\n onNodesChange: OnNodesChange<NodeType> | undefined,\n): MeasuredNodesResult<NodeType> {\n const measured = useRef(new Map<string, { width: number; height: number }>());\n // A version counter, not the map itself: the map is a ref so the change handler stays\n // stable, and this is what tells the memo below that its contents moved.\n const [version, setVersion] = useState(0);\n\n const handleNodesChange = useCallback<OnNodesChange<NodeType>>(\n (changes: NodeChange<NodeType>[]) => {\n let touched = false;\n for (const change of changes) {\n if (change.type !== \"dimensions\" || !change.dimensions) continue;\n const previous = measured.current.get(change.id);\n if (\n previous?.width === change.dimensions.width &&\n previous.height === change.dimensions.height\n ) {\n continue;\n }\n measured.current.set(change.id, { ...change.dimensions });\n touched = true;\n }\n onNodesChange?.(changes);\n if (touched) setVersion((n) => n + 1);\n },\n [onNodesChange],\n );\n\n const mergedNodes = useMemo(() => {\n if (!nodes) return nodes;\n // `version` is read so this recomputes when the ref's contents move; the map itself\n // is intentionally not a dependency (a ref never changes identity).\n void version;\n let changed = false;\n const next = nodes.map((node) => {\n const dimensions = measured.current.get(node.id);\n if (!dimensions) return node;\n if (\n node.measured?.width === dimensions.width &&\n node.measured?.height === dimensions.height\n ) {\n return node;\n }\n changed = true;\n return { ...node, measured: dimensions };\n });\n return changed ? next : nodes;\n }, [nodes, version]);\n\n return {\n nodes: mergedNodes,\n // Only take the handler over when there is something to measure INTO. An uncontrolled\n // canvas is left exactly as it was.\n onNodesChange: nodes ? handleNodesChange : onNodesChange,\n };\n}\n","/**\n * The class every React Flow `<Handle>` in this package carries — and the one a\n * consumer's own custom node must carry too.\n *\n * A connector dot is a MEASUREMENT ANCHOR, not a moving part. React Flow measures\n * `handleBounds` from the DOM exactly once per layout change (`updateNodeInternals`,\n * fired from the effect that sees `sourcePosition`/`targetPosition` change) and then\n * draws every edge endpoint from that stored number until something invalidates it.\n * So a dot that is still ON ITS WAY to its new side when that measurement is taken is\n * measured at the wrong place — permanently, because nothing measures again.\n *\n * That is not hypothetical, and it is not only about author-written transitions:\n *\n * - `@elabs-ai/components-tokens`' reduced-motion backstop (`themes.css`, MOTION GATE)\n * sets `transition-duration: 0.01ms !important` on `*` so third-party engines that\n * ignore the `--t-*` tokens (Monaco, `@xyflow/react`, Streamdown) cannot animate.\n * `transition-property` is left at its initial value, `all` — so under\n * `prefers-reduced-motion: reduce` that rule does not remove a transition from a\n * handle, it CREATES one: every geometric property of every element becomes\n * transitioned, for one frame.\n * - One frame is all it takes. Measured on `ProcessMap direction=\"LR\"`: at the commit\n * that flips the handles from top/bottom to left/right, the dot's computed box was\n * still the OLD one (`getAnimations()` on it returned live `left`+`top`+`transform`\n * transitions), React Flow measured `{ x: 164, y: 45.5 }` where the settled DOM has\n * `{ x: 172, y: 37.5 }`, and every edge on the map then terminated up to 67 px away\n * from the dot it points at — for reduced-motion readers only, forever.\n *\n * `transition-property: none` is the fix at the right layer: it costs nothing under\n * normal motion (no rule animates a handle there anyway) and it makes the reposition\n * synchronous, so whenever React Flow measures, it measures the settled dot.\n *\n * The literal is written out here rather than assembled, because Tailwind extracts\n * candidates from source TEXT: this file is scanned, so the utility is emitted, and\n * components may interpolate the constant freely.\n */\nexport const FLOW_HANDLE_ANCHOR_CLASS = \"transition-none\";\n","import { type ReactNode } from \"react\";\nimport { Handle, Position, type Node, type NodeProps } from \"@xyflow/react\";\nimport { Star, type LucideIcon } from \"lucide-react\";\nimport { STATUS_TONE_ICONS } from \"@elabs-ai/components-ui\";\nimport { FLOW_HANDLE_ANCHOR_CLASS } from \"../flow-handle/flow-handle-anchor\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\n\n/** A node side that can carry a handle. Doubles as the handle's stable id. */\nexport type FlowHandleSide = \"top\" | \"right\" | \"bottom\" | \"left\";\n\n/**\n * Which sides of a node expose source and/or target handles. When omitted,\n * `FlowNode` keeps its default single top **target** + bottom **source**. When\n * set, a `<Handle>` is rendered on each listed side with a **stable id equal to\n * the side name** (`\"top\" | \"right\" | \"bottom\" | \"left\"`), addressable per\n * handle type — so edges (e.g. `FlowSmartEdge`) can pick a specific anchor.\n */\nexport interface FlowNodeHandles {\n /** Sides exposing a source handle (handle `id` === side). */\n source?: FlowHandleSide[];\n /** Sides exposing a target handle (handle `id` === side). */\n target?: FlowHandleSide[];\n}\n\n/**\n * A `FlowNodeHandles` config exposing source **and** target anchors on all four\n * sides. The recommended setup for `FlowSmartEdge` / `FlowFloatingEdge`: with\n * anchors on every side, an edge connects on whichever side **faces** the other\n * node (left/right when side-by-side, top/bottom when stacked) instead of always\n * top/bottom. Spread onto a node's `data.handles`.\n */\nexport const FLOW_ALL_SIDE_HANDLES: FlowNodeHandles = {\n source: [\"top\", \"right\", \"bottom\", \"left\"],\n target: [\"top\", \"right\", \"bottom\", \"left\"],\n};\n\nexport interface FlowNodeData extends Record<string, unknown> {\n title: string;\n subtitle?: string;\n /** Short type label shown as an eyebrow, e.g. \"Source\", \"Transform\". */\n kind?: string;\n icon?: ReactNode;\n tone?: \"default\" | \"accent\" | \"success\" | \"warning\" | \"destructive\";\n /**\n * Optional multi-side handle configuration. Absent → default top-target /\n * bottom-source (unchanged, backward-compatible).\n */\n handles?: FlowNodeHandles;\n /**\n * An extra row rendered INSIDE the card, below the text block — a meter bar, a\n * sparkline, a chip row.\n *\n * It exists because content a composing package renders BESIDE `FlowNode` (as a sibling\n * inside React Flow's node element) silently breaks the canvas's geometry: React Flow\n * positions every `<Handle>` against the nearest positioned ancestor and measures the\n * node box from its own wrapper, so a sibling row makes the node box taller than the\n * visible card and the handles drift off the card's border by exactly that difference.\n * Measured on the process map's activity node, whose 6px meter and 4px gap put every\n * bottom dot 10px below the card it was supposed to sit on, and every left/right dot\n * 5px below the card's own mid-line.\n *\n * Put the row here instead and the card IS the node box again, so the dots land on the\n * card edge for free. Nothing is rendered when it is absent — existing nodes are\n * byte-identical.\n */\n footer?: ReactNode;\n}\n\nexport type BrandFlowNode = Node<FlowNodeData, \"brand\">;\n\nconst toneRing: Record<NonNullable<FlowNodeData[\"tone\"]>, string> = {\n default: \"border-border\",\n accent: \"border-primary\",\n success: \"border-success\",\n warning: \"border-warning\",\n destructive: \"border-destructive\",\n};\n\n/**\n * `tone` used to be encoded in colour ALONE — a 1px border and nothing else\n * (WCAG 1.4.1, #387). Every non-default tone now also gets a leading Lucide\n * glyph (`aria-hidden`, decorative — the sr-only text below carries the\n * meaning) and a distinct accessible name. `success`/`warning`/`destructive`\n * reuse `@elabs-ai/components-ui`'s `STATUS_TONE_ICONS` (the SAME glyph\n * `StatusBadge`/`StatusIcon` already pair with that tone) rather than\n * inventing a second icon vocabulary; `accent` has no canonical `StatusTone`\n * counterpart to reuse (the closed `StatusTone` enum is deliberately\n * CALM-ONLY and excludes a \"primary/highlighted\" bucket — see\n * `status-badge.tsx`'s docblock), so it gets the one net-new pairing.\n * `default` keeps neither — reaching for a glyph/label on every ordinary node\n * would be visual and assistive-tech noise on the common case.\n */\nconst toneIcon: Partial<Record<NonNullable<FlowNodeData[\"tone\"]>, LucideIcon>> = {\n accent: Star,\n success: STATUS_TONE_ICONS.success,\n warning: STATUS_TONE_ICONS.warning,\n destructive: STATUS_TONE_ICONS.destructive,\n};\n\n/** Ink for the tone glyph — the coloured-text rung (`-text`), not the fill rung. */\nconst toneIconColor: Partial<Record<NonNullable<FlowNodeData[\"tone\"]>, string>> = {\n accent: \"text-primary\",\n success: \"text-success-text\",\n warning: \"text-warning-text\",\n destructive: \"text-destructive-text\",\n};\n\n/** The accessible name the tone glyph stands in for (`sr-only`, #387). */\nconst toneLabel: Partial<Record<NonNullable<FlowNodeData[\"tone\"]>, string>> = {\n accent: \"Highlighted\",\n success: \"Success\",\n warning: \"Warning\",\n destructive: \"Destructive\",\n};\n\nconst sidePosition: Record<FlowHandleSide, Position> = {\n top: Position.Top,\n right: Position.Right,\n bottom: Position.Bottom,\n left: Position.Left,\n};\n\n// `FLOW_HANDLE_ANCHOR_CLASS` last: a connector dot must never be in flight when React\n// Flow measures it. See `flow-handle/flow-handle-anchor.ts`.\nconst handleClassName = `!size-2 !border-2 !border-flow-edge !bg-flow-node ${FLOW_HANDLE_ANCHOR_CLASS}`;\n\n/**\n * Branded custom node. Register it in `nodeTypes={{ brand: FlowNode }}` and\n * create nodes with `type: \"brand\"` and `data: FlowNodeData`.\n *\n * ## Focus vs selection (#312)\n *\n * `selected && \"ring-2 ring-ring\"` below is a SELECTION marker, not a focus\n * indicator — it is React Flow's own click-driven `selected` state and is the\n * genuine-selection carve-out `.claude/rules/theming.md` names explicitly.\n * Keyboard focus is a separate, independent signal this component used to omit\n * entirely (issue #312): React Flow puts `tabIndex`/`role=\"group\"` and the real\n * `:focus-visible` state on **its own wrapper** (`.react-flow__node`, which\n * also always carries a `data-id` attribute), one level ABOVE the `<div>` this\n * component returns — so neither `focus-ring` (`:focus-visible` on self) nor\n * `focus-ring-within` (`:focus-within`, a focused descendant) can ever fire\n * here; focus is PROXIED to an ancestor this component doesn't render.\n * `focus-ring-static` is the flavour built for that exact shape (ADR 0027),\n * gated by an ancestor-selector arbitrary variant — the same idiom\n * `FlowEdgePath` uses for `.react-flow__edge:focus-visible`, keyed on\n * `[data-id]` here (rather than the escaped `.react-flow\\_\\_node` class) so\n * the selector needs no backslash escaping inside a plain JS string — a\n * literal `\\_` in a `cn()` argument is a real JS string escape and would be\n * silently stripped at runtime (unlike in a bare, unbraced JSX attribute,\n * where backslashes are never processed — the reason `FlowEdgePath` can use\n * the class form safely and this component, composing through `cn()`, cannot).\n * The two signals compose without merging into one ring: `selected` alone\n * paints the ring layer only, while a focused node additionally gets the\n * `--ring-contour` outline drawn outside it, so \"selected AND focused\" reads\n * as two visible layers, not the single ring \"selected alone\" paints.\n */\nexport function FlowNode({\n data,\n selected,\n sourcePosition,\n targetPosition,\n}: NodeProps<BrandFlowNode>) {\n const tone = data.tone ?? \"default\";\n const ToneIcon = toneIcon[tone];\n return (\n <div\n // The PAINTED card, and the box every handle dot must sit on the border of.\n // `.react-flow__node` (the wrapper React Flow positions) can legitimately be\n // taller than this — a composing package may render a badge or a meter beside\n // the card — so a test that wants \"is the connector on the card?\" measures\n // against this slot, never against the wrapper. See `testing/canvas-framing`.\n data-slot=\"flow-node\"\n data-tone={tone}\n className={cn(\n \"min-w-44 rounded-lg border bg-flow-node px-3 py-2 text-flow-node-foreground shadow-sm transition-[box-shadow,border-color] duration-fast ease-standard\",\n toneRing[tone],\n selected && \"ring-2 ring-ring\",\n \"[[data-id]:focus-visible_&]:focus-ring-static\",\n )}\n >\n {data.handles ? (\n <>\n {(data.handles.target ?? []).map((side) => (\n <Handle\n key={`target-${side}`}\n id={side}\n type=\"target\"\n position={sidePosition[side]}\n className={handleClassName}\n />\n ))}\n {(data.handles.source ?? []).map((side) => (\n <Handle\n key={`source-${side}`}\n id={side}\n type=\"source\"\n position={sidePosition[side]}\n className={handleClassName}\n />\n ))}\n </>\n ) : (\n <>\n {/* Default single target + source. Positions follow the layout\n direction: React Flow passes `sourcePosition`/`targetPosition`\n (which `layoutFlow` sets per direction), so an LR layout puts the\n target on the LEFT and the source on the RIGHT. Falls back to\n top-in / bottom-out when unset. Handle placement is by `position`,\n not DOM order. */}\n <Handle\n type=\"target\"\n position={targetPosition ?? Position.Top}\n className={handleClassName}\n />\n <Handle\n type=\"source\"\n position={sourcePosition ?? Position.Bottom}\n className={handleClassName}\n />\n </>\n )}\n <div className=\"flex items-center gap-2\">\n {data.icon ? (\n <span className=\"[&_svg]:size-4 text-muted-foreground\">{data.icon}</span>\n ) : null}\n <div className=\"min-w-0 flex-1\">\n {data.kind ? (\n <div className=\"text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n {data.kind}\n </div>\n ) : null}\n <div className=\"truncate text-sm font-medium\">{data.title}</div>\n {data.subtitle ? (\n <div className=\"truncate text-xs text-muted-foreground\">{data.subtitle}</div>\n ) : null}\n </div>\n {ToneIcon ? (\n <ToneIcon aria-hidden=\"true\" className={cn(\"size-3.5 shrink-0\", toneIconColor[tone])} />\n ) : null}\n </div>\n {data.footer ? <div className=\"mt-2\">{data.footer}</div> : null}\n {toneLabel[tone] ? <span className=\"sr-only\">{toneLabel[tone]}</span> : null}\n </div>\n );\n}\n","import { getBezierPath, type EdgeProps } from \"@xyflow/react\";\nimport { FlowEdgePath } from \"../flow-edge-path\";\n\n/**\n * Branded bezier edge using the `--flow-edge` token. Register it in\n * `edgeTypes={{ brand: FlowEdge }}` and create edges with `type: \"brand\"`.\n *\n * Drawn through `FlowEdgePath`, so it carries the shared keyboard focus\n * indicator (#286) — never reach for React Flow's `BaseEdge` directly.\n */\nexport function FlowEdge({\n id,\n sourceX,\n sourceY,\n targetX,\n targetY,\n sourcePosition,\n targetPosition,\n markerEnd,\n style,\n}: EdgeProps) {\n const [edgePath] = getBezierPath({\n sourceX,\n sourceY,\n targetX,\n targetY,\n sourcePosition,\n targetPosition,\n });\n return (\n <FlowEdgePath\n id={id}\n path={edgePath}\n markerEnd={markerEnd}\n data-slot=\"flow-edge\"\n stroke=\"var(--flow-edge)\"\n strokeWidth={1.5}\n style={style}\n />\n );\n}\n","import type { CSSProperties, SVGProps } from \"react\";\nimport { BaseEdge } from \"@xyflow/react\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\n\n/**\n * Extra stroke width, in px, of the `--ring` band drawn around a focused edge.\n * The band sits *outside* the edge's own stroke, so it is visible whatever the\n * edge's resting width is.\n */\nexport const FLOW_EDGE_FOCUS_RING_WIDTH = 3;\n\n/**\n * Extra stroke width, in px, of the neutral contour drawn outside the `--ring`\n * band. This is the layer that carries the WCAG 1.4.11 bar: `--foreground`\n * measures 12.50:1 against `--canvas` in `light` and 16.25:1 in `dark`, where\n * `--ring` alone measures 1.30:1 in `light` (issue #286).\n */\nexport const FLOW_EDGE_FOCUS_CONTOUR_WIDTH = 6;\n\nexport interface FlowEdgePathProps extends Omit<\n SVGProps<SVGPathElement>,\n \"path\" | \"stroke\" | \"strokeWidth\" | \"style\" | \"ref\"\n> {\n /** SVG path `d` for this edge, from `getBezierPath`/`getSmoothStepPath`/… */\n path: string;\n /** Resting stroke paint — a `var(--token)` reference or a resolved colour. */\n stroke: string;\n /** Resting stroke width in px. The focus layers are drawn wider than this. */\n strokeWidth: number;\n /** Dash pattern, applied to the edge AND to both focus layers so a dashed edge keeps its shape when focused. */\n strokeDasharray?: string;\n /** Stroke opacity for the edge itself. Never applied to the focus layers. */\n strokeOpacity?: number;\n /** React Flow marker url, e.g. from `EdgeProps.markerEnd`. */\n markerEnd?: string;\n /** React Flow marker url, e.g. from `EdgeProps.markerStart`. */\n markerStart?: string;\n /** Width of the invisible pointer-target path React Flow draws over the edge. */\n interactionWidth?: number;\n /** Merged onto the edge path, last — so a consumer's `style.stroke` still wins. */\n style?: CSSProperties;\n}\n\n/**\n * The edge path every brand edge type draws, with the keyboard focus indicator\n * built in. **Use this instead of React Flow's `BaseEdge`** — a custom edge that\n * reaches for `BaseEdge` directly ships with no focus indicator at all, which is\n * the defect issue #286 records (and `no-raw-base-edge.test.ts` fails on).\n *\n * ## Why the indicator is drawn rather than restyled\n *\n * React Flow zeroes the native outline on a focused edge\n * (`.react-flow__edge:focus-visible { outline: none }`) and substitutes a stroke\n * recolour on `.react-flow__edge-path`. Every brand edge passes its stroke as an\n * **inline style** on that exact path — `BaseEdge` spreads `style` onto it — and\n * an inline declaration beats any stylesheet rule, so the substitute never\n * painted. Focusing an edge changed nothing on screen: WCAG 2.4.7 failed on\n * every edge of every canvas, in both themes.\n *\n * The obvious repair — move the stroke into a custom property so a stylesheet\n * rule can reach it — does **not** work here, and that was measured rather than\n * assumed: React Flow's own `.react-flow__edge-path { stroke: … }` ships\n * **unlayered**, and unlayered CSS outranks anything in `@layer utilities`,\n * where Tailwind puts every utility. A `stroke-[var(--flow-edge-stroke)]` class\n * would therefore lose to React Flow's `#b1b1b7` default and repaint every edge\n * in the library. `!important` and setting `--xy-edge-stroke-selected` were both\n * rejected in the issue.\n *\n * So the indicator is drawn on two paths this component owns, under the edge and\n * on the same geometry. Nothing can shadow them: not React Flow's stylesheet,\n * not a consumer's inline `style.stroke`, not a future edge type's own painting.\n * They are hidden (`opacity-0`) until the ancestor `g.react-flow__edge` matches\n * `:focus-visible`, which is a plain descendant selector on classes only this\n * component uses.\n *\n * ## Compound, because one colour is not enough\n *\n * The indicator is two layers, per the compound-indicator recipe in\n * `.claude/rules/theming.md`: a neutral `--foreground` contour at\n * `strokeWidth + 6`, and the `--ring` band at `strokeWidth + 3` over it. The\n * contour is the layer that clears WCAG 1.4.11's 3:1 bar against `--canvas` in\n * both reference themes (12.50:1 `light`, 16.25:1 `dark`); a bare `--ring`\n * stroke would not (1.30:1 in `light`). It is opacity + stroke only — no\n * shadow — so it survives `data-decoration=\"8|9|10\"`, which goes shadowless.\n */\nexport function FlowEdgePath({\n path,\n stroke,\n strokeWidth,\n strokeDasharray,\n strokeOpacity,\n markerEnd,\n markerStart,\n interactionWidth,\n className,\n style,\n ...props\n}: FlowEdgePathProps) {\n // The focus layers repeat the edge's own dash pattern so a dashed edge (the\n // `\"back\"` variant) does not gain a solid halo. `\"none\"` is explicit rather\n // than omitted because React Flow's `.react-flow__edge.animated path` rule\n // sets a dasharray on *every* path inside an animated edge, ours included.\n const focusDash = strokeDasharray ?? \"none\";\n\n return (\n <>\n {/* Outer neutral contour — the layer that carries the 3:1 bar. */}\n <path\n d={path}\n fill=\"none\"\n aria-hidden=\"true\"\n data-slot=\"flow-edge-focus-contour\"\n className=\"pointer-events-none stroke-foreground opacity-0 [.react-flow\\_\\_edge:focus-visible_&]:opacity-100\"\n strokeWidth={strokeWidth + FLOW_EDGE_FOCUS_CONTOUR_WIDTH}\n strokeDasharray={focusDash}\n strokeLinecap=\"round\"\n />\n {/* Inner --ring band, drawn over the contour and under the edge. */}\n <path\n d={path}\n fill=\"none\"\n aria-hidden=\"true\"\n data-slot=\"flow-edge-focus-ring\"\n className=\"pointer-events-none stroke-ring opacity-0 [.react-flow\\_\\_edge:focus-visible_&]:opacity-100\"\n strokeWidth={strokeWidth + FLOW_EDGE_FOCUS_RING_WIDTH}\n strokeDasharray={focusDash}\n strokeLinecap=\"round\"\n />\n <BaseEdge\n {...props}\n path={path}\n markerEnd={markerEnd}\n markerStart={markerStart}\n interactionWidth={interactionWidth}\n className={cn(\n \"transition-[stroke,stroke-width] duration-fast ease-standard motion-reduce:transition-none\",\n className,\n )}\n style={{\n stroke,\n strokeWidth,\n ...(strokeDasharray ? { strokeDasharray } : null),\n ...(strokeOpacity !== undefined ? { strokeOpacity } : null),\n ...style,\n }}\n />\n </>\n );\n}\n","import {\n getBezierPath,\n useInternalNode,\n type EdgeProps,\n type InternalNode,\n type Node,\n type Position,\n} from \"@xyflow/react\";\nimport { FlowEdgePath } from \"../flow-edge-path\";\nimport type { FlowHandleSide, FlowNodeData } from \"../flow-node\";\nimport {\n pickClosestAnchors,\n pickClosestHandles,\n positionToSide,\n sideToPosition,\n type HandleAnchor,\n type NodeRect,\n} from \"./smart-edge-geometry\";\n\ntype HandleKind = \"source\" | \"target\";\n\nfunction toRect(node: InternalNode): NodeRect {\n return {\n x: node.internals.positionAbsolute.x,\n y: node.internals.positionAbsolute.y,\n width: node.measured.width ?? 0,\n height: node.measured.height ?? 0,\n };\n}\n\n/**\n * The candidate anchors for one end of the edge, read from React Flow's\n * **measured** handle bounds — the DOM truth about where each dot was painted.\n *\n * `handleBounds` entries are node-relative, so the node's absolute position is\n * added back; the dot's centre is the entry's own box centre, which is why this\n * tracks any handle size or offset the node's CSS chooses instead of assuming\n * the dot sits on the side's midpoint.\n *\n * `handleId` pins the result to one handle when the edge names one\n * (`sourceHandle`/`targetHandle`), so an explicitly wired edge is never\n * re-routed to a different dot.\n */\nfunction anchorsOf(\n node: InternalNode,\n kind: HandleKind,\n handleId: string | null | undefined,\n): HandleAnchor[] {\n const bounds = node.internals.handleBounds?.[kind] ?? [];\n const origin = node.internals.positionAbsolute;\n return bounds\n .filter((handle) => (handleId ? handle.id === handleId : true))\n .map((handle) => ({\n id: handle.id ?? null,\n x: origin.x + handle.x + handle.width / 2,\n y: origin.y + handle.y + handle.height / 2,\n side: positionToSide[handle.position],\n }));\n}\n\n/**\n * The sides a `FlowNode` genuinely renders a handle on, for the pre-measurement\n * fallback only: its `data.handles` config when it has one, otherwise the\n * single default handle React Flow's layout direction placed\n * (`targetPosition`/`sourcePosition`, defaulting to top-in / bottom-out).\n */\nfunction declaredSides(node: InternalNode, kind: HandleKind): FlowHandleSide[] {\n const userNode = node.internals.userNode as Node<Record<string, unknown>>;\n const data = userNode.data as FlowNodeData | undefined;\n const configured = data?.handles?.[kind];\n if (configured?.length) return [...configured];\n const position = (kind === \"target\" ? userNode.targetPosition : userNode.sourcePosition) as\n | Position\n | undefined;\n if (position) return [positionToSide[position]];\n return [kind === \"target\" ? \"top\" : \"bottom\"];\n}\n\n/**\n * Branded edge that picks the closest source/target **handle** pair and routes a\n * bezier between them. Anchors are recomputed every render, so they flip as\n * nodes are dragged. Register it in `edgeTypes={{ smart: FlowSmartEdge }}`; give\n * the connected nodes a `data.handles` config (e.g. `FLOW_ALL_SIDE_HANDLES`) to\n * offer it more than the default two anchors. Uses the `--flow-edge` token,\n * matching `FlowEdge`.\n *\n * ## The path terminates ON the handle dot\n *\n * The endpoints come from React Flow's **measured `handleBounds`** — the centre\n * of the rendered dot — not from a point derived from the node's rectangle.\n * That is a correctness property, not a refinement: an earlier version slid each\n * anchor along the chosen side toward the other node (to fan out edges sharing a\n * side) and picked sides from a four-way fallback list, so a line could meet the\n * node up to half a side away from any dot — measured at ~22px on multi-side\n * nodes and ~124px on nodes carrying only the default top/bottom handles, where\n * a left/right side with no handle at all could be chosen. Two edges leaving the\n * same handle now leave from the same point and diverge, exactly as React Flow's\n * own edges do.\n */\nexport function FlowSmartEdge({\n id,\n source,\n target,\n sourceHandleId,\n targetHandleId,\n markerEnd,\n style,\n}: EdgeProps) {\n const sourceNode = useInternalNode(source);\n const targetNode = useInternalNode(target);\n if (!sourceNode || !targetNode) return null;\n\n const picked = pickClosestAnchors(\n anchorsOf(sourceNode, \"source\", sourceHandleId),\n anchorsOf(targetNode, \"target\", targetHandleId),\n );\n\n let sx: number;\n let sy: number;\n let tx: number;\n let ty: number;\n let sourcePosition: Position;\n let targetPosition: Position;\n\n if (picked) {\n ({ x: sx, y: sy } = picked.source);\n ({ x: tx, y: ty } = picked.target);\n sourcePosition = sideToPosition[picked.source.side];\n targetPosition = sideToPosition[picked.target.side];\n } else {\n // Handles not measured yet (first render). Fall back to the side midpoints\n // of the sides the nodes actually declare, so the edge is drawn sanely for\n // the frame or two before React Flow reports real handle bounds.\n const fallback = pickClosestHandles(\n toRect(sourceNode),\n declaredSides(sourceNode, \"source\"),\n toRect(targetNode),\n declaredSides(targetNode, \"target\"),\n );\n ({ sx, sy, tx, ty } = fallback);\n sourcePosition = sideToPosition[fallback.sourceSide];\n targetPosition = sideToPosition[fallback.targetSide];\n }\n\n const [edgePath] = getBezierPath({\n sourceX: sx,\n sourceY: sy,\n sourcePosition,\n targetX: tx,\n targetY: ty,\n targetPosition,\n });\n\n return (\n <FlowEdgePath\n id={id}\n path={edgePath}\n markerEnd={markerEnd}\n data-slot=\"flow-smart-edge\"\n stroke=\"var(--flow-edge)\"\n strokeWidth={1.5}\n style={style}\n />\n );\n}\n","import { Position } from \"@xyflow/react\";\nimport type { FlowHandleSide } from \"../flow-node\";\n\n/** Axis-aligned node rectangle in absolute flow coordinates. */\nexport interface NodeRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/** All four candidate handle sides, in a stable order. */\nexport const HANDLE_SIDES: FlowHandleSide[] = [\"top\", \"right\", \"bottom\", \"left\"];\n\n/** Maps a handle side to the React Flow `Position` used for bezier control. */\nexport const sideToPosition: Record<FlowHandleSide, Position> = {\n top: Position.Top,\n right: Position.Right,\n bottom: Position.Bottom,\n left: Position.Left,\n};\n\n/** Maps a React Flow `Position` back to the handle side it names. */\nexport const positionToSide: Record<Position, FlowHandleSide> = {\n [Position.Top]: \"top\",\n [Position.Right]: \"right\",\n [Position.Bottom]: \"bottom\",\n [Position.Left]: \"left\",\n};\n\n/**\n * One candidate connection point: the **measured centre of a rendered handle\n * dot**, in absolute flow coordinates, plus the side it sits on.\n *\n * This is the unit `FlowSmartEdge` routes between. Anchoring on a measured\n * handle — rather than on a point derived from the node's rectangle — is what\n * guarantees the drawn path terminates exactly on the dot the user sees,\n * whatever the handle's size, offset or CSS. See {@link pickClosestAnchors}.\n */\nexport interface HandleAnchor {\n /** The handle's `id`, when it has one (`FlowNode` uses the side name). */\n id: string | null;\n /** Absolute x of the handle dot's centre. */\n x: number;\n /** Absolute y of the handle dot's centre. */\n y: number;\n /** The node side the handle sits on — the bezier's control direction. */\n side: FlowHandleSide;\n}\n\n/** The chosen source/target anchor pair. */\nexport interface ClosestAnchors {\n source: HandleAnchor;\n target: HandleAnchor;\n}\n\n/**\n * Picks the source/target pair of **rendered handles** with the shortest\n * straight-line distance between them.\n *\n * Returns `undefined` when either side has no candidates, so the caller can\n * fall back to rectangle geometry for a node whose handles have not been\n * measured yet (React Flow populates `handleBounds` on its first measurement\n * pass; before that there is nothing to anchor to).\n */\nexport function pickClosestAnchors(\n sources: HandleAnchor[],\n targets: HandleAnchor[],\n): ClosestAnchors | undefined {\n if (!sources.length || !targets.length) return undefined;\n\n let best: ClosestAnchors | undefined;\n let bestDist = Infinity;\n\n for (const source of sources) {\n for (const target of targets) {\n const dx = target.x - source.x;\n const dy = target.y - source.y;\n const dist = dx * dx + dy * dy;\n if (dist < bestDist) {\n bestDist = dist;\n best = { source, target };\n }\n }\n }\n\n return best;\n}\n\n/** Absolute coordinate of a handle on the given side (the side's midpoint). */\nexport function handlePoint(rect: NodeRect, side: FlowHandleSide): { x: number; y: number } {\n switch (side) {\n case \"top\":\n return { x: rect.x + rect.width / 2, y: rect.y };\n case \"bottom\":\n return { x: rect.x + rect.width / 2, y: rect.y + rect.height };\n case \"left\":\n return { x: rect.x, y: rect.y + rect.height / 2 };\n case \"right\":\n return { x: rect.x + rect.width, y: rect.y + rect.height / 2 };\n }\n}\n\n/** The side pair picked by {@link pickClosestHandles}, with both anchor points. */\nexport interface ClosestHandles {\n sourceSide: FlowHandleSide;\n targetSide: FlowHandleSide;\n sx: number;\n sy: number;\n tx: number;\n ty: number;\n}\n\n/**\n * Rectangle-only fallback: picks the closest pair of **side midpoints** from the\n * candidate sides of each node.\n *\n * `FlowSmartEdge` prefers {@link pickClosestAnchors} (measured handle centres)\n * and only reaches for this before React Flow has measured the nodes. Pass the\n * sides each node genuinely renders a handle on — passing sides that carry no\n * handle produces an anchor floating on a bare border, which is the defect this\n * module exists to avoid.\n */\nexport function pickClosestHandles(\n source: NodeRect,\n sourceSides: FlowHandleSide[],\n target: NodeRect,\n targetSides: FlowHandleSide[],\n): ClosestHandles {\n const srcSides = sourceSides.length ? sourceSides : HANDLE_SIDES;\n const tgtSides = targetSides.length ? targetSides : HANDLE_SIDES;\n\n let best: ClosestHandles | undefined;\n let bestDist = Infinity;\n\n for (const s of srcSides) {\n const sp = handlePoint(source, s);\n for (const t of tgtSides) {\n const tp = handlePoint(target, t);\n const dx = tp.x - sp.x;\n const dy = tp.y - sp.y;\n const dist = dx * dx + dy * dy;\n if (dist < bestDist) {\n bestDist = dist;\n best = { sourceSide: s, targetSide: t, sx: sp.x, sy: sp.y, tx: tp.x, ty: tp.y };\n }\n }\n }\n\n // srcSides/tgtSides are non-empty (fallback above), so best is always set.\n return best as ClosestHandles;\n}\n","import { getBezierPath, useInternalNode, type EdgeProps } from \"@xyflow/react\";\nimport { FlowEdgePath } from \"../flow-edge-path\";\nimport { getEdgeParams } from \"./floating-edge-geometry\";\n\n/** Optional per-edge `data` for {@link FlowFloatingEdge}. */\nexport interface FloatingEdgeData {\n /**\n * Show a small anchor dot at each border connection point. On by default so the\n * edge visibly terminates on the node's closest side (rather than a bare line\n * touching the border with no anchor). Set `false` to hide.\n */\n anchors?: boolean;\n [key: string]: unknown;\n}\n\n/** Radius of the connection anchor dot (matches the `FlowNode` handle size). */\nconst ANCHOR_RADIUS = 4;\n\n/**\n * Branded floating edge: it attaches to the node **border** at the point facing\n * the other node (no fixed handle), recomputed as nodes drag. Register it in\n * `edgeTypes={{ floating: FlowFloatingEdge }}`; the connected nodes need no\n * handle config. Uses the `--flow-edge` token, matching `FlowEdge`.\n *\n * A small **anchor dot** is drawn at each connection point (on by default) so the\n * line clearly terminates on the node's closest border side — not at a bare,\n * unanchored spot. Toggle per edge with `data.anchors: false`.\n */\nexport function FlowFloatingEdge({ id, source, target, markerEnd, style, data }: EdgeProps) {\n const sourceNode = useInternalNode(source);\n const targetNode = useInternalNode(target);\n if (!sourceNode || !targetNode) return null;\n\n const { sx, sy, tx, ty, sourcePos, targetPos } = getEdgeParams(sourceNode, targetNode);\n\n const [edgePath] = getBezierPath({\n sourceX: sx,\n sourceY: sy,\n sourcePosition: sourcePos,\n targetX: tx,\n targetY: ty,\n targetPosition: targetPos,\n });\n\n const showAnchors = (data as FloatingEdgeData | undefined)?.anchors !== false;\n\n return (\n <>\n <FlowEdgePath\n id={id}\n path={edgePath}\n markerEnd={markerEnd}\n data-slot=\"flow-floating-edge\"\n stroke=\"var(--flow-edge)\"\n strokeWidth={1.5}\n style={style}\n />\n {showAnchors ? (\n <g\n className=\"brand-floating-edge__anchors\"\n fill=\"var(--flow-node)\"\n stroke=\"var(--flow-edge)\"\n >\n {/* Decorative rings matching the FlowNode handle look; never intercept\n pointer events so nodes stay draggable through them. */}\n <circle\n cx={sx}\n cy={sy}\n r={ANCHOR_RADIUS}\n strokeWidth={2}\n style={{ pointerEvents: \"none\" }}\n aria-hidden=\"true\"\n />\n <circle\n cx={tx}\n cy={ty}\n r={ANCHOR_RADIUS}\n strokeWidth={2}\n style={{ pointerEvents: \"none\" }}\n aria-hidden=\"true\"\n />\n </g>\n ) : null}\n </>\n );\n}\n","import { Position } from \"@xyflow/react\";\n\n/**\n * The minimal live node geometry `getEdgeParams` needs. A React Flow\n * `InternalNode` satisfies this structurally (`internals.positionAbsolute` +\n * `measured`), so you can pass one directly; tests can pass a plain fake.\n */\nexport interface FloatingNodeGeometry {\n internals: { positionAbsolute: { x: number; y: number } };\n measured: { width?: number; height?: number };\n}\n\ninterface Point {\n x: number;\n y: number;\n}\n\n/**\n * The point where the line from `intersectionNode`'s center to `targetNode`'s\n * center crosses `intersectionNode`'s rectangle border. Standard React Flow\n * floating-edge math (ellipse-normalized).\n */\nfunction getNodeIntersection(\n intersectionNode: FloatingNodeGeometry,\n targetNode: FloatingNodeGeometry,\n): Point {\n const w = (intersectionNode.measured.width ?? 0) / 2;\n const h = (intersectionNode.measured.height ?? 0) / 2;\n const pos = intersectionNode.internals.positionAbsolute;\n const targetPos = targetNode.internals.positionAbsolute;\n\n const x2 = pos.x + w;\n const y2 = pos.y + h;\n const x1 = targetPos.x + (targetNode.measured.width ?? 0) / 2;\n const y1 = targetPos.y + (targetNode.measured.height ?? 0) / 2;\n\n // A not-yet-measured node has w/h = 0; guard the divisions so we return the\n // node's position (finite) instead of NaN (which would break the edge path and\n // make the line appear to end at a random spot until measurement lands).\n const safeW = w || 1;\n const safeH = h || 1;\n\n const xx1 = (x1 - x2) / (2 * safeW) - (y1 - y2) / (2 * safeH);\n const yy1 = (x1 - x2) / (2 * safeW) + (y1 - y2) / (2 * safeH);\n const a = 1 / (Math.abs(xx1) + Math.abs(yy1) || 1);\n const xx3 = a * xx1;\n const yy3 = a * yy1;\n const x = safeW * (xx3 + yy3) + x2;\n const y = safeH * (-xx3 + yy3) + y2;\n\n return { x, y };\n}\n\n/**\n * Snap `point` exactly onto `node`'s nearest border side. The intersection math\n * already lands on the border for a measured node; this guarantees it (removing\n * any sub-pixel drift that would leave the anchor a hair inside the node) and\n * gives a sensible on-border fallback if a point ever lands inside. Returns the\n * point unchanged when the node has no measured size yet.\n */\nfunction clampToBorder(point: Point, node: FloatingNodeGeometry): Point {\n const x0 = node.internals.positionAbsolute.x;\n const y0 = node.internals.positionAbsolute.y;\n const w = node.measured.width ?? 0;\n const h = node.measured.height ?? 0;\n if (w <= 0 || h <= 0) return point;\n\n const cx = Math.min(Math.max(point.x, x0), x0 + w);\n const cy = Math.min(Math.max(point.y, y0), y0 + h);\n // Distance from the (clamped-inside) point to each side; snap to the nearest.\n const dLeft = cx - x0;\n const dRight = x0 + w - cx;\n const dTop = cy - y0;\n const dBottom = y0 + h - cy;\n const nearest = Math.min(dLeft, dRight, dTop, dBottom);\n if (nearest === dLeft) return { x: x0, y: cy };\n if (nearest === dRight) return { x: x0 + w, y: cy };\n if (nearest === dTop) return { x: cx, y: y0 };\n return { x: cx, y: y0 + h };\n}\n\n/** Which border of `node` the intersection `point` sits on. */\nfunction getEdgeBorderPosition(node: FloatingNodeGeometry, point: Point): Position {\n const nx = Math.round(node.internals.positionAbsolute.x);\n const ny = Math.round(node.internals.positionAbsolute.y);\n const px = Math.round(point.x);\n const py = Math.round(point.y);\n const w = node.measured.width ?? 0;\n const h = node.measured.height ?? 0;\n\n if (px <= nx + 1) return Position.Left;\n if (px >= nx + w - 1) return Position.Right;\n if (py <= ny + 1) return Position.Top;\n if (py >= ny + h - 1) return Position.Bottom;\n return Position.Top;\n}\n\n/** Border anchor points + `Position`s for a floating edge between two nodes. */\nexport interface EdgeParams {\n sx: number;\n sy: number;\n tx: number;\n ty: number;\n sourcePos: Position;\n targetPos: Position;\n}\n\n/**\n * Computes where a floating edge attaches to each node's border — the\n * intersection of the center-to-center line with each rectangle — plus the\n * border `Position`. Recompute per render so anchors track dragging nodes.\n */\nexport function getEdgeParams(\n source: FloatingNodeGeometry,\n target: FloatingNodeGeometry,\n): EdgeParams {\n const sourceIntersection = clampToBorder(getNodeIntersection(source, target), source);\n const targetIntersection = clampToBorder(getNodeIntersection(target, source), target);\n\n return {\n sx: sourceIntersection.x,\n sy: sourceIntersection.y,\n tx: targetIntersection.x,\n ty: targetIntersection.y,\n sourcePos: getEdgeBorderPosition(source, sourceIntersection),\n targetPos: getEdgeBorderPosition(target, targetIntersection),\n };\n}\n","import { Handle, Position, type Node, type NodeProps } from \"@xyflow/react\";\nimport { Plus } from \"lucide-react\";\nimport { FLOW_HANDLE_ANCHOR_CLASS } from \"../flow-handle/flow-handle-anchor\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\n\nexport interface FlowPlaceholderNodeData extends Record<string, unknown> {\n /** Label rendered inside the placeholder. @default \"Add node\" */\n label?: string;\n /** Called when the placeholder is activated (click, Enter, or Space). */\n onActivate?: () => void;\n}\n\nexport type BrandFlowPlaceholderNode = Node<FlowPlaceholderNodeData, \"placeholder\">;\n\n/**\n * Dashed, muted \"add here\" node — a clickable affordance that grows the\n * graph. Register it in `nodeTypes={{ placeholder: FlowPlaceholderNode }}`\n * and create nodes with `type: \"placeholder\"` and `data: FlowPlaceholderNodeData`.\n *\n * Renders a real `<button>` (keyboard-activatable via native Enter/Space) with\n * an `aria-label`, and a single **target** `<Handle>` (top) so an existing\n * edge can point at it. Fires `data.onActivate?.()` on activation — the\n * typical handler converts the placeholder into a real `FlowNode` and grows a\n * fresh placeholder beneath it (see the \"Placeholder tail\" story).\n */\nexport function FlowPlaceholderNode({ data }: NodeProps<BrandFlowPlaceholderNode>) {\n const label = data.label ?? \"Add node\";\n return (\n <div className=\"relative\">\n <Handle\n type=\"target\"\n position={Position.Top}\n // `FLOW_HANDLE_ANCHOR_CLASS`: a connector dot must never be in flight when\n // React Flow measures it. See `flow-handle/flow-handle-anchor.ts`.\n className={`!size-2 !border-2 !border-flow-edge !bg-flow-node ${FLOW_HANDLE_ANCHOR_CLASS}`}\n />\n <button\n type=\"button\"\n aria-label={label}\n onClick={() => data.onActivate?.()}\n className={cn(\n \"flex min-w-44 items-center justify-center gap-1.5 rounded-lg border border-dashed border-flow-group-border bg-flow-group px-3 py-2 text-muted-foreground\",\n \"transition-colors duration-fast ease-standard hover:bg-accent hover:text-accent-foreground\",\n \"focus-ring\",\n )}\n >\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n <span className=\"text-body\">{label}</span>\n </button>\n </div>\n );\n}\n","import { EdgeLabelRenderer, getBezierPath, type Edge, type EdgeProps } from \"@xyflow/react\";\nimport { Plus } from \"lucide-react\";\nimport { FlowEdgePath } from \"../flow-edge-path\";\n\nexport interface FlowButtonEdgeData extends Record<string, unknown> {\n /** aria-label for the insert button. @default \"Insert node on edge\" */\n label?: string;\n /** Called when the edge's \"+\" button is activated (click, Enter, or Space). */\n onInsert?: () => void;\n}\n\nexport type BrandFlowButtonEdge = Edge<FlowButtonEdgeData, \"button\">;\n\n/**\n * Branded bezier edge (matching `FlowEdge`'s `--flow-edge` token) with a\n * centered \"+\" button rendered via `EdgeLabelRenderer` at the edge midpoint.\n * Register it in `edgeTypes={{ button: FlowButtonEdge }}` and create edges\n * with `type: \"button\"` and `data: FlowButtonEdgeData`.\n *\n * The button is a real `<button>` with an `aria-label`, keyboard-activatable\n * via native Enter/Space. Fires `data.onInsert?.()` on activation — the\n * typical handler splits the edge: insert a new node between source and\n * target and rewire the two edges (see the \"Insert between\" story).\n */\nexport function FlowButtonEdge({\n id,\n sourceX,\n sourceY,\n targetX,\n targetY,\n sourcePosition,\n targetPosition,\n markerEnd,\n style,\n data,\n}: EdgeProps<BrandFlowButtonEdge>) {\n const [edgePath, labelX, labelY] = getBezierPath({\n sourceX,\n sourceY,\n sourcePosition,\n targetX,\n targetY,\n targetPosition,\n });\n const label = data?.label ?? \"Insert node on edge\";\n\n return (\n <>\n <FlowEdgePath\n id={id}\n path={edgePath}\n markerEnd={markerEnd}\n data-slot=\"flow-button-edge\"\n stroke=\"var(--flow-edge)\"\n strokeWidth={1.5}\n style={style}\n />\n <EdgeLabelRenderer>\n <div\n style={{\n position: \"absolute\",\n transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,\n }}\n className=\"nodrag nopan pointer-events-auto\"\n >\n <button\n type=\"button\"\n aria-label={label}\n onClick={() => data?.onInsert?.()}\n className=\"flex size-5 items-center justify-center rounded-full border border-flow-group-border bg-flow-node text-flow-node-foreground shadow-sm transition-colors duration-fast ease-standard hover:bg-accent hover:text-accent-foreground focus-ring\"\n >\n <Plus className=\"size-3\" aria-hidden=\"true\" />\n </button>\n </div>\n </EdgeLabelRenderer>\n </>\n );\n}\n","import dagre from \"@dagrejs/dagre\";\nimport { Position } from \"@xyflow/react\";\nimport type { Edge, Node } from \"@xyflow/react\";\n\n/** The graphlib graph `dagre.graphlib.Graph` produces — dagre exports no standalone type for it. */\ntype DagreGraph = InstanceType<typeof dagre.graphlib.Graph>;\n\n/**\n * The fields dagre writes back onto a node label during `layout()`. `rank` is\n * absent from dagre's own `.d.ts` (it is documented output, not declared\n * output), so it is narrowed here rather than asserted at the call site.\n */\ninterface DagreNodeLabel {\n x: number;\n y: number;\n rank?: number;\n}\n\n/** Direction dagre lays the graph out in — top-to-bottom, left-to-right, etc. */\nexport type FlowLayoutDirection = \"TB\" | \"LR\" | \"BT\" | \"RL\";\n\n/**\n * The handle sides an edge should attach to for each layout direction. A\n * directional layout must move the anchors with it: a left-to-right flow exits\n * the source's RIGHT and enters the target's LEFT; top-to-bottom uses\n * bottom/top. `layoutFlow` stamps these onto every node so `FlowNode` renders\n * its default handles on the correct sides (and `FlowEdge` routes accordingly)\n * — otherwise every layout is stuck with top/bottom handles.\n */\nexport const HANDLE_BY_DIRECTION: Record<\n FlowLayoutDirection,\n { source: Position; target: Position }\n> = {\n TB: { source: Position.Bottom, target: Position.Top },\n BT: { source: Position.Top, target: Position.Bottom },\n LR: { source: Position.Right, target: Position.Left },\n RL: { source: Position.Left, target: Position.Right },\n};\n\nexport interface FlowLayoutOptions {\n /** Layout direction. @default \"TB\" */\n direction?: FlowLayoutDirection;\n /** Gap between nodes in the same rank (dagre `nodesep`). @default 48 */\n nodeSpacing?: number;\n /** Gap between ranks (dagre `ranksep`). @default 72 */\n rankSpacing?: number;\n}\n\nexport interface FlowLayoutResult<NodeType extends Node = Node, EdgeType extends Edge = Edge> {\n nodes: NodeType[];\n edges: EdgeType[];\n /**\n * Ids of edges that run **against** the layout direction — a rework / retry\n * loop in a process graph. dagre breaks cycles by reversing such edges\n * internally and never surfaces which ones it reversed, so this is derived\n * from the ranks dagre stamps on the laid-out graph: an edge whose source\n * ranks at or after its target went backwards. Render these with\n * `FlowWeightedEdge`'s `variant=\"back\"`.\n */\n backEdges: string[];\n /**\n * Ids of edges whose `source === target`. dagre does not lay out self-loops,\n * so they are withheld from the graph entirely (never `setEdge`-ed) and are\n * returned unchanged in `edges` — they take part in no rank computation and\n * cannot distort the layout. Render these with `FlowSelfLoopEdge`.\n */\n selfLoops: string[];\n}\n\n/** Fallback size used when a node hasn't been measured yet (React Flow's own default node width). */\nconst DEFAULT_NODE_WIDTH = 172;\nconst DEFAULT_NODE_HEIGHT = 40;\n\nfunction nodeSize(node: Node): { width: number; height: number } {\n return {\n width: node.measured?.width ?? node.width ?? DEFAULT_NODE_WIDTH,\n height: node.measured?.height ?? node.height ?? DEFAULT_NODE_HEIGHT,\n };\n}\n\n/**\n * Pure dagre-powered auto layout. Computes new `position`s for `nodes` given\n * `edges` — no React, no side effects, safe to call anywhere (including\n * outside a component or in a test).\n *\n * Respects each node's **measured** size (`node.measured` — how React Flow\n * v12 reports actual rendered dimensions), falling back to `node.width`/\n * `node.height`, then a sensible default. Node identity and `data` are left\n * untouched — only `position` changes.\n *\n * Also reports the graph's two structural signals — `backEdges` (edges that run\n * against the layout direction) and `selfLoops` (`source === target`). Both are\n * additive fields on the result; a caller that only destructures\n * `{ nodes, edges }` is unaffected.\n *\n * Pair with `useFlowLayout` to apply the result to a live canvas.\n */\nexport function layoutFlow<NodeType extends Node = Node, EdgeType extends Edge = Edge>(\n nodes: NodeType[],\n edges: EdgeType[],\n options: FlowLayoutOptions = {},\n): FlowLayoutResult<NodeType, EdgeType> {\n const { direction = \"TB\", nodeSpacing = 48, rankSpacing = 72 } = options;\n\n const graph = new dagre.graphlib.Graph();\n graph.setDefaultEdgeLabel(() => ({}));\n graph.setGraph({ rankdir: direction, nodesep: nodeSpacing, ranksep: rankSpacing });\n\n for (const node of nodes) {\n const { width, height } = nodeSize(node);\n graph.setNode(node.id, { width, height });\n }\n\n // Self-loops are withheld from dagre entirely: dagre does not lay them out,\n // and feeding them in only perturbs the ranks of a graph they say nothing\n // about. They are re-attached untouched in the returned `edges`.\n const selfLoops: string[] = [];\n for (const edge of edges) {\n if (edge.source === edge.target) {\n selfLoops.push(edge.id);\n continue;\n }\n graph.setEdge(edge.source, edge.target);\n }\n\n dagre.layout(graph);\n\n const backEdges = collectBackEdges(graph, edges);\n\n const handles = HANDLE_BY_DIRECTION[direction];\n const layoutedNodes = nodes.map((node) => {\n const dagreNode = graph.node(node.id);\n const { width, height } = nodeSize(node);\n // dagre positions nodes by their center; React Flow positions by top-left.\n return {\n ...node,\n // Move the anchors with the layout direction so `FlowNode` puts its\n // handles on the sides the edges actually leave/enter.\n sourcePosition: handles.source,\n targetPosition: handles.target,\n position: {\n x: dagreNode.x - width / 2,\n y: dagreNode.y - height / 2,\n },\n };\n });\n\n return { nodes: layoutedNodes, edges, backEdges, selfLoops };\n}\n\n/**\n * Which edges dagre had to run backwards, derived from the ranks it stamps on\n * the laid-out graph (`graph.node(id).rank`).\n *\n * The obvious alternative — reading `graph.edge(e).reversed` after dagre's\n * `acyclic.run` — is **not usable on the pinned `@dagrejs/dagre` 3.0.0**:\n * cycle breaking happens on an internal copy of the graph, and the caller's\n * graph carries no `reversed` flag once `dagre.layout()` returns (verified\n * against the installed version — every edge label is bare `{ points }`).\n * `rank` *is* on the public graph, and is direction-independent: it counts up\n * along the flow for every `rankdir`, so `rank(source) >= rank(target)` means\n * \"this edge does not advance the process\" in TB, BT, LR and RL alike.\n *\n * `>=` rather than `>` on purpose: a same-rank edge between two siblings is\n * not forward progress either, and dagre would have had to reverse or flatten\n * it. Self-loops never reach here — they are filtered out before layout.\n * An edge naming a node that isn't in the graph has no ranks to compare and is\n * left out rather than guessed at.\n */\nfunction collectBackEdges(graph: DagreGraph, edges: Edge[]): string[] {\n const backEdges: string[] = [];\n for (const edge of edges) {\n if (edge.source === edge.target) continue;\n const sourceRank = (graph.node(edge.source) as DagreNodeLabel | undefined)?.rank;\n const targetRank = (graph.node(edge.target) as DagreNodeLabel | undefined)?.rank;\n if (typeof sourceRank !== \"number\" || typeof targetRank !== \"number\") continue;\n if (sourceRank >= targetRank) backEdges.push(edge.id);\n }\n return backEdges;\n}\n","import { useCallback, useRef, useState } from \"react\";\nimport { useNodesInitialized, useReactFlow, type Edge, type Node } from \"@xyflow/react\";\nimport { layoutFlow, type FlowLayoutDirection, type FlowLayoutOptions } from \"./flow-layout\";\n\nexport interface UseFlowLayoutResult {\n /**\n * Auto-lay-out the current nodes/edges with dagre and fit the view. Safe to\n * call repeatedly (e.g. from a toolbar button on every click). No-ops until\n * React Flow has measured every node.\n */\n layout: (direction?: FlowLayoutDirection, options?: Omit<FlowLayoutOptions, \"direction\">) => void;\n /** Whether nodes are measured and ready to be laid out. */\n ready: boolean;\n /** Whether a layout pass is currently running (true only for the duration of `layout()`). */\n layouting: boolean;\n}\n\n/**\n * Hook form of `layoutFlow`. Reads the live nodes/edges from `useReactFlow()`,\n * waits for `useNodesInitialized()` so real (measured) node sizes are used —\n * never lays out against 0×0 unmeasured nodes — applies the computed\n * positions via `setNodes`, then calls `fitView()`.\n *\n * Must be called from a component rendered inside a `<CanvasShell>` /\n * `<ReactFlow>` (React Flow context).\n */\nexport function useFlowLayout<\n NodeType extends Node = Node,\n EdgeType extends Edge = Edge,\n>(): UseFlowLayoutResult {\n const { getNodes, getEdges, setNodes, fitView } = useReactFlow<NodeType, EdgeType>();\n const nodesInitialized = useNodesInitialized();\n const [layouting, setLayouting] = useState(false);\n // Avoid overlapping layout passes if `layout()` is invoked in quick succession.\n const runningRef = useRef(false);\n\n const layout = useCallback(\n (direction?: FlowLayoutDirection, options?: Omit<FlowLayoutOptions, \"direction\">) => {\n if (!nodesInitialized || runningRef.current) return;\n\n runningRef.current = true;\n setLayouting(true);\n try {\n const { nodes: layoutedNodes } = layoutFlow<NodeType, EdgeType>(getNodes(), getEdges(), {\n ...options,\n direction,\n });\n setNodes(layoutedNodes);\n // Defer fitView one frame so React Flow has committed the new positions first.\n requestAnimationFrame(() => fitView());\n } finally {\n runningRef.current = false;\n setLayouting(false);\n }\n },\n [nodesInitialized, getNodes, getEdges, setNodes, fitView],\n );\n\n return { layout, ready: nodesInitialized, layouting };\n}\n","import {\n forceCenter,\n forceCollide,\n forceLink,\n forceManyBody,\n forceSimulation,\n type SimulationLinkDatum,\n type SimulationNodeDatum,\n} from \"d3-force\";\nimport type { Edge, Node } from \"@xyflow/react\";\nimport { HANDLE_BY_DIRECTION, layoutFlow, type FlowLayoutDirection } from \"./flow-layout\";\n\n/** Which generic graph-geometry algorithm `layoutGraph` should run. */\nexport type LayoutAlgorithm = \"concentric\" | \"force\" | \"layered-lr\" | \"layered-tb\" | \"grid\";\n\n/** Horizontal/vertical gap used by the `\"grid\"` and `\"layered-*\"` algorithms. */\nexport interface LayoutSpacing {\n x: number;\n y: number;\n}\n\nexport interface LayoutOptions {\n /** Which layout algorithm to run. */\n algorithm: LayoutAlgorithm;\n /**\n * `\"concentric\"` only — the focal node placed at the origin. Defaults to the\n * highest-degree node when omitted (or when the id doesn't match a node).\n */\n centerId?: string;\n /** `\"concentric\"` only — radius of ring 1; ring N sits at `N × ringRadius`. @default 180 */\n ringRadius?: number;\n /** `\"force\"` only — number of synchronous simulation ticks to run. @default 300 */\n iterations?: number;\n /** `\"grid\"` / `\"layered-lr\"` / `\"layered-tb\"` — gap between nodes. @default {x:200,y:120} for grid. */\n spacing?: LayoutSpacing;\n /** Resolve a node's size by id. Falls back to `measured`/`width`/`height`/a sensible default. */\n nodeSize?: (id: string) => { width: number; height: number };\n}\n\nconst DEFAULT_NODE_WIDTH = 172;\nconst DEFAULT_NODE_HEIGHT = 40;\nconst DEFAULT_RING_RADIUS = 180;\nconst DEFAULT_FORCE_ITERATIONS = 300;\nconst DEFAULT_GRID_SPACING: LayoutSpacing = { x: 200, y: 120 };\n/** Base charge/link/collide tuning — chosen to read well at brand-ui's default node size. */\nconst FORCE_CHARGE_STRENGTH = -300;\nconst FORCE_LINK_DISTANCE = 150;\n\nfunction resolveNodeSize(\n node: Node,\n nodeSize?: (id: string) => { width: number; height: number },\n): { width: number; height: number } {\n if (nodeSize) return nodeSize(node.id);\n return {\n width: node.measured?.width ?? node.width ?? DEFAULT_NODE_WIDTH,\n height: node.measured?.height ?? node.height ?? DEFAULT_NODE_HEIGHT,\n };\n}\n\n/**\n * Pure graph-geometry auto layout. Computes new `position`s for `nodes` given\n * `edges` and a `LayoutAlgorithm` — no React, no DOM, no side effects, safe to\n * call anywhere (including outside a component or in a test). Deterministic:\n * identical `nodes`/`edges`/`options` always produce identical positions.\n *\n * Node identity and `data` are left untouched — only `position` changes.\n * Pair with `useAutoLayout` for a memoized hook form.\n *\n * - `\"layered-lr\"` / `\"layered-tb\"` delegate to the dagre-powered `layoutFlow`\n * (direction `\"LR\"` / `\"TB\"`). `spacing.x` is always the horizontal gap and\n * `spacing.y` the vertical one, so the two differ only in which axis carries\n * the ranks.\n * - `\"concentric\"` places `centerId` (or the highest-degree node) at the\n * origin, with BFS shells at `ring × ringRadius`; disconnected nodes land in\n * one extra outer ring so positions are never `NaN`.\n * - `\"force\"` runs `d3-force` synchronously for a fixed number of `iterations`,\n * seeded on a circle by index (no randomness) so runs are reproducible.\n * - `\"grid\"` lays nodes out row-major using `spacing`.\n */\nexport function layoutGraph<NodeType extends Node = Node, EdgeType extends Edge = Edge>(\n nodes: NodeType[],\n edges: EdgeType[],\n options: LayoutOptions,\n): NodeType[] {\n if (nodes.length === 0) return [];\n\n switch (options.algorithm) {\n case \"layered-lr\":\n return layoutLayered(nodes, edges, options, \"LR\");\n case \"layered-tb\":\n return layoutLayered(nodes, edges, options, \"TB\");\n case \"concentric\":\n return layoutConcentric(nodes, edges, options);\n case \"force\":\n return layoutForce(nodes, edges, options);\n case \"grid\":\n return layoutGrid(nodes, options);\n default: {\n const exhaustiveCheck: never = options.algorithm;\n throw new Error(`layoutGraph: unknown algorithm \"${String(exhaustiveCheck)}\"`);\n }\n }\n}\n\nfunction layoutLayered<NodeType extends Node, EdgeType extends Edge>(\n nodes: NodeType[],\n edges: EdgeType[],\n options: LayoutOptions,\n direction: FlowLayoutDirection,\n): NodeType[] {\n const { spacing, nodeSize } = options;\n\n // dagre (via layoutFlow) reads sizes off `measured`/`width`/`height` — feed it\n // resolved sizes on a throwaway copy so a caller-supplied `nodeSize` is honored\n // without mutating (or permanently resizing) the nodes we return.\n const sizedNodes = nodeSize\n ? nodes.map((node) => {\n const { width, height } = nodeSize(node.id);\n return { ...node, width, height, measured: { width, height } };\n })\n : nodes;\n\n // Ranks progress along the layout axis and nodes stack across it, so the two\n // directions map the SAME `spacing.x`/`spacing.y` onto dagre's rank/node sep\n // with the axes swapped: LR ranks horizontally (x), TB ranks vertically (y).\n const horizontalRanks = direction === \"LR\" || direction === \"RL\";\n const { nodes: laidOut } = layoutFlow(sizedNodes, edges, {\n direction,\n nodeSpacing: horizontalRanks ? spacing?.y : spacing?.x,\n rankSpacing: horizontalRanks ? spacing?.x : spacing?.y,\n });\n\n // Carry the handle sides layoutFlow stamped on (LR right-out/left-in, TB\n // bottom-out/top-in), not just the position — otherwise the layout moves the\n // nodes but leaves the anchors on the wrong sides.\n return nodes.map((node, i) => ({\n ...node,\n sourcePosition: laidOut[i]!.sourcePosition,\n targetPosition: laidOut[i]!.targetPosition,\n position: laidOut[i]!.position,\n }));\n}\n\nfunction resolveCenterId(\n centerId: string | undefined,\n nodeIds: string[],\n adjacency: Map<string, Set<string>>,\n): string {\n if (centerId && nodeIds.includes(centerId)) return centerId;\n\n let best = nodeIds[0]!;\n let bestDegree = -1;\n for (const id of nodeIds) {\n const degree = adjacency.get(id)?.size ?? 0;\n if (degree > bestDegree) {\n bestDegree = degree;\n best = id;\n }\n }\n return best;\n}\n\nfunction layoutConcentric<NodeType extends Node, EdgeType extends Edge>(\n nodes: NodeType[],\n edges: EdgeType[],\n options: LayoutOptions,\n): NodeType[] {\n const ringRadius = options.ringRadius ?? DEFAULT_RING_RADIUS;\n const nodeIds = nodes.map((node) => node.id);\n const idSet = new Set(nodeIds);\n\n const adjacency = new Map<string, Set<string>>();\n for (const id of nodeIds) adjacency.set(id, new Set());\n for (const edge of edges) {\n if (!idSet.has(edge.source) || !idSet.has(edge.target)) continue;\n adjacency.get(edge.source)!.add(edge.target);\n adjacency.get(edge.target)!.add(edge.source);\n }\n\n const centerId = resolveCenterId(options.centerId, nodeIds, adjacency);\n\n // BFS shells from the center — hop distance doubles as the ring index.\n const hopOf = new Map<string, number>();\n hopOf.set(centerId, 0);\n const queue: string[] = [centerId];\n let maxHop = 0;\n while (queue.length > 0) {\n const current = queue.shift()!;\n const currentHop = hopOf.get(current)!;\n for (const neighbor of adjacency.get(current) ?? []) {\n if (hopOf.has(neighbor)) continue;\n const hop = currentHop + 1;\n hopOf.set(neighbor, hop);\n maxHop = Math.max(maxHop, hop);\n queue.push(neighbor);\n }\n }\n\n // Disconnected nodes (unreachable from the center) get one extra ring beyond\n // the farthest reached hop — never left with a NaN/undefined position.\n const leftoverHop = maxHop + 1;\n for (const id of nodeIds) {\n if (!hopOf.has(id)) hopOf.set(id, leftoverHop);\n }\n\n const rings = new Map<number, string[]>();\n for (const id of nodeIds) {\n const hop = hopOf.get(id)!;\n if (hop === 0) continue;\n if (!rings.has(hop)) rings.set(hop, []);\n rings.get(hop)!.push(id);\n }\n\n const positionById = new Map<string, { x: number; y: number }>();\n positionById.set(centerId, { x: 0, y: 0 });\n\n for (const [hop, ids] of rings) {\n const count = ids.length;\n const angleStep = (2 * Math.PI) / count;\n // Stagger successive rings by a growing half-step so radial edges don't\n // trivially line up with the ring behind/ahead of them.\n const ringOffset = (hop - 1) * (angleStep / 2);\n const radius = hop * ringRadius;\n ids.forEach((id, i) => {\n const angle = ringOffset + i * angleStep;\n positionById.set(id, { x: radius * Math.cos(angle), y: radius * Math.sin(angle) });\n });\n }\n\n return nodes.map((node) => ({\n ...node,\n position: positionById.get(node.id) ?? { x: 0, y: 0 },\n }));\n}\n\ninterface ForceSimNode extends SimulationNodeDatum {\n id: string;\n}\n\nfunction layoutForce<NodeType extends Node, EdgeType extends Edge>(\n nodes: NodeType[],\n edges: EdgeType[],\n options: LayoutOptions,\n): NodeType[] {\n const iterations = options.iterations ?? DEFAULT_FORCE_ITERATIONS;\n const nodeIds = new Set(nodes.map((node) => node.id));\n\n // Seed initial positions deterministically on a circle by index — never\n // `Math.random()` — so two runs with the same input converge identically.\n const seedRadius = Math.max(nodes.length * 20, 100);\n const simNodes: ForceSimNode[] = nodes.map((node, i) => {\n const angle = (2 * Math.PI * i) / Math.max(nodes.length, 1);\n return { id: node.id, x: seedRadius * Math.cos(angle), y: seedRadius * Math.sin(angle) };\n });\n\n const simLinks: SimulationLinkDatum<ForceSimNode>[] = edges\n .filter((edge) => nodeIds.has(edge.source) && nodeIds.has(edge.target))\n .map((edge) => ({ source: edge.source, target: edge.target }));\n\n const collisionById = new Map(\n nodes.map((node) => {\n const { width, height } = resolveNodeSize(node, options.nodeSize);\n return [node.id, Math.max(width, height) / 2];\n }),\n );\n\n const simulation = forceSimulation(simNodes)\n .force(\"charge\", forceManyBody().strength(FORCE_CHARGE_STRENGTH))\n .force(\n \"link\",\n forceLink<ForceSimNode, SimulationLinkDatum<ForceSimNode>>(simLinks)\n .id((d) => d.id)\n .distance(FORCE_LINK_DISTANCE),\n )\n .force(\"center\", forceCenter(0, 0))\n .force(\n \"collide\",\n forceCollide<ForceSimNode>((d) => collisionById.get(d.id) ?? DEFAULT_NODE_WIDTH / 2),\n )\n .stop();\n\n for (let i = 0; i < iterations; i++) {\n simulation.tick();\n }\n\n const positionById = new Map(simNodes.map((n) => [n.id, { x: n.x ?? 0, y: n.y ?? 0 }]));\n\n return nodes.map((node) => ({\n ...node,\n position: positionById.get(node.id) ?? { x: 0, y: 0 },\n }));\n}\n\nfunction layoutGrid<NodeType extends Node>(nodes: NodeType[], options: LayoutOptions): NodeType[] {\n const spacing = options.spacing ?? DEFAULT_GRID_SPACING;\n const cols = Math.max(1, Math.ceil(Math.sqrt(nodes.length)));\n // A grid reads top-to-bottom; anchor edges bottom-out / top-in to match.\n const handles = HANDLE_BY_DIRECTION.TB;\n\n return nodes.map((node, i) => {\n const row = Math.floor(i / cols);\n const col = i % cols;\n return {\n ...node,\n sourcePosition: handles.source,\n targetPosition: handles.target,\n position: { x: col * spacing.x, y: row * spacing.y },\n };\n });\n}\n","import { useMemo } from \"react\";\nimport type { Edge, Node } from \"@xyflow/react\";\nimport { layoutGraph, type LayoutOptions } from \"./layout-graph\";\n\n/**\n * Memoized `layoutGraph` — pure graph-geometry layout (no live canvas access,\n * no side effects). Recomputes only when `nodes`, `edges`, or `options`\n * change; pass stable references (e.g. from `useNodesState`/`useEdgesState`\n * and a memoized `options` object) to avoid recomputing every render.\n *\n * Use this when you want laid-out nodes to render with (e.g. feeding\n * `useNodesState`'s initial value, or a derived/read-only view). To apply a\n * layout to a *live* React Flow instance (read current nodes, set them back,\n * fit the view), call `layoutGraph` directly from an event handler instead —\n * see the `LayoutGraph` story.\n */\nexport function useAutoLayout<NodeType extends Node = Node, EdgeType extends Edge = Edge>(\n nodes: NodeType[],\n edges: EdgeType[],\n options: LayoutOptions,\n): NodeType[] {\n return useMemo(\n () => layoutGraph<NodeType, EdgeType>(nodes, edges, options),\n [nodes, edges, options],\n );\n}\n","import { MiniMap, type MiniMapProps, type Node } from \"@xyflow/react\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\n\nexport type FlowMiniMapProps<NodeType extends Node = Node> = MiniMapProps<NodeType>;\n\n/**\n * Branded preset over React Flow's `<MiniMap>` — sources node/mask colors from\n * the `--flow-minimap-*` tokens so the overview stays theme-aware (the raw\n * `MiniMap` re-export defaults to library colors and renders theme-blind,\n * especially under a low-chroma theme). Every prop can still be overridden; render\n * inside a `<CanvasShell>` (it needs the React Flow context via `useStore`).\n */\nexport function FlowMiniMap<NodeType extends Node = Node>({\n className,\n nodeColor = \"var(--flow-minimap-node)\",\n nodeStrokeColor = \"var(--flow-minimap-node)\",\n maskColor = \"var(--flow-minimap-mask)\",\n ...props\n}: FlowMiniMapProps<NodeType>) {\n return (\n <MiniMap\n className={cn(\"!rounded-lg !bg-surface-elevated !shadow-ring-sm\", className)}\n nodeColor={nodeColor}\n nodeStrokeColor={nodeStrokeColor}\n maskColor={maskColor}\n {...props}\n />\n );\n}\n","import { type CSSProperties, type ReactNode } from \"react\";\nimport { Reveal, useCollapsiblePanel } from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\n\nconst INSPECTOR_PANEL_WIDTH = \"18rem\";\n\nexport interface InspectorPanelProps {\n title?: ReactNode;\n children: ReactNode;\n onClose?: () => void;\n /** Empty-state shown when nothing is selected. */\n emptyMessage?: ReactNode;\n /** When false, render the empty state instead of children. */\n hasSelection?: boolean;\n /**\n * Stable identity of the current selection (e.g. the selected node's id).\n * When it changes, the body replays a quick fade so it reads as \"details for\n * THIS node\". Pass the selected entity's id (or any stable key derived from\n * the selected data).\n */\n selectionKey?: string | number;\n /**\n * Collapse (research 09 step 3 — converged on the shared `@elabs-ai/components-ui`\n * `useCollapsiblePanel` base; the panel stays single-view, drill-in is\n * ContextPanel-only). Controlled `open` / uncontrolled `defaultOpen`.\n */\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n /** Which edge the panel sits on (collapse slide direction). Default `\"right\"`. */\n side?: \"left\" | \"right\";\n /** Expanded width (any CSS length). Default `\"18rem\"` (the original `w-72`). */\n width?: string;\n className?: string;\n}\n\n/**\n * Properties panel for the selected node/edge. Reusable beside a canvas (e.g.\n * inside a <SplitPanel>) or as a React Flow <Panel>. Always mounted; collapses\n * via the canonical `useCollapsiblePanel` width tween (CSS-gated, reduced-\n * motion safe) when `open`/`defaultOpen` drive it.\n */\nexport function InspectorPanel({\n title = \"Inspector\",\n children,\n onClose,\n emptyMessage = \"Select a node to see its details.\",\n hasSelection = true,\n selectionKey,\n open,\n defaultOpen = true,\n onOpenChange,\n side = \"right\",\n width = INSPECTOR_PANEL_WIDTH,\n className,\n}: InspectorPanelProps) {\n const panel = useCollapsiblePanel({\n side,\n open,\n defaultOpen,\n onOpenChange,\n widthClassName: \"w-(--inspector-panel-width)\",\n spacerCollapsedClassName: \"group-data-[state=collapsed]:w-0\",\n containerSlideClassNames: {\n left: \"left-0 group-data-[state=collapsed]:left-[calc(var(--inspector-panel-width)*-1)]\",\n right: \"right-0 group-data-[state=collapsed]:right-[calc(var(--inspector-panel-width)*-1)]\",\n },\n });\n\n return (\n <div\n className={cn(\"group relative h-full overflow-hidden\", className)}\n style={{ \"--inspector-panel-width\": width } as CSSProperties}\n inert={panel.open ? undefined : true}\n {...panel.attrs}\n >\n <div data-slot=\"inspector-panel-gap\" className={panel.spacerClassName} />\n <div\n data-slot=\"inspector-panel-container\"\n // Bounded to the host (a SplitPanel / canvas edge), not the viewport:\n // absolute within the relative group, full height (research 09 §B.2).\n className={cn(panel.containerClassName, \"absolute inset-y-0 flex h-full border-s\")}\n >\n {/* Detail-tier surface: raised `card` — robust across themes (#193). */}\n <div className=\"flex h-full w-full flex-col bg-card\">\n <div className=\"flex items-center justify-between border-b px-4 py-3\">\n <h3 className=\"text-body font-semibold text-foreground\">{title}</h3>\n {onClose ? (\n <button\n type=\"button\"\n aria-label=\"Close inspector\"\n onClick={onClose}\n className=\"rounded-sm p-1 text-muted-foreground hover:text-foreground focus-ring\"\n >\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n </button>\n ) : null}\n </div>\n {/*\n `tabIndex={0}` on a scroll container, in both branches: a region that scrolls\n but contains nothing focusable is unreachable by keyboard — a mouse user can\n read the overflow and a keyboard user cannot (axe\n `scrollable-region-focusable`). An inspector body is exactly that shape\n whenever its content is plain text, a definition list or a chart, which is\n most of the time. It carries `focus-ring` because anything that can take\n focus has to show it.\n */}\n {hasSelection ? (\n <Reveal\n key={selectionKey}\n appear=\"fade\"\n speed=\"fast\"\n tabIndex={0}\n className=\"focus-ring flex-1 overflow-y-auto p-4 text-body\"\n >\n {children}\n </Reveal>\n ) : (\n <div tabIndex={0} className=\"focus-ring flex-1 overflow-y-auto p-4 text-body\">\n <p className=\"text-muted-foreground\">{emptyMessage}</p>\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n","import { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport { computeEdgeWeightScale, type WeightedEdgeLike } from \"../flow-weighted-edge/weight-scale\";\n\nexport interface LegendItem {\n label: string;\n /** Any CSS color or token reference, e.g. \"var(--chart-1)\". */\n color: string;\n}\n\n/**\n * Categorical legend props — a swatch-and-label list. This is the shape\n * `Legend` renders when `variant` is omitted, byte-for-byte unchanged from\n * before the `\"scale\"` variant existed.\n */\nexport interface LegendCategoricalProps {\n variant?: \"categorical\";\n items: LegendItem[];\n title?: string;\n className?: string;\n}\n\n/**\n * Continuous scale legend — a reading key that explains a WIDTH or COLOR\n * encoding as a *range*, not a set of discrete categories (e.g.\n * `FlowWeightedEdge`'s `data.weight` → stroke width, `data.value` → stroke\n * color). `kind: \"width\"` reuses `computeEdgeWeightScale` — the exact pure\n * scale `FlowWeightedEdge` calls — so the sample strokes drawn here can never\n * drift from the widths a real flow's edges render for the same weights.\n */\nexport interface LegendScaleProps {\n variant: \"scale\";\n /** Which continuous encoding this legend explains. */\n kind: \"width\" | \"color\";\n /** `[min, max]` of the underlying value the ramp represents. */\n domain: [number, number];\n /** Formats a domain value for display at a tick. @default `(v) => v.toLocaleString()` */\n format?: (value: number) => string;\n /**\n * Sample count for `kind: \"width\"`: `\"minmax\"` draws a min/max pair of\n * sample strokes, `\"minmedmax\"` adds the domain midpoint as a third\n * sample. `kind: \"color\"` always renders a fixed 5-stop gradient\n * regardless of this prop — five stops is what makes a 2-endpoint color\n * ramp legible as an ordered scale (see the color-ramp a11y note below),\n * independent of how many width samples are shown.\n * @default \"minmax\"\n */\n ticks?: \"minmax\" | \"minmedmax\";\n title?: string;\n className?: string;\n}\n\nexport type LegendProps = LegendCategoricalProps | LegendScaleProps;\n\nconst defaultFormat = (value: number): string => value.toLocaleString();\n\n/**\n * Small legend mapping colors/types to labels for a canvas or chart\n * (`variant: \"categorical\"`, the default), or — via `variant=\"scale\"` — a\n * continuous width/color ramp with a domain, tick marks and formatting.\n */\nexport function Legend(props: LegendProps) {\n if (props.variant === \"scale\") {\n return <LegendScale {...props} />;\n }\n return <LegendCategorical {...props} />;\n}\n\nfunction LegendCategorical({ items, title, className }: LegendCategoricalProps) {\n return (\n <div\n className={cn(\n \"rounded-lg bg-surface-elevated/90 p-3 text-xs shadow-ring-sm backdrop-blur\",\n className,\n )}\n >\n {title ? <div className=\"mb-1.5 font-medium text-foreground\">{title}</div> : null}\n <ul className=\"space-y-1\">\n {items.map((item) => (\n <li key={item.label} className=\"flex items-center gap-2 text-muted-foreground\">\n <span\n aria-hidden=\"true\"\n className=\"size-2.5 shrink-0 rounded-full\"\n style={{ backgroundColor: item.color }}\n />\n {item.label}\n </li>\n ))}\n </ul>\n </div>\n );\n}\n\n/**\n * Width-ramp sample: a tick value plus the stroke width `computeEdgeWeightScale`\n * assigns it. The \"edges\" fed to the scale are exactly the requested tick\n * values, so the group's own min/max always equal the domain — the returned\n * widths are byte-identical to what `computeEdgeWeightScale` would compute for\n * real edges carrying these same weights (no duplicated min-max math).\n */\nfunction computeWidthSamples(\n domain: [number, number],\n ticks: \"minmax\" | \"minmedmax\",\n): { value: number; width: number }[] {\n const [min, max] = domain;\n const values = ticks === \"minmedmax\" ? [min, (min + max) / 2, max] : [min, max];\n const likeEdges: WeightedEdgeLike[] = values.map((weight, index) => ({\n id: String(index),\n data: { weight },\n }));\n const widths = computeEdgeWeightScale(likeEdges);\n return values.map((value, index) => ({ value, width: widths.get(String(index)) ?? 0 }));\n}\n\n/** Fractions along the domain the color ramp samples — fixed at 5 stops (see `LegendScaleProps.ticks`). */\nconst COLOR_STOP_FRACTIONS = [0, 0.25, 0.5, 0.75, 1] as const;\n\nfunction scaleKindLabel(kind: \"width\" | \"color\"): string {\n return kind === \"width\" ? \"width\" : \"color\";\n}\n\nfunction LegendScale({\n kind,\n domain,\n format = defaultFormat,\n ticks = \"minmax\",\n title,\n className,\n}: LegendScaleProps) {\n const [min, max] = domain;\n // e.g. \"Edge width scale, 2 to 48, minimum to maximum\" — a single accessible\n // name for the whole reading key, since it is one keyboard tab stop, not one\n // per tick.\n const ariaLabel = `Edge ${scaleKindLabel(kind)} scale, ${format(min)} to ${format(max)}, minimum to maximum`;\n\n return (\n <div\n aria-label={ariaLabel}\n className={cn(\n \"flex flex-col gap-2 rounded-lg bg-surface-elevated/90 p-3 shadow-ring-sm backdrop-blur\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n className,\n )}\n role=\"group\"\n tabIndex={0}\n >\n {title ? <div className=\"text-body font-medium text-foreground\">{title}</div> : null}\n {kind === \"width\" ? (\n <LegendScaleWidth domain={domain} format={format} ticks={ticks} />\n ) : (\n <LegendScaleColor domain={domain} format={format} />\n )}\n </div>\n );\n}\n\nfunction LegendScaleWidth({\n domain,\n format,\n ticks,\n}: {\n domain: [number, number];\n format: (value: number) => string;\n ticks: \"minmax\" | \"minmedmax\";\n}) {\n const samples = computeWidthSamples(domain, ticks);\n return (\n <div className=\"flex flex-col gap-2\">\n {samples.map((sample, index) => (\n // Index disambiguates a zero-width domain, where every sample shares\n // the same value (and therefore the same width).\n <div key={`${index}-${sample.value}`} className=\"flex items-center gap-3\">\n {/* The thin end of the ramp (default floor 1.5px) is the weakest mark\n in the system — `--flow-edge` is the same token a real, unweighted\n edge draws, and `strokeLinecap=\"round\"` keeps that floor width from\n disappearing into a hairline. */}\n <svg aria-hidden=\"true\" className=\"shrink-0\" height={16} overflow=\"visible\" width={40}>\n <line\n stroke=\"var(--flow-edge)\"\n strokeLinecap=\"round\"\n strokeWidth={sample.width}\n x1={2}\n x2={38}\n y1={8}\n y2={8}\n />\n </svg>\n <span className=\"min-w-0 truncate text-meta tabular-nums text-muted-foreground\">\n {format(sample.value)}\n </span>\n </div>\n ))}\n </div>\n );\n}\n\nfunction LegendScaleColor({\n domain,\n format,\n}: {\n domain: [number, number];\n format: (value: number) => string;\n}) {\n const [min, max] = domain;\n const span = max - min;\n const stops = COLOR_STOP_FRACTIONS.map((fraction) => ({\n fraction,\n value: min + fraction * span,\n }));\n // Explicit 5-stop `color-mix` gradient (interpolated `in oklch`, matching how\n // the `--flow-edge-weak`/`--flow-edge-strong` tokens themselves are authored)\n // rather than a bare 2-color `linear-gradient`, so the ramp's perceptual\n // steps match the token authoring space instead of the browser's default\n // sRGB gradient interpolation.\n const gradient = `linear-gradient(to right, ${stops\n .map(({ fraction }) => {\n const percent = Math.round(fraction * 100);\n return `color-mix(in oklch, var(--flow-edge-strong) ${percent}%, var(--flow-edge-weak)) ${percent}%`;\n })\n .join(\", \")})`;\n\n return (\n <div className=\"flex flex-col gap-1.5\">\n {/*\n WCAG 1.4.1 — this gradient alone cannot carry the ordering, so the\n numbered ticks below are the required second channel, not decoration.\n */}\n <div\n aria-hidden=\"true\"\n className=\"h-3 w-full rounded-full\"\n style={{ backgroundImage: gradient }}\n />\n <div className=\"flex justify-between gap-1\">\n {stops.map((stop, index) => (\n <span\n // Index disambiguates a zero-width domain, where every stop shares\n // the same formatted value.\n key={`${index}-${stop.value}`}\n className={cn(\n \"min-w-0 truncate text-meta tabular-nums text-muted-foreground\",\n index === 0 && \"text-start\",\n index === stops.length - 1 && \"text-end\",\n )}\n >\n {format(stop.value)}\n </span>\n ))}\n </div>\n </div>\n );\n}\n","/**\n * weight-scale — pure, framework-free \"edge weight → stroke width\" scale.\n *\n * `FlowWeightedEdge` calls this with every edge in the flow (via `useEdges()`,\n * memoized) so a group of edges can be min-maxed against ONE shared domain\n * instead of each edge picking its own arbitrary width. No React, no DOM: a\n * sibling (e.g. RM-045's continuous `Legend`) can call it directly to render\n * the same ramp the edges use, and it is trivially unit-testable.\n */\n\n/** Minimal edge shape the scale needs — a subset of `Edge<FlowWeightedEdgeData>`. */\nexport interface WeightedEdgeLike {\n id: string;\n data?: {\n weight?: number;\n scaleGroup?: string;\n };\n}\n\nexport interface EdgeWeightScaleOptions {\n /** Output stroke-width range, in px. @default [1.5, 8] */\n widthRange?: [number, number];\n /**\n * Restrict the domain calculation (and the returned map) to edges whose\n * `data.scaleGroup` equals this value — edges in a different group are\n * skipped entirely. Omit to compute one scale per distinct `scaleGroup`\n * present in `edges` (edges with no `scaleGroup` share one implicit\n * default group, so a flow that never sets it still gets one shared\n * domain — \"all edges in the same `<ReactFlow>`\").\n */\n scaleGroup?: string;\n}\n\n/** Matches today's fixed 1.5px `FlowEdge` stroke, so an unweighted edge is unchanged. */\nexport const DEFAULT_EDGE_WIDTH_RANGE: [number, number] = [1.5, 8];\n\nconst DEFAULT_SCALE_GROUP = \"__default__\";\n\n/**\n * Resolve every edge's `data.weight` into a stroke-width, linearly min-maxed\n * into `widthRange` per `scaleGroup`. An edge with no `data.weight` gets the\n * range floor — the existing fixed 1.5px `FlowEdge` already draws, so a plain\n * edge renders unchanged. An edge that is the only member of its group (or\n * whose group has zero weight variance) gets the midpoint of the range —\n * there is no domain to compare it against.\n */\nexport function computeEdgeWeightScale(\n edges: WeightedEdgeLike[],\n opts: EdgeWeightScaleOptions = {},\n): Map<string, number> {\n const [minWidth, maxWidth] = opts.widthRange ?? DEFAULT_EDGE_WIDTH_RANGE;\n const result = new Map<string, number>();\n const groups = new Map<string, { id: string; weight: number }[]>();\n\n for (const edge of edges) {\n const weight = edge.data?.weight;\n if (weight === undefined) {\n result.set(edge.id, minWidth);\n continue;\n }\n const group = edge.data?.scaleGroup ?? DEFAULT_SCALE_GROUP;\n if (opts.scaleGroup !== undefined && group !== opts.scaleGroup) continue;\n const list = groups.get(group);\n if (list) list.push({ id: edge.id, weight });\n else groups.set(group, [{ id: edge.id, weight }]);\n }\n\n for (const list of groups.values()) {\n let min = Infinity;\n let max = -Infinity;\n for (const { weight } of list) {\n if (weight < min) min = weight;\n if (weight > max) max = weight;\n }\n const span = max - min;\n for (const { id, weight } of list) {\n const width =\n span === 0\n ? (minWidth + maxWidth) / 2\n : minWidth + ((weight - min) / span) * (maxWidth - minWidth);\n result.set(id, width);\n }\n }\n\n return result;\n}\n","import { Panel, useReactFlow, type PanelPosition } from \"@xyflow/react\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\n\nexport interface ZoomControlsProps {\n position?: PanelPosition;\n className?: string;\n}\n\nfunction ControlButton({\n label,\n onClick,\n children,\n}: {\n label: string;\n onClick: () => void;\n children: React.ReactNode;\n}) {\n return (\n <button\n type=\"button\"\n aria-label={label}\n onClick={onClick}\n className=\"flex size-8 items-center justify-center text-foreground transition-colors duration-fast hover:bg-surface-muted focus-ring [&_svg]:size-4\"\n >\n {children}\n </button>\n );\n}\n\n/** Branded zoom in / out / fit controls. Render inside <CanvasShell>. */\nexport function ZoomControls({ position = \"bottom-right\", className }: ZoomControlsProps) {\n const { zoomIn, zoomOut, fitView } = useReactFlow();\n return (\n <Panel position={position}>\n <div\n className={cn(\n \"flex divide-x overflow-hidden rounded-lg bg-surface-elevated shadow-ring-sm\",\n className,\n )}\n >\n <ControlButton label=\"Zoom in\" onClick={() => zoomIn()}>\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n >\n <path d=\"M12 5v14M5 12h14\" />\n </svg>\n </ControlButton>\n <ControlButton label=\"Zoom out\" onClick={() => zoomOut()}>\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n >\n <path d=\"M5 12h14\" />\n </svg>\n </ControlButton>\n <ControlButton label=\"Fit view\" onClick={() => fitView()}>\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M8 3H5a2 2 0 0 0-2 2v3M21 8V5a2 2 0 0 0-2-2h-3M16 21h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3\" />\n </svg>\n </ControlButton>\n </div>\n </Panel>\n );\n}\n","import { type ReactNode } from \"react\";\nimport { Handle, NodeResizer, Position, useNodes, type Node, type NodeProps } from \"@xyflow/react\";\nimport { ChevronDown, ChevronRight } from \"lucide-react\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport { useFlowGroups } from \"../use-flow-groups\";\nimport { FLOW_HANDLE_ANCHOR_CLASS } from \"../flow-handle/flow-handle-anchor\";\n\n/** Visual accent for a group container. */\nexport type FlowGroupTone = \"default\" | \"accent\" | \"success\" | \"warning\" | \"destructive\";\n\nexport interface FlowGroupNodeData extends Record<string, unknown> {\n /** Header label for the group. */\n title: string;\n /** Optional leading glyph (a Lucide icon element, etc.). */\n icon?: ReactNode;\n /**\n * Whether the group is collapsed to an overview chip. Managed by\n * `useFlowGroups().collapseGroup` / `expandGroup`; the header toggle flips it.\n */\n collapsed?: boolean;\n /**\n * Fallback count shown next to the title. When omitted, the node counts its\n * live direct children from the store, so the badge stays accurate as nodes\n * are added or removed.\n */\n childCount?: number;\n /** Accent tone for the header rule + count badge. */\n tone?: FlowGroupTone;\n}\n\nexport type BrandFlowGroupNode = Node<FlowGroupNodeData, \"group\">;\n\n// #124 split this map in two: it used to serve both the icon slot (a MARK,\n// >=3:1) and the child-count text badge (running text, >=4.5:1) from one set\n// of values, which is exactly the fill-rung-as-text shape #124 fixes\n// elsewhere. `accent` is unchanged (`#399` already put it on the ink rung for\n// both slots — legal, since the ink rung clears the 3:1 mark bar trivially).\n\n/** Icon-slot tone (the leading glyph next to the title) — the 3:1 mark rung. */\nconst toneMark: Record<FlowGroupTone, string> = {\n default: \"text-muted-foreground\",\n accent: \"text-primary-text\",\n success: \"text-success\",\n warning: \"text-warning\",\n destructive: \"text-destructive\",\n};\n\n/** Child-count badge tone — running text, so it takes the >=4.5:1 ink rung. */\nconst toneInk: Record<FlowGroupTone, string> = {\n default: \"text-muted-foreground\",\n accent: \"text-primary-text\",\n success: \"text-success-text\",\n warning: \"text-warning-text\",\n destructive: \"text-destructive-text\",\n};\n\n// `FLOW_HANDLE_ANCHOR_CLASS` last: a connector dot must never be in flight when React\n// Flow measures it. See `flow-handle/flow-handle-anchor.ts`.\nconst handleClassName = `!size-2 !border-2 !border-flow-group-border !bg-flow-group ${FLOW_HANDLE_ANCHOR_CLASS}`;\n\n/**\n * Branded group container node. Register it as `nodeTypes={{ group: FlowGroupNode }}`\n * and create nodes with `type: \"group\"` + typed `data: FlowGroupNodeData`. Children\n * are re-parented onto it (`parentId`, `extent: \"parent\"`) — see `useFlowGroups`.\n *\n * The header carries the title, a live child-count badge and a collapse/expand\n * toggle (a real `<button>` with `aria-expanded`) that drives\n * `useFlowGroups().collapseGroup` / `expandGroup`. When selected (and expanded)\n * a `<NodeResizer>` lets the group be resized.\n *\n * The surface uses the FL-01 tokens `--flow-group` (fill) + `--flow-group-border`\n * (border). Under a theme whose `--flow-group` is near-transparent, the\n * drawn border does the work.\n */\nexport function FlowGroupNode({ id, data, selected }: NodeProps<BrandFlowGroupNode>) {\n const { toggleCollapse } = useFlowGroups();\n const nodes = useNodes();\n const collapsed = data.collapsed ?? false;\n const tone = data.tone ?? \"default\";\n\n // Live count of direct children; fall back to the stored count before the\n // store settles (or in a non-interactive preview).\n const liveChildCount = nodes.filter((n) => n.parentId === id).length;\n const childCount = liveChildCount || data.childCount || 0;\n\n return (\n <div\n className={cn(\n \"flex h-full w-full flex-col rounded-lg border bg-flow-group/60 text-foreground\",\n \"border-flow-group-border transition-[box-shadow,border-color] duration-fast ease-standard motion-reduce:transition-none\",\n collapsed ? \"shadow-sm\" : \"shadow-none\",\n selected && \"ring-2 ring-ring\",\n )}\n >\n {!collapsed && selected ? (\n <NodeResizer\n minWidth={160}\n minHeight={96}\n lineClassName=\"!border-flow-group-border\"\n handleClassName={handleClassName}\n />\n ) : null}\n\n {/* Handles so proxy edges can attach to the group when collapsed. */}\n <Handle type=\"target\" position={Position.Top} className={handleClassName} />\n <Handle type=\"source\" position={Position.Bottom} className={handleClassName} />\n\n <div\n className={cn(\n \"flex items-center gap-2 px-3 py-2\",\n !collapsed && \"border-b border-flow-group-border\",\n )}\n >\n <button\n type=\"button\"\n onClick={() => toggleCollapse(id)}\n aria-label={collapsed ? `Expand group ${data.title}` : `Collapse group ${data.title}`}\n aria-expanded={!collapsed}\n className={cn(\n \"-ml-1 grid size-5 shrink-0 place-items-center rounded\",\n \"text-muted-foreground hover:bg-muted hover:text-foreground\",\n \"focus-ring\",\n )}\n >\n {collapsed ? (\n <ChevronRight aria-hidden=\"true\" className=\"size-4\" />\n ) : (\n <ChevronDown aria-hidden=\"true\" className=\"size-4\" />\n )}\n </button>\n\n {data.icon ? (\n <span className={cn(\"[&_svg]:size-4\", toneMark[tone])}>{data.icon}</span>\n ) : null}\n\n <span className=\"min-w-0 flex-1 truncate text-body font-medium\">{data.title}</span>\n\n <span\n className={cn(\n \"shrink-0 rounded-full bg-muted px-1.5 py-0.5 text-meta font-medium tabular-nums\",\n toneInk[tone],\n )}\n aria-label={`${childCount} ${childCount === 1 ? \"node\" : \"nodes\"}`}\n >\n {childCount}\n </span>\n </div>\n </div>\n );\n}\n","import { useCallback } from \"react\";\nimport { useReactFlow, type Edge, type Node } from \"@xyflow/react\";\nimport {\n collapseGroup as collapseGroupOp,\n expandGroup as expandGroupOp,\n groupNodes as groupNodesOp,\n toggleGroupCollapsed as toggleGroupCollapsedOp,\n ungroup as ungroupOp,\n type GroupNodesOptions,\n} from \"./group-operations\";\n\n/** Options for `groupNodes`/`groupSelection` with an optional generated id. */\nexport type GroupOptions = Partial<Pick<GroupNodesOptions, \"groupId\">> &\n Omit<GroupNodesOptions, \"groupId\">;\n\nexport interface UseFlowGroupsResult {\n /**\n * Wrap the given (top-level) node ids in a new `FlowGroupNode`. Returns the new\n * group id, or `undefined` if nothing was grouped. Pass `options.groupId` for a\n * deterministic id; otherwise one is generated.\n */\n groupNodes: (nodeIds: string[], options?: GroupOptions) => string | undefined;\n /** Group the currently-selected nodes. Returns the new group id (or `undefined`). */\n groupSelection: (options?: GroupOptions) => string | undefined;\n /** Dissolve a group, restoring its children to absolute positions. */\n ungroup: (groupId: string) => void;\n /** Collapse a group to an overview chip (hides subtree, reroutes boundary edges). */\n collapseGroup: (groupId: string) => void;\n /** Expand a collapsed group (exact inverse of collapse). */\n expandGroup: (groupId: string) => void;\n /** Collapse if expanded, expand if collapsed. */\n toggleCollapse: (groupId: string) => void;\n}\n\nfunction generateGroupId(): string {\n return `flow-group-${Math.random().toString(36).slice(2, 9)}`;\n}\n\n/**\n * Thin hook over the pure grouping operations in `group-operations.ts`. Reads and\n * writes the live graph via `useReactFlow()`; all the logic lives in the pure,\n * unit-tested core. Must be called inside a React Flow context (e.g. from a\n * `<Panel>` child of `<CanvasShell>` or from within a custom node).\n */\nexport function useFlowGroups<\n NodeType extends Node = Node,\n EdgeType extends Edge = Edge,\n>(): UseFlowGroupsResult {\n const { getNodes, getEdges, setNodes, setEdges } = useReactFlow<NodeType, EdgeType>();\n\n const apply = useCallback(\n (result: { nodes: NodeType[]; edges: EdgeType[] }) => {\n setNodes(result.nodes);\n setEdges(result.edges);\n },\n [setNodes, setEdges],\n );\n\n const groupNodes = useCallback(\n (nodeIds: string[], options?: GroupOptions): string | undefined => {\n if (nodeIds.length === 0) return undefined;\n const groupId = options?.groupId ?? generateGroupId();\n const result = groupNodesOp<NodeType, EdgeType>(getNodes(), getEdges(), nodeIds, {\n ...options,\n groupId,\n });\n apply(result);\n return groupId;\n },\n [getNodes, getEdges, apply],\n );\n\n const groupSelection = useCallback(\n (options?: GroupOptions): string | undefined => {\n const selectedIds = getNodes()\n .filter((n) => n.selected && !n.parentId)\n .map((n) => n.id);\n return groupNodes(selectedIds, options);\n },\n [getNodes, groupNodes],\n );\n\n const ungroup = useCallback(\n (groupId: string) => apply(ungroupOp<NodeType, EdgeType>(getNodes(), getEdges(), groupId)),\n [getNodes, getEdges, apply],\n );\n\n const collapseGroup = useCallback(\n (groupId: string) =>\n apply(collapseGroupOp<NodeType, EdgeType>(getNodes(), getEdges(), groupId)),\n [getNodes, getEdges, apply],\n );\n\n const expandGroup = useCallback(\n (groupId: string) => apply(expandGroupOp<NodeType, EdgeType>(getNodes(), getEdges(), groupId)),\n [getNodes, getEdges, apply],\n );\n\n const toggleCollapse = useCallback(\n (groupId: string) =>\n apply(toggleGroupCollapsedOp<NodeType, EdgeType>(getNodes(), getEdges(), groupId)),\n [getNodes, getEdges, apply],\n );\n\n return {\n groupNodes,\n groupSelection,\n ungroup,\n collapseGroup,\n expandGroup,\n toggleCollapse,\n };\n}\n","import { getNodesBounds, type Edge, type Node } from \"@xyflow/react\";\nimport type { FlowGroupNodeData, FlowGroupTone } from \"../flow-group-node\";\n\n/**\n * Pure, framework-agnostic grouping operations over `nodes`/`edges` arrays.\n *\n * Every function is a **pure transform** — it never mutates its inputs and\n * returns a fresh `{ nodes, edges }`. This is the testable core; `useFlowGroups`\n * is a thin hook that wires these to a live canvas via `useReactFlow()`.\n */\n\n/** Node type used for the group container (matches `nodeTypes={{ group: FlowGroupNode }}`). */\nexport const FLOW_GROUP_NODE_TYPE = \"group\";\n\n/** Padding (px) left around the child bounds when a group is created. */\nconst DEFAULT_GROUP_PADDING = 28;\n/** Extra top space reserved for the group header so children don't sit under it. */\nconst GROUP_HEADER_OFFSET = 44;\n/** Fixed size of the collapsed \"overview chip\". */\nconst OVERVIEW_WIDTH = 220;\nconst OVERVIEW_HEIGHT = 48;\n\nexport interface GroupNodesOptions {\n /** Id for the new group node. Required so the operation stays deterministic/testable. */\n groupId: string;\n /** Header title for the group. @default \"Group\" */\n title?: string;\n /** Accent tone for the group header. */\n tone?: FlowGroupTone;\n /** Padding (px) around the child bounds. @default 28 */\n padding?: number;\n}\n\nexport interface FlowGroupOperationResult<\n NodeType extends Node = Node,\n EdgeType extends Edge = Edge,\n> {\n nodes: NodeType[];\n edges: EdgeType[];\n}\n\n/**\n * Snapshot stashed on a collapsed group's `data` so `expandGroup` can restore the\n * exact pre-collapse graph (an exact inverse). Holds original node/edge object\n * references — never mutated — so restoration is object-exact.\n */\ninterface FlowGroupCollapsedState {\n /** The group node exactly as it was before collapse (no collapsed state on it). */\n groupSnapshot: Node;\n /** Every descendant node exactly as it was before collapse. */\n descendantSnapshots: Node[];\n /** The boundary-crossing edges exactly as they were before rerouting. */\n reroutedEdges: Edge[];\n}\n\nconst COLLAPSED_STATE_KEY = \"__flowGroupCollapsedState\";\n\n/** Marker + metadata written onto a proxy edge's `data`. */\nexport interface FlowGroupProxyEdgeData extends Record<string, unknown> {\n __flowGroupProxy: true;\n /** Id of the collapsed group this proxy edge stands in for. */\n groupId: string;\n}\n\n/** True if `edge` is a proxy edge synthesized for a collapsed group. */\nexport function isFlowGroupProxyEdge(edge: Edge): boolean {\n return Boolean((edge.data as Partial<FlowGroupProxyEdgeData> | undefined)?.__flowGroupProxy);\n}\n\nfunction proxyEdgeId(groupId: string, originalEdgeId: string): string {\n return `flow-group-proxy__${groupId}__${originalEdgeId}`;\n}\n\nfunction readCollapsedState(node: Node): FlowGroupCollapsedState | undefined {\n return (node.data as Record<string, unknown> | undefined)?.[COLLAPSED_STATE_KEY] as\n | FlowGroupCollapsedState\n | undefined;\n}\n\n/** All node ids whose parent chain reaches `groupId` (children, grandchildren, …). */\nfunction collectDescendantIds(nodes: Node[], groupId: string): Set<string> {\n const childrenByParent = new Map<string, Node[]>();\n for (const n of nodes) {\n if (n.parentId) {\n const arr = childrenByParent.get(n.parentId);\n if (arr) arr.push(n);\n else childrenByParent.set(n.parentId, [n]);\n }\n }\n\n const out = new Set<string>();\n const stack = [...(childrenByParent.get(groupId) ?? [])];\n while (stack.length > 0) {\n const n = stack.pop()!;\n if (out.has(n.id)) continue;\n out.add(n.id);\n const kids = childrenByParent.get(n.id);\n if (kids) stack.push(...kids);\n }\n return out;\n}\n\n/**\n * Wrap `nodeIds` (assumed top-level) in a new `FlowGroupNode` parent.\n *\n * - Computes the child bounds with `getNodesBounds`.\n * - Inserts the group node **before** its children in the array (React Flow\n * requires a parent to precede its children).\n * - Re-parents each child (`parentId`, `extent: \"parent\"`) and rewrites its\n * position to be **relative to the group origin**.\n */\nexport function groupNodes<NodeType extends Node = Node, EdgeType extends Edge = Edge>(\n nodes: NodeType[],\n edges: EdgeType[],\n nodeIds: string[],\n options: GroupNodesOptions,\n): FlowGroupOperationResult<NodeType, EdgeType> {\n const idSet = new Set(nodeIds);\n const children = nodes.filter((n) => idSet.has(n.id));\n if (children.length === 0) return { nodes, edges };\n\n const bounds = getNodesBounds(children);\n const padding = options.padding ?? DEFAULT_GROUP_PADDING;\n const originX = bounds.x - padding;\n const originY = bounds.y - padding - GROUP_HEADER_OFFSET;\n\n const data: FlowGroupNodeData = {\n title: options.title ?? \"Group\",\n childCount: children.length,\n ...(options.tone ? { tone: options.tone } : {}),\n };\n\n const groupNode = {\n id: options.groupId,\n type: FLOW_GROUP_NODE_TYPE,\n position: { x: originX, y: originY },\n width: bounds.width + padding * 2,\n height: bounds.height + padding * 2 + GROUP_HEADER_OFFSET,\n data,\n } as unknown as NodeType;\n\n const reparented = new Map<string, NodeType>();\n for (const child of children) {\n reparented.set(child.id, {\n ...child,\n parentId: options.groupId,\n extent: \"parent\",\n position: { x: child.position.x - originX, y: child.position.y - originY },\n } as NodeType);\n }\n\n let earliest = nodes.length;\n for (let i = 0; i < nodes.length; i++) {\n if (idSet.has(nodes[i]!.id)) {\n earliest = i;\n break;\n }\n }\n\n const mapped = nodes.map((n) => reparented.get(n.id) ?? n);\n const resultNodes = [...mapped.slice(0, earliest), groupNode, ...mapped.slice(earliest)];\n return { nodes: resultNodes, edges };\n}\n\n/**\n * Dissolve a group: remove the group node and restore its **direct** children to\n * absolute positions (dropping `parentId`/`extent`). If the group is currently\n * collapsed it is expanded first, so ungrouping a collapsed group is safe.\n */\nexport function ungroup<NodeType extends Node = Node, EdgeType extends Edge = Edge>(\n nodes: NodeType[],\n edges: EdgeType[],\n groupId: string,\n): FlowGroupOperationResult<NodeType, EdgeType> {\n const group = nodes.find((n) => n.id === groupId);\n if (!group) return { nodes, edges };\n\n const base = (group.data as FlowGroupNodeData | undefined)?.collapsed\n ? expandGroup(nodes, edges, groupId)\n : { nodes, edges };\n\n const liveGroup = base.nodes.find((n) => n.id === groupId)!;\n const gx = liveGroup.position.x;\n const gy = liveGroup.position.y;\n\n const resultNodes = base.nodes\n .filter((n) => n.id !== groupId)\n .map((n) => {\n if (n.parentId === groupId) {\n const {\n parentId: _parentId,\n extent: _extent,\n ...rest\n } = n as NodeType & {\n parentId?: string;\n extent?: unknown;\n };\n return {\n ...rest,\n position: { x: n.position.x + gx, y: n.position.y + gy },\n } as NodeType;\n }\n return n;\n });\n\n return { nodes: resultNodes, edges: base.edges };\n}\n\n/**\n * Collapse a group to an overview chip.\n *\n * - Hides every descendant (including nested groups and their subtrees).\n * - Shrinks the group to a fixed overview size and marks `data.collapsed`.\n * - Re-routes every edge that **crosses the collapse boundary** (exactly one\n * endpoint inside the collapsed subtree) to a **proxy edge** targeting the\n * group node; the original edge is hidden and stashed for restoration.\n * - Edges fully inside or fully outside the subtree are left untouched.\n *\n * The full pre-collapse graph is stashed on the group's `data` so `expandGroup`\n * is an exact inverse. Nested collapse is handled by snapshotting each\n * descendant's exact prior state (a subgroup that was already collapsed stays\n * collapsed on expand — no double-unhide).\n */\nexport function collapseGroup<NodeType extends Node = Node, EdgeType extends Edge = Edge>(\n nodes: NodeType[],\n edges: EdgeType[],\n groupId: string,\n): FlowGroupOperationResult<NodeType, EdgeType> {\n const group = nodes.find((n) => n.id === groupId);\n if (!group) return { nodes, edges };\n if ((group.data as FlowGroupNodeData | undefined)?.collapsed) return { nodes, edges };\n\n const descendantIds = collectDescendantIds(nodes, groupId);\n\n const descendantSnapshots = nodes.filter((n) => descendantIds.has(n.id));\n const directChildCount = nodes.filter((n) => n.parentId === groupId).length;\n\n const isInside = (id: string) => descendantIds.has(id);\n const reroutedEdges: EdgeType[] = [];\n const proxyEdges: EdgeType[] = [];\n for (const edge of edges) {\n const sourceInside = isInside(edge.source);\n const targetInside = isInside(edge.target);\n if (sourceInside === targetInside) continue; // fully inside or fully outside\n\n reroutedEdges.push(edge);\n const proxyData: FlowGroupProxyEdgeData = {\n ...(edge.data ?? {}),\n __flowGroupProxy: true,\n groupId,\n };\n proxyEdges.push({\n ...edge,\n id: proxyEdgeId(groupId, edge.id),\n source: sourceInside ? groupId : edge.source,\n target: targetInside ? groupId : edge.target,\n sourceHandle: undefined,\n targetHandle: undefined,\n data: proxyData,\n } as EdgeType);\n }\n\n const collapsedState: FlowGroupCollapsedState = {\n groupSnapshot: group,\n descendantSnapshots,\n reroutedEdges,\n };\n\n const resultNodes = nodes.map((n) => {\n if (n.id === groupId) {\n return {\n ...n,\n width: OVERVIEW_WIDTH,\n height: OVERVIEW_HEIGHT,\n data: {\n ...(n.data as Record<string, unknown>),\n collapsed: true,\n childCount: directChildCount,\n [COLLAPSED_STATE_KEY]: collapsedState,\n },\n } as NodeType;\n }\n if (descendantIds.has(n.id)) {\n return { ...n, hidden: true } as NodeType;\n }\n return n;\n });\n\n const reroutedIds = new Set(reroutedEdges.map((e) => e.id));\n const resultEdges = edges.map((e) =>\n reroutedIds.has(e.id) ? ({ ...e, hidden: true } as EdgeType) : e,\n );\n resultEdges.push(...proxyEdges);\n\n return { nodes: resultNodes, edges: resultEdges };\n}\n\n/**\n * Expand a collapsed group — the exact inverse of `collapseGroup`. Restores the\n * group node, un-hides the descendant subtree (to each node's exact prior\n * state), removes this group's proxy edges and restores the original edges.\n * No-op if the group isn't collapsed.\n */\nexport function expandGroup<NodeType extends Node = Node, EdgeType extends Edge = Edge>(\n nodes: NodeType[],\n edges: EdgeType[],\n groupId: string,\n): FlowGroupOperationResult<NodeType, EdgeType> {\n const group = nodes.find((n) => n.id === groupId);\n const state = group ? readCollapsedState(group) : undefined;\n if (!group || !state) return { nodes, edges };\n\n const snapshotById = new Map(state.descendantSnapshots.map((n) => [n.id, n]));\n const reroutedById = new Map(state.reroutedEdges.map((e) => [e.id, e]));\n\n const resultNodes = nodes.map((n) => {\n if (n.id === groupId) return state.groupSnapshot as NodeType;\n const snap = snapshotById.get(n.id);\n return snap ? (snap as NodeType) : n;\n });\n\n const resultEdges = edges\n .filter(\n (e) =>\n !(\n isFlowGroupProxyEdge(e) &&\n (e.data as Partial<FlowGroupProxyEdgeData> | undefined)?.groupId === groupId\n ),\n )\n .map((e) => {\n const original = reroutedById.get(e.id);\n return original ? (original as EdgeType) : e;\n });\n\n return { nodes: resultNodes, edges: resultEdges };\n}\n\n/** Collapse if expanded, expand if collapsed. */\nexport function toggleGroupCollapsed<NodeType extends Node = Node, EdgeType extends Edge = Edge>(\n nodes: NodeType[],\n edges: EdgeType[],\n groupId: string,\n): FlowGroupOperationResult<NodeType, EdgeType> {\n const group = nodes.find((n) => n.id === groupId);\n if (!group) return { nodes, edges };\n return (group.data as FlowGroupNodeData | undefined)?.collapsed\n ? expandGroup(nodes, edges, groupId)\n : collapseGroup(nodes, edges, groupId);\n}\n","/**\n * @elabs-ai/components-flow — branded React Flow (@xyflow/react) building blocks.\n *\n * Import the React Flow stylesheet once at the app root:\n * import \"@xyflow/react/dist/style.css\";\n */\nexport * from \"./canvas-shell\";\nexport * from \"./flow-handle\";\nexport * from \"./flow-node\";\nexport * from \"./flow-edge\";\n// The shared edge path + keyboard focus indicator every edge type draws through (#286).\nexport * from \"./flow-edge-path\";\nexport * from \"./flow-smart-edge\";\nexport * from \"./flow-floating-edge\";\nexport * from \"./flow-placeholder-node\";\nexport * from \"./flow-button-edge\";\nexport * from \"./flow-layout\";\nexport * from \"./helper-lines\";\nexport * from \"./flow-mini-map\";\nexport * from \"./inspector-panel\";\nexport * from \"./legend\";\nexport * from \"./zoom-controls\";\nexport * from \"./flow-group-node\";\nexport * from \"./use-flow-groups\";\n\n// Convenience re-exports so consumers can build flows without a direct dep.\nexport {\n Background,\n Controls,\n MiniMap,\n Panel,\n Position,\n ReactFlow,\n ReactFlowProvider,\n useReactFlow,\n useNodesState,\n useEdgesState,\n addEdge,\n} from \"@xyflow/react\";\nexport type { Node, Edge, Connection, NodeProps, EdgeProps } from \"@xyflow/react\";\n\n// FlowWeightedEdge — RM-043\nexport * from \"./flow-weighted-edge\";\n\n// FlowSelfLoopEdge — RM-044\nexport * from \"./flow-self-loop-edge\";\n","import { useCallback, useMemo } from \"react\";\nimport {\n Position,\n getBezierPath,\n getSmoothStepPath,\n useEdges,\n useStore,\n type Edge,\n type EdgeProps,\n type ReactFlowState,\n} from \"@xyflow/react\";\nimport { resolveTokenColor } from \"@elabs-ai/components-tokens\";\nimport { FlowEdgePath } from \"../flow-edge-path\";\nimport { EdgeLabelPill, type EdgeLabelPillProps } from \"./edge-label-pill\";\nimport { backEdgeDetour, type BackEdgeNodeRect } from \"./back-edge-geometry\";\nimport {\n computeEdgeWeightScale,\n DEFAULT_EDGE_WIDTH_RANGE,\n type WeightedEdgeLike,\n} from \"./weight-scale\";\n\n/** Stable empty array for the forward branch of the node subscription below. */\nconst NO_NODES: ReactFlowState[\"nodes\"] = [];\n\nexport interface FlowWeightedEdgeData extends Record<string, unknown> {\n /** Frequency/volume this edge carries. Scaled into stroke width — see `computeEdgeWeightScale`. */\n weight?: number;\n /** Edges sharing a `scaleGroup` share one min-max width domain. @default all edges in the flow */\n scaleGroup?: string;\n /** A second, continuous measure (e.g. average duration). Colours the stroke — needs `valueDomain` too. */\n value?: number;\n /** `[min, max]` domain `value` is interpolated across, from `--flow-edge-weak` to `--flow-edge-strong`. */\n valueDomain?: [number, number];\n /** Primary edge-label-pill text, e.g. a frequency count. */\n label?: string;\n /** Secondary edge-label-pill text, e.g. a duration. */\n secondaryLabel?: string;\n /** Path geometry. Ignored when `variant` is `\"back\"`, which always routes smoothstep. @default \"bezier\" */\n path?: \"bezier\" | \"smoothstep\";\n /**\n * Whether this edge advances the process (`\"forward\"`) or runs against the\n * layout direction (`\"back\"` — a rework/retry edge, as reported by\n * `layoutFlow`'s `backEdges`).\n *\n * `\"back\"` is distinguished by SHAPE, not colour: a dashed stroke, and a\n * smoothstep route pushed clear of the forward edge between the same pair of\n * nodes so the two never overlap. It also carries a real accessible name, so\n * the direction reaches assistive technology as text rather than only as a\n * `data-variant` attribute.\n *\n * @default \"forward\"\n */\n variant?: \"forward\" | \"back\";\n /**\n * Overrides the accessible name given to a `\"back\"` edge's graphic. Defaults\n * to \"Back edge — runs against the process direction\".\n */\n variantLabel?: string;\n /**\n * Passed straight through to the rendered `EdgeLabelPill`'s `className`/`...props`\n * (see `EdgeLabelPillProps`) — the seam a composing package (e.g.\n * `@elabs-ai/components-process`'s `ProcessTransitionEdge`) uses to reach the pill's\n * own root button from outside this component, without a new semantic prop here.\n */\n labelProps?: Omit<EdgeLabelPillProps, \"label\" | \"secondaryLabel\" | \"x\" | \"y\" | \"selected\">;\n}\n\nexport type BrandFlowWeightedEdge = Edge<FlowWeightedEdgeData, \"weighted\">;\n\n// Approximate hex fallbacks for `--flow-edge-weak`/`--flow-edge-strong`, used\n// only when the CSS custom property can't be read (SSR, or the tokens\n// stylesheet isn't loaded yet) — resolveTokenColor() reads the live theme\n// value whenever a `document` is available.\nconst FALLBACK_WEAK = \"#6085a1\";\nconst FALLBACK_STRONG = \"#496d89\";\n\n/**\n * Shape channel for `variant=\"back\"`: a dash pattern plus a reduced stroke\n * opacity on the same `--flow-edge` token. Dashes are readable in greyscale\n * and under any theme, so the back edge never depends on hue (WCAG 1.4.1).\n */\nconst BACK_EDGE_DASHARRAY = \"6 4\";\nconst BACK_EDGE_OPACITY = 0.7;\n/**\n * Clearance, in px, between a back edge and everything it must stay clear OF.\n *\n * Two uses. As `getSmoothStepPath`'s `offset` it is how far the path runs straight out of\n * a handle before it turns, which is what keeps it off the forward edge joining the same\n * two nodes. As the gap in {@link backEdgeDetour} it is how far past the outermost card\n * the return leg sits — see that function for why a card-relative placement, rather than\n * a nudge off the midpoint, is the only one that stays visible.\n */\nconst BACK_EDGE_CLEARANCE = 40;\n\nconst DEFAULT_BACK_EDGE_LABEL = \"Back edge — runs against the process direction\";\n\nfunction clamp01(t: number): number {\n return Math.min(1, Math.max(0, t));\n}\n\nfunction hexToRgb(hex: string): [number, number, number] {\n const m = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/i.exec(hex);\n if (!m) return [0, 0, 0];\n return [parseInt(m[1]!, 16), parseInt(m[2]!, 16), parseInt(m[3]!, 16)];\n}\n\nfunction toHexByte(n: number): string {\n return Math.round(Math.min(255, Math.max(0, n)))\n .toString(16)\n .padStart(2, \"0\");\n}\n\n/** Linear RGB interpolation between two hex colors — pure, no DOM. */\nfunction mixHex(a: string, b: string, t: number): string {\n const [ar, ag, ab] = hexToRgb(a);\n const [br, bg, bb] = hexToRgb(b);\n const r = ar + (br - ar) * t;\n const g = ag + (bg - ag) * t;\n const bl = ab + (bb - ab) * t;\n return `#${toHexByte(r)}${toHexByte(g)}${toHexByte(bl)}`;\n}\n\n/**\n * Resolve `data.value`/`data.valueDomain` into a stroke color interpolated\n * between the `--flow-edge-weak`/`--flow-edge-strong` tokens. Returns\n * `undefined` when either input is missing, so the caller can fall back to\n * the plain `--flow-edge` token (unweighted-looking) edge color.\n */\nfunction resolveValueStrokeColor(\n value: number | undefined,\n domain: [number, number] | undefined,\n): string | undefined {\n if (value === undefined || !domain) return undefined;\n const [lo, hi] = domain;\n const t = hi === lo ? 1 : clamp01((value - lo) / (hi - lo));\n const weak = resolveTokenColor(\"--flow-edge-weak\", { fallback: FALLBACK_WEAK });\n const strong = resolveTokenColor(\"--flow-edge-strong\", { fallback: FALLBACK_STRONG });\n return mixHex(weak, strong, t);\n}\n\n/**\n * Branded weighted edge: `data.weight` scales stroke width (min-maxed per\n * `data.scaleGroup` across every edge in the flow — see `computeEdgeWeightScale`,\n * exported so a sibling like a continuous `Legend` renders the same ramp);\n * `data.value` + `data.valueDomain` interpolate stroke colour between\n * `--flow-edge-weak` and `--flow-edge-strong`; `data.label`/`data.secondaryLabel`\n * render as an `EdgeLabelPill`. An edge with none of this data renders exactly\n * like `FlowEdge` (fixed 1.5px, `--flow-edge` token) — fully backward-compatible.\n * Register it in `edgeTypes={{ weighted: FlowWeightedEdge }}` and create edges\n * with `type: \"weighted\"` and `data: FlowWeightedEdgeData`.\n *\n * `weight`/`value` are VISUAL ONLY — stroke width and colour reach no screen\n * reader. This component cannot set its own accessible name: React Flow's\n * `EdgeWrapper` sources it from `edge.ariaLabel` on the edge OBJECT, one level\n * above the component it renders as a child (issue #285). Run your `edges`\n * through `withWeightedEdgeAria` (from `./edge-aria`, or set `edge.ariaLabel`\n * yourself) before handing them to `<CanvasShell>`/`<ReactFlow>` — otherwise\n * the measure this component exists to show reaches no assistive technology,\n * and the edge announces only React Flow's generic \"Edge from n1 to n2\".\n * `withWeightedEdgeAria` returns a new array each call — memoize it\n * (`useMemo(() => withWeightedEdgeAria(edges), [edges])`) if you call it\n * inline in render.\n *\n * `data-weight`/`data-value` are also stamped onto the rendered `<path>` (raw\n * `data.weight`/`data.value`, not the scaled stroke width) — a stable\n * selector for tests/consumers, independent of the naming seam above.\n *\n * `data.variant: \"back\"` marks an edge that runs against the process direction\n * (dagre's reversed edges — see `layoutFlow`'s `backEdges`). It is dashed and\n * routed clear of the forward edge between the same two nodes, and carries a\n * real accessible name; the default `\"forward\"` renders exactly as before.\n *\n * Selected state uses `--ring` (matching the `ring-ring` treatment `FlowNode`/\n * `FlowGroupNode` use), overriding weight/value-derived width and colour so a\n * selected edge always reads clearly. No stroke-dasharray animation — reduced\n * motion is respected because there is no motion to reduce.\n *\n * KEYBOARD FOCUS is a separate state, drawn by `FlowEdgePath` (#286): selection\n * needs a consumer's `onEdgesChange` to ever become true, so it can never be the\n * indicator a tab stop owes its user.\n */\nexport function FlowWeightedEdge({\n id,\n sourceX,\n sourceY,\n targetX,\n targetY,\n sourcePosition,\n targetPosition,\n markerEnd,\n style,\n selected,\n data,\n}: EdgeProps<BrandFlowWeightedEdge>) {\n const edges = useEdges();\n const widthByEdgeId = useMemo(\n () => computeEdgeWeightScale(edges as unknown as WeightedEdgeLike[]),\n [edges],\n );\n\n const variant = data?.variant ?? \"forward\";\n const isBack = variant === \"back\";\n // Only a BACK edge needs the node rects (to route its return leg around the cards it\n // crosses). `useNodes()` would subscribe every edge to the whole nodes array, which the\n // store replaces on each drag frame, selection and resize — so every forward edge on\n // the canvas re-rendered on any node change anywhere. The forward branch hands back one\n // frozen constant, which `useStore`'s default `Object.is` compares equal every time.\n const nodes = useStore(\n useCallback((s: ReactFlowState) => (isBack ? s.nodes : NO_NODES), [isBack]),\n );\n const pathType = data?.path ?? \"bezier\";\n\n // Ranks advance vertically when the handles are on the top/bottom faces. Read from the\n // TARGET side: `sourcePosition` on a self-connecting or hand-placed edge can disagree,\n // and it is the incoming face that decides which way the last leg must approach from.\n const axis =\n targetPosition === Position.Top || targetPosition === Position.Bottom\n ? \"vertical\"\n : \"horizontal\";\n const rects = useMemo<BackEdgeNodeRect[]>(() => {\n if (!isBack) return [];\n const out: BackEdgeNodeRect[] = [];\n for (const node of nodes) {\n // A child node's `position` is parent-relative, so it is not comparable with the\n // absolute handle coordinates this edge is placed against. Skipping one only costs\n // a little clearance; mixing coordinate spaces would move the leg somewhere wrong.\n if (node.parentId) continue;\n const width = node.measured?.width ?? node.width;\n const height = node.measured?.height ?? node.height;\n if (!width || !height) continue;\n out.push({ x: node.position.x, y: node.position.y, width, height });\n }\n return out;\n }, [isBack, nodes]);\n const detour = isBack\n ? backEdgeDetour(\n rects,\n axis,\n axis === \"vertical\" ? [sourceY, targetY] : [sourceX, targetX],\n BACK_EDGE_CLEARANCE,\n )\n : null;\n\n const [edgePath, labelX, labelY] = isBack\n ? getSmoothStepPath({\n sourceX,\n sourceY,\n sourcePosition,\n targetX,\n targetY,\n targetPosition,\n offset: BACK_EDGE_CLEARANCE,\n ...(detour === null ? {} : axis === \"vertical\" ? { centerX: detour } : { centerY: detour }),\n })\n : pathType === \"smoothstep\"\n ? getSmoothStepPath({ sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition })\n : getBezierPath({ sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition });\n\n const scaledWidth = widthByEdgeId.get(id) ?? DEFAULT_EDGE_WIDTH_RANGE[0];\n const valueColor = useMemo(\n () => resolveValueStrokeColor(data?.value, data?.valueDomain),\n [data?.value, data?.valueDomain],\n );\n\n const stroke = selected ? \"var(--ring)\" : (valueColor ?? \"var(--flow-edge)\");\n const strokeWidth = selected ? scaledWidth + 1.5 : scaledWidth;\n\n const edge = (\n <FlowEdgePath\n id={id}\n path={edgePath}\n markerEnd={markerEnd}\n data-slot=\"flow-weighted-edge\"\n data-variant={variant}\n data-weight={data?.weight}\n data-value={data?.value}\n stroke={stroke}\n strokeWidth={strokeWidth}\n strokeDasharray={isBack ? BACK_EDGE_DASHARRAY : undefined}\n strokeOpacity={isBack ? BACK_EDGE_OPACITY : undefined}\n style={style}\n />\n );\n\n return (\n <>\n {isBack ? (\n // A `data-variant` is invisible to assistive technology, so the back\n // edge's meaning is also published as a named graphic. Only the back\n // variant is wrapped — a forward edge's DOM is unchanged from before\n // this prop existed.\n <g role=\"img\" aria-label={data?.variantLabel ?? DEFAULT_BACK_EDGE_LABEL}>\n {edge}\n </g>\n ) : (\n edge\n )}\n <EdgeLabelPill\n label={data?.label}\n secondaryLabel={data?.secondaryLabel}\n x={labelX}\n y={labelY}\n selected={selected}\n {...data?.labelProps}\n />\n </>\n );\n}\n","import type { ComponentPropsWithoutRef } from \"react\";\nimport { EdgeLabelRenderer } from \"@xyflow/react\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\n\nexport interface EdgeLabelPillProps extends ComponentPropsWithoutRef<\"button\"> {\n /** Primary label, e.g. a frequency count (\"128×\"). */\n label?: string;\n /** Secondary label rendered alongside the primary, e.g. a duration (\"3.4d avg\"). */\n secondaryLabel?: string;\n /** Label anchor, from `getBezierPath`/`getSmoothStepPath`'s `labelX`/`labelY`. */\n x: number;\n y: number;\n /** Matches the parent edge's `selected` state. */\n selected?: boolean;\n}\n\n/**\n * A small HTML pill (via `EdgeLabelRenderer`, not SVG `<text>`) anchored at an\n * edge's label point — so it can theme, wrap, and carry two values, unlike a\n * bare SVG text node. Renders nothing when neither label is set. Real\n * `<button>` so it is a genuine keyboard tab stop with a visible focus ring;\n * `pointer-events: auto` on an otherwise `nodrag nopan` wrapper so it doesn't\n * drag/pan the canvas, and doesn't block hovering the edge underneath it (the\n * wrapper is sized to the pill itself, not the whole edge).\n *\n * `className`/`...props` spread onto the root `<button>` (`data-slot=\"edge-label-pill\"`)\n * so a consumer that composes this pill from outside `@elabs-ai/components-flow` —\n * `@elabs-ai/components-process`'s `ProcessTransitionEdge` is the reference caller —\n * can reach it directly (a dashed frame, a `data-selection` attribute) without a new\n * semantic prop on this component. `className` merges LAST via `cn()`, so a caller can\n * override any of the pill's own utility classes; omitting both leaves every existing\n * caller's rendered markup unchanged.\n */\nexport function EdgeLabelPill({\n label,\n secondaryLabel,\n x,\n y,\n selected,\n className,\n ...props\n}: EdgeLabelPillProps) {\n if (!label && !secondaryLabel) return null;\n\n const accessibleName = [label, secondaryLabel].filter(Boolean).join(\" · \");\n\n return (\n <EdgeLabelRenderer>\n <div\n style={{\n position: \"absolute\",\n transform: `translate(-50%, -50%) translate(${x}px, ${y}px)`,\n pointerEvents: \"none\",\n }}\n className=\"nodrag nopan\"\n data-slot=\"edge-label-pill-anchor\"\n >\n <button\n type=\"button\"\n aria-label={accessibleName}\n data-slot=\"edge-label-pill\"\n {...props}\n className={cn(\n \"pointer-events-auto flex items-center gap-1 rounded-full border bg-flow-node px-2 py-0.5\",\n \"text-meta font-medium text-flow-node-foreground shadow-sm\",\n \"transition-colors duration-fast ease-standard\",\n \"focus-ring\",\n selected ? \"border-ring\" : \"border-flow-group-border\",\n className,\n )}\n >\n {label ? <span aria-hidden=\"true\">{label}</span> : null}\n {secondaryLabel ? (\n <span aria-hidden=\"true\" className=\"text-flow-node-foreground/70 tabular-nums\">\n {secondaryLabel}\n </span>\n ) : null}\n </button>\n </div>\n </EdgeLabelRenderer>\n );\n}\n","/**\n * Where a back edge's return leg runs, so it stays VISIBLE.\n *\n * A back edge is drawn as a smoothstep with one long cross-segment running against the\n * layout direction. React Flow puts that segment halfway between the two handles by\n * default, and a fixed nudge off the midpoint does not help: in a top-down layout the\n * midpoint of a rework edge sits between two ranks, i.e. squarely inside the column of\n * cards it is supposed to run past. Edges paint UNDER nodes, so the segment vanishes\n * behind the cards and all the reader is left with is a dashed stub below one node and\n * another above the other — which reads as a stray rectangle, not as a loop back.\n *\n * So the leg is placed OUTSIDE every card it would otherwise pass behind: past the far\n * side of every node whose extent overlaps the span the edge crosses. That is how a\n * process-mining tool draws a rework loop — out, around the rank, back in — and it is\n * the only placement that is correct for a graph rather than for a pair of nodes.\n */\n\n/** One node's laid-out box, in flow coordinates. */\nexport interface BackEdgeNodeRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/** Which axis the layout's ranks advance along. */\nexport type BackEdgeAxis = \"vertical\" | \"horizontal\";\n\n/**\n * The `centerX` (vertical layout) or `centerY` (horizontal layout) to hand\n * `getSmoothStepPath`, or `null` when nothing is measured yet and React Flow's own\n * midpoint should stand.\n *\n * @param rects Every laid-out node on the canvas. Nodes that cannot be hit are filtered\n * out here rather than by the caller, so the caller stays a plain map.\n * @param span The interval the edge crosses on the RANK axis (`[min, max]` of the two\n * handle coordinates) — a node outside it is never behind this edge.\n * @param clearance Gap left between the leg and the outermost card it clears.\n */\nexport function backEdgeDetour(\n rects: readonly BackEdgeNodeRect[],\n axis: BackEdgeAxis,\n span: readonly [number, number],\n clearance: number,\n): number | null {\n const [lo, hi] = span[0] <= span[1] ? span : [span[1], span[0]];\n let far = Number.NEGATIVE_INFINITY;\n\n for (const rect of rects) {\n if (!(rect.width > 0) || !(rect.height > 0)) continue;\n // Overlap on the RANK axis (the one the edge travels along): a node beside the\n // corridor but on another rank cannot be crossed by this leg.\n const [near, beyond] =\n axis === \"vertical\" ? [rect.y, rect.y + rect.height] : [rect.x, rect.x + rect.width];\n if (beyond < lo || near > hi) continue;\n far = Math.max(far, axis === \"vertical\" ? rect.x + rect.width : rect.y + rect.height);\n }\n\n return Number.isFinite(far) ? far + clearance : null;\n}\n","/**\n * edge-aria — pure, framework-free \"edge data → accessible name\" naming seam\n * for `FlowWeightedEdge`, mirroring `weight-scale.ts`'s shape (no React, no\n * DOM, trivially unit-testable).\n *\n * `FlowWeightedEdge` encodes `data.weight` as stroke width and, optionally,\n * `data.value` as stroke colour — both purely visual. React Flow's own\n * `EdgeWrapper` sources an edge's accessible name from `edge.ariaLabel`\n * (`edge` object, not the edge COMPONENT — see issue #285), so the component\n * has no channel to set it. This module is the seam a caller runs their\n * `edges` array through instead.\n *\n * Naming contract (a decision, not an accident):\n * 1. An explicit `edge.ariaLabel` always wins — never overwritten.\n * 2. Otherwise compose `\"Edge from <source> to <target>, weight <n>\"`,\n * appending `\", <valueLabel> <n>\"` when `data.value` is set and the pill\n * text (`data.label`/`data.secondaryLabel`, space-joined) when either is\n * present.\n * 3. When an edge carries none of `weight`/`value`/`label`/`secondaryLabel`,\n * return `undefined` so React Flow's own default\n * (\"Edge from <source> to <target>\") survives untouched. That quoted\n * string is `@xyflow/react`'s output, not ours — the `e-bare` edge in\n * `flow-weighted-edge.stories.tsx`'s `NamingContractEdges` story (#327)\n * is the browser lock for it: it renders a measure-less edge against a\n * real `CanvasShell` and asserts the default name via\n * `toHaveAccessibleName`. If that story goes red after a dependency\n * bump, read it as a signal that React Flow's default changed, not as a\n * flake to silence.\n */\n\nimport type { BrandFlowWeightedEdge } from \"./flow-weighted-edge\";\n\nexport interface WeightedEdgeAriaOptions {\n /** Word used for the width measure in the composed name. @default \"weight\" */\n weightLabel?: string;\n /** Word used for the colour measure in the composed name. @default \"value\" */\n valueLabel?: string;\n /**\n * Map a node id to its display name, so the composed name says\n * \"Edge from Order placed to Picked\" rather than \"Edge from n1 to n2\". The\n * caller owns node display names (this module never reaches into a `nodes`\n * array), so it is a hook, not an automatic lookup.\n */\n nameOf?: (nodeId: string) => string;\n /** Format a numeric measure for display. @default String(n) */\n formatNumber?: (n: number) => string;\n}\n\nconst DEFAULT_WEIGHT_LABEL = \"weight\";\nconst DEFAULT_VALUE_LABEL = \"value\";\n\nfunction defaultNameOf(nodeId: string): string {\n return nodeId;\n}\n\nfunction defaultFormatNumber(n: number): string {\n return String(n);\n}\n\n/**\n * Derive one edge's accessible name from its weight/value/labels. Pure — no\n * React, no DOM. Returns:\n * - the edge's own `ariaLabel`, unchanged, when the caller already set one;\n * - a composed string when the edge carries `weight`, `value`, `label` or\n * `secondaryLabel`;\n * - `undefined` when there is nothing to add (so the default announcement\n * from React Flow's `EdgeWrapper` survives untouched).\n */\nexport function buildWeightedEdgeAriaLabel(\n edge: BrandFlowWeightedEdge,\n opts: WeightedEdgeAriaOptions = {},\n): string | undefined {\n if (edge.ariaLabel) return edge.ariaLabel;\n\n const { weight, value, label, secondaryLabel } = edge.data ?? {};\n if (weight === undefined && value === undefined && !label && !secondaryLabel) {\n return undefined;\n }\n\n const {\n weightLabel = DEFAULT_WEIGHT_LABEL,\n valueLabel = DEFAULT_VALUE_LABEL,\n nameOf = defaultNameOf,\n formatNumber = defaultFormatNumber,\n } = opts;\n\n const parts = [`Edge from ${nameOf(edge.source)} to ${nameOf(edge.target)}`];\n if (weight !== undefined) parts.push(`${weightLabel} ${formatNumber(weight)}`);\n if (value !== undefined) parts.push(`${valueLabel} ${formatNumber(value)}`);\n\n const pillText = [label, secondaryLabel].filter(Boolean).join(\" \");\n if (pillText) parts.push(pillText);\n\n return parts.join(\", \");\n}\n\n/**\n * Stamp `ariaLabel` onto every weighted edge that has none, per\n * `buildWeightedEdgeAriaLabel`'s naming contract. An edge that already\n * carries an `ariaLabel` is returned unchanged (never overwritten); an edge\n * with nothing to add is also returned unchanged, so an unrelated edge type\n * sharing this array keeps its identity.\n *\n * Returns a NEW array — memoize the call when used inline in render (e.g.\n * `useMemo(() => withWeightedEdgeAria(edges), [edges])`), or a consumer hands\n * React Flow a new `edges` identity every frame.\n */\nexport function withWeightedEdgeAria<E extends BrandFlowWeightedEdge>(\n edges: E[],\n opts?: WeightedEdgeAriaOptions,\n): E[] {\n return edges.map((edge) => {\n if (edge.ariaLabel) return edge;\n const ariaLabel = buildWeightedEdgeAriaLabel(edge, opts);\n return ariaLabel === undefined ? edge : { ...edge, ariaLabel };\n });\n}\n","import { useMemo } from \"react\";\nimport { useEdges, useInternalNode, type Edge, type EdgeProps } from \"@xyflow/react\";\nimport { FlowEdgePath } from \"../flow-edge-path\";\nimport {\n computeEdgeWeightScale,\n DEFAULT_EDGE_WIDTH_RANGE,\n EdgeLabelPill,\n type EdgeLabelPillProps,\n type WeightedEdgeLike,\n} from \"../flow-weighted-edge\";\nimport { DEFAULT_LOOP_RADIUS, selfLoopHandleArc, selfLoopPath } from \"./self-loop-geometry\";\n\nexport interface FlowSelfLoopEdgeData extends Record<string, unknown> {\n /**\n * Frequency/volume this loop carries. Scaled into stroke width by the SAME\n * `computeEdgeWeightScale` domain as `FlowWeightedEdge`, so a loop's weight\n * is directly comparable with the forward edges around it.\n */\n weight?: number;\n /** Edges sharing a `scaleGroup` share one min-max width domain. @default all edges in the flow */\n scaleGroup?: string;\n /** Primary edge-label-pill text, e.g. a repeat count. */\n label?: string;\n /** Secondary edge-label-pill text, e.g. an average duration. */\n secondaryLabel?: string;\n /** Radius of the arc, in px. @default 28 */\n loopRadius?: number;\n /**\n * Overrides the accessible name given to the loop's graphic. Defaults to\n * \"Self-loop on <node> — this step repeats\".\n */\n loopLabel?: string;\n /**\n * Passed straight through to the rendered `EdgeLabelPill`'s `className`/`...props`\n * (see `EdgeLabelPillProps`) — the seam a composing package (e.g.\n * `@elabs-ai/components-process`'s `ProcessTransitionEdge`) uses to reach the pill's\n * own root button from outside this component, without a new semantic prop here.\n */\n labelProps?: Omit<EdgeLabelPillProps, \"label\" | \"secondaryLabel\" | \"x\" | \"y\" | \"selected\">;\n}\n\nexport type BrandFlowSelfLoopEdge = Edge<FlowSelfLoopEdgeData, \"self-loop\">;\n\n/** Node `data` shapes a title can be read from — `FlowNode`'s is `{ title }`. */\nfunction nodeName(data: unknown, fallback: string): string {\n if (data && typeof data === \"object\" && \"title\" in data) {\n const title = (data as { title?: unknown }).title;\n if (typeof title === \"string\" && title.length > 0) return title;\n }\n return fallback;\n}\n\n/**\n * Branded self-loop edge: an edge whose `source === target` — the \"this step\n * repeated\" signal of a process map. Register it in\n * `edgeTypes={{ \"self-loop\": FlowSelfLoopEdge }}` and create edges with\n * `type: \"self-loop\"` and `data: FlowSelfLoopEdgeData`.\n *\n * dagre cannot lay a self-loop out, so `layoutFlow` withholds them from the\n * graph entirely and reports their ids in `selfLoops` — this component draws\n * the arc itself, from the node's own live geometry (`useInternalNode`).\n *\n * ## It terminates ON the handle dots, like every other brand edge\n *\n * The two handle points of a self-loop sit on OPPOSITE sides of one node, so they\n * describe no useful straight line — which is why this edge used to ignore them and\n * arc over the node's top edge instead, with its feet on bare border a loop-radius\n * away from the nearest dot. Measured on the process map, that read as a detached\n * arc floating above the card: 24 px clear of any dot in a top-to-bottom layout, and\n * 69 px in a left-to-right one, where the loop stayed stubbornly on TOP while the\n * flow (and the handles) had moved to the sides. Both are the \"an edge terminates on\n * a handle dot\" rule being broken, just by a component that had declared itself\n * exempt.\n *\n * `selfLoopHandleArc` keeps the loop a SHAPE — it just bulges clear of the node on\n * the side a quarter turn from the source handle's own normal, so it lassos down the\n * right in a top-to-bottom layout and arcs over the top in a left-to-right one,\n * without this component knowing which direction is in force. Before the node is\n * measured there is nothing to clear, so it falls back to the node-box arc\n * (`selfLoopPath`) rather than to `NaN`.\n *\n * The loop is distinguished from a forward edge by its SHAPE, not by colour —\n * a closed arc above the node, legible in greyscale and in every theme — and\n * publishes that meaning as a real accessible name, because a `data-slot` is\n * invisible to assistive technology. Its label is an `EdgeLabelPill` at the\n * arc's apex, a genuine keyboard tab stop with a visible focus ring.\n *\n * Stroke width comes from the same `computeEdgeWeightScale` domain\n * `FlowWeightedEdge` uses, so a loop weighted 8 reads as thick as a forward\n * edge weighted 8. Nothing animates, so there is no motion to reduce.\n *\n * The arc is drawn through `FlowEdgePath`, so it inherits the shared keyboard\n * focus indicator (#286) rather than having to opt into it.\n */\nexport function FlowSelfLoopEdge({\n id,\n source,\n sourceX,\n sourceY,\n targetX,\n targetY,\n markerEnd,\n style,\n selected,\n data,\n}: EdgeProps<BrandFlowSelfLoopEdge>) {\n const edges = useEdges();\n const widthByEdgeId = useMemo(\n () => computeEdgeWeightScale(edges as unknown as WeightedEdgeLike[]),\n [edges],\n );\n const node = useInternalNode(source);\n\n const measuredWidth = node?.measured?.width;\n const measuredHeight = node?.measured?.height;\n const loopRadius = data?.loopRadius ?? DEFAULT_LOOP_RADIUS;\n\n const { path, labelX, labelY } = useMemo(() => {\n if (node && measuredWidth && measuredHeight) {\n return selfLoopHandleArc(\n {\n sourceX,\n sourceY,\n targetX,\n targetY,\n centerX: node.internals.positionAbsolute.x + measuredWidth / 2,\n centerY: node.internals.positionAbsolute.y + measuredHeight / 2,\n width: measuredWidth,\n height: measuredHeight,\n },\n loopRadius,\n );\n }\n // Before measurement lands there is no box to clear, and the two handle points\n // still bracket the node — draw the plain arc above them.\n return selfLoopPath(\n { centerX: (sourceX + targetX) / 2, topY: Math.min(sourceY, targetY) },\n loopRadius,\n );\n }, [node, measuredWidth, measuredHeight, sourceX, sourceY, targetX, targetY, loopRadius]);\n\n const scaledWidth = widthByEdgeId.get(id) ?? DEFAULT_EDGE_WIDTH_RANGE[0];\n const stroke = selected ? \"var(--ring)\" : \"var(--flow-edge)\";\n const strokeWidth = selected ? scaledWidth + 1.5 : scaledWidth;\n\n const accessibleName =\n data?.loopLabel ?? `Self-loop on ${nodeName(node?.data, source)} — this step repeats`;\n\n return (\n <>\n <g role=\"img\" aria-label={accessibleName}>\n <FlowEdgePath\n id={id}\n path={path}\n markerEnd={markerEnd}\n data-slot=\"flow-self-loop-edge\"\n stroke={stroke}\n strokeWidth={strokeWidth}\n style={{ fill: \"none\", ...style }}\n />\n </g>\n <EdgeLabelPill\n label={data?.label}\n secondaryLabel={data?.secondaryLabel}\n x={labelX}\n y={labelY}\n selected={selected}\n {...data?.labelProps}\n />\n </>\n );\n}\n","/**\n * self-loop-geometry — pure, framework-free arc math for a self-referencing edge.\n *\n * Kept out of the component (and out of React) so the loop's shape can be\n * unit-tested without a canvas, and so a sibling that needs the same apex\n * point (a legend, an overlay, a screenshot harness) can compute it directly.\n */\n\n/** Default arc radius, in px. A loop this size clears `FlowNode`'s header without dominating it. */\nexport const DEFAULT_LOOP_RADIUS = 28;\n\n/**\n * How far the cubic's control points reach sideways and upwards, as multiples\n * of `loopRadius`. Tuned so the arc reads as a closed loop rather than a bump:\n * the horizontal reach opens the throat of the loop, the vertical reach sets\n * its height.\n */\nconst CONTROL_REACH_X = 1.2;\nconst CONTROL_REACH_Y = 2.4;\n/** A symmetric cubic's midpoint sits at 3/4 of its control-point height — see `selfLoopPath`. */\nconst APEX_FACTOR = (3 / 4) * CONTROL_REACH_Y;\n\n/** The box a self-loop is drawn above. Matches a React Flow `InternalNode` structurally. */\nexport interface SelfLoopAnchor {\n /** Horizontal centre of the node the loop belongs to. */\n centerX: number;\n /** Top edge of that node — the loop is drawn above this line. */\n topY: number;\n}\n\nexport interface SelfLoopPath {\n /** SVG `d` for the arc. */\n path: string;\n /** Where a label belongs: the arc's apex. */\n labelX: number;\n labelY: number;\n}\n\n/**\n * A cubic arc that leaves the node's top-RIGHT, bulges up over the node, and\n * re-enters at its top-LEFT — the conventional \"this step repeated\" mark in a\n * process map, and a SHAPE rather than a colour, so it survives greyscale.\n *\n * The curve is symmetric about `centerX`, which puts its `t = 0.5` midpoint\n * exactly at `(centerX, topY - APEX_FACTOR × loopRadius)` — the apex the label\n * is anchored to. Never returns `NaN`: a non-finite input falls back to `0`\n * and a non-positive radius falls back to the default.\n */\nexport function selfLoopPath(anchor: SelfLoopAnchor, loopRadius: number): SelfLoopPath {\n const cx = Number.isFinite(anchor.centerX) ? anchor.centerX : 0;\n const ay = Number.isFinite(anchor.topY) ? anchor.topY : 0;\n const r = Number.isFinite(loopRadius) && loopRadius > 0 ? loopRadius : DEFAULT_LOOP_RADIUS;\n\n const startX = cx + r;\n const endX = cx - r;\n const controlY = ay - r * CONTROL_REACH_Y;\n\n return {\n path: `M ${startX},${ay} C ${startX + r * CONTROL_REACH_X},${controlY} ${\n endX - r * CONTROL_REACH_X\n },${controlY} ${endX},${ay}`,\n labelX: cx,\n labelY: ay - r * APEX_FACTOR,\n };\n}\n\n/**\n * The two handle anchors a self-loop actually joins, plus the box it has to clear.\n *\n * A self-loop's `sourceX/sourceY` and `targetX/targetY` are the SAME node's two handle\n * points, which is why they describe no useful straight line — but they are still the\n * only two points on the canvas the reader recognises as connectors, so the arc has to\n * start and end exactly on them. {@link selfLoopHandleArc} is the geometry that does\n * that; {@link selfLoopPath} remains the node-box arc used before a node is measured.\n */\nexport interface SelfLoopHandleAnchor {\n /** Where the loop leaves the node — React Flow's own SOURCE handle anchor. */\n sourceX: number;\n sourceY: number;\n /** Where it re-enters — React Flow's own TARGET handle anchor. */\n targetX: number;\n targetY: number;\n /** The node's centre. Decides which way is \"out of the card\". */\n centerX: number;\n centerY: number;\n /** The node's rendered size, so the arc clears the card instead of crossing it. */\n width: number;\n height: number;\n}\n\n/** Unit vector from the node's centre to a handle point, or `fallback` when degenerate. */\nfunction outward(\n x: number,\n y: number,\n centerX: number,\n centerY: number,\n fallback: readonly [number, number],\n): readonly [number, number] {\n const dx = x - centerX;\n const dy = y - centerY;\n const length = Math.hypot(dx, dy);\n if (!Number.isFinite(length) || length < 1e-6) return fallback;\n return [dx / length, dy / length];\n}\n\n/**\n * A cubic that leaves the SOURCE handle dot, bulges clear of the node on one side, and\n * re-enters at the TARGET handle dot.\n *\n * It is direction-agnostic by construction: the bulge is the source normal rotated a\n * quarter turn, so a top-to-bottom layout (handles on the bottom and top) gets a lasso\n * down the node's right-hand side, and a left-to-right layout (handles on the right and\n * left) gets an arc over the top — with no `direction` prop, and no list of cases to keep\n * in step with `layoutFlow`'s `HANDLE_BY_DIRECTION`.\n *\n * `loopRadius` is how far the curve shoots straight out of each dot before it turns, and\n * also the gap the arc's widest point keeps from the card's edge. The label sits at that\n * widest point — the cubic's `t = 0.5` midpoint — so it is off the node by construction.\n *\n * Never returns `NaN`: non-finite inputs fall back to `0`, a non-positive radius to\n * {@link DEFAULT_LOOP_RADIUS}, and a handle point sitting on the node's centre (nothing\n * measured yet) to a downward source normal.\n */\nexport function selfLoopHandleArc(anchor: SelfLoopHandleAnchor, loopRadius: number): SelfLoopPath {\n const num = (value: number) => (Number.isFinite(value) ? value : 0);\n const sx = num(anchor.sourceX);\n const sy = num(anchor.sourceY);\n const tx = num(anchor.targetX);\n const ty = num(anchor.targetY);\n const cx = num(anchor.centerX);\n const cy = num(anchor.centerY);\n const width = Math.max(0, num(anchor.width));\n const height = Math.max(0, num(anchor.height));\n const r = Number.isFinite(loopRadius) && loopRadius > 0 ? loopRadius : DEFAULT_LOOP_RADIUS;\n\n const [osx, osy] = outward(sx, sy, cx, cy, [0, 1]);\n const [otx, oty] = outward(tx, ty, cx, cy, [-osx, -osy]);\n // A quarter turn from the source normal: the side the loop bulges out on.\n const lx = osy;\n const ly = -osx;\n // How far the card extends along the bulge, plus the clearance we want beyond it.\n const clearance = Math.abs(lx) * (width / 2) + Math.abs(ly) * (height / 2) + r;\n // A cubic reaches only 3/4 of the way to its control points at the midpoint — its\n // WIDEST point — so `clearance` has to be divided by that factor, not used directly.\n // Used directly (the first cut of this function did) the apex lands at 0.75 × clearance,\n // which for a 176 px card is 87 px against an 88 px half-width: the loop is drawn ON the\n // card it is supposed to encircle, and its label — anchored at that same midpoint —\n // prints on top of the activity's own name. Measured on the process map: one collision\n // per direction that no amount of layout spacing could remove, because the arc's reach\n // is a property of the node it belongs to, not of the gap to its neighbours.\n const reach = (4 / 3) * clearance;\n\n const c1x = sx + osx * r + lx * reach;\n const c1y = sy + osy * r + ly * reach;\n const c2x = tx + otx * r + lx * reach;\n const c2y = ty + oty * r + ly * reach;\n\n return {\n path: `M ${sx},${sy} C ${c1x},${c1y} ${c2x},${c2y} ${tx},${ty}`,\n // A cubic's midpoint is (P0 + 3·C1 + 3·C2 + P3) / 8 — the arc's apex, which `reach`\n // above places `loopRadius` clear of the card, so the label sits off the node.\n labelX: (sx + 3 * c1x + 3 * c2x + tx) / 8,\n labelY: (sy + 3 * c1y + 3 * c2y + ty) / 8,\n };\n}\n"],"mappings":"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","names":["useReactFlow","cn","HelperLines","useCallback","useState","jsx","jsxs","cn","getNodesBounds","useReactFlow","cn","jsx","jsxs","cn","Fragment","jsx","jsxs","jsx","getBezierPath","Position","jsx","getBezierPath","getBezierPath","useInternalNode","Position","Fragment","jsx","jsxs","useInternalNode","getBezierPath","Handle","Position","cn","jsx","jsxs","Handle","Position","getBezierPath","Plus","Fragment","jsx","jsxs","getBezierPath","Plus","Position","useCallback","useRef","useState","useNodesInitialized","useReactFlow","useReactFlow","useNodesInitialized","useState","useRef","useCallback","DEFAULT_NODE_WIDTH","DEFAULT_NODE_HEIGHT","nodeSize","useMemo","useMemo","cn","jsx","cn","jsx","jsxs","cn","jsx","jsxs","cn","useReactFlow","cn","jsx","jsxs","Handle","Position","cn","useCallback","useReactFlow","useReactFlow","useCallback","groupNodes","ungroup","collapseGroup","expandGroup","jsx","jsxs","handleClassName","cn","Handle","Position","Background","MiniMap","Panel","Position","ReactFlow","ReactFlowProvider","useReactFlow","useCallback","useMemo","Position","getBezierPath","EdgeLabelRenderer","cn","jsx","jsxs","Fragment","jsx","jsxs","useMemo","useCallback","Position","getBezierPath","useMemo","useEdges","useInternalNode","Fragment","jsx","jsxs","useEdges","useMemo","useInternalNode"]}
|