{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "flow",
  "type": "registry:ui",
  "title": "Flow",
  "description": "Node graph editor parts that skin React Flow with Control UI tokens.",
  "dependencies": [
    "@xyflow/react@^12.12.0",
    "lucide-react@^1.47.0"
  ],
  "registryDependencies": [
    "https://control-ui.dev/r/button.json",
    "https://control-ui.dev/r/canvas-grid.json",
    "https://control-ui.dev/r/core.json",
    "https://control-ui.dev/r/motion.json"
  ],
  "files": [
    {
      "path": "src/registry/knob-contracts/flow-knobs.ts",
      "target": "@components/control-ui/knob-contracts/flow-knobs.ts",
      "type": "registry:component",
      "content": "// Generated from src/registry/sources/control-ui/recipes/flow.css by scripts/gen-knob-contracts.ts — run `bun run sync:knobs`.\nexport const flowKnobs = [\n  \"--cui-flow-background\",\n  \"--cui-flow-grid-dot-color\",\n  \"--cui-flow-edge-stroke\",\n  \"--cui-flow-selected-edge-stroke\",\n  \"--cui-flow-edge-size\",\n  \"--cui-flow-node-background\",\n  \"--cui-flow-node-foreground\",\n  \"--cui-flow-node-border-color\",\n  \"--cui-flow-node-hover-border-color\",\n  \"--cui-flow-node-selected-border-color\",\n  \"--cui-flow-node-border-width\",\n  \"--cui-flow-node-radius\",\n  \"--cui-flow-node-shadow\",\n  \"--cui-flow-handle-size\",\n  \"--cui-flow-handle-background\",\n  \"--cui-flow-handle-border-color\",\n  \"--cui-flow-handle-border-width\",\n  \"--cui-flow-edge-label-background\",\n  \"--cui-flow-edge-label-foreground\",\n  \"--cui-flow-edge-label-border-color\",\n  \"--cui-flow-edge-label-border-width\",\n  \"--cui-flow-controls-item-radius\",\n  \"--cui-flow-controls-padding\",\n  \"--cui-flow-controls-radius\",\n  \"--cui-flow-controls-background\",\n  \"--cui-flow-controls-border-color\",\n  \"--cui-flow-controls-shadow\",\n] as const;\nexport type FlowKnobStyle = Partial<Record<(typeof flowKnobs)[number], string>>;\n"
    },
    {
      "path": "src/registry/sources/control-ui/recipes/flow.css",
      "target": "@components/control-ui/styles/recipes/flow.css",
      "type": "registry:file",
      "content": "@layer components {\n  :where([data-control-family=\"flow\"][data-slot=\"root\"]) {\n    --cui-flow-background: var(--canvas);\n    --cui-flow-grid-dot-color: var(--canvas-grid-dot);\n    --cui-flow-edge-stroke: oklch(from var(--muted-foreground) l c h / 0.6);\n    --cui-flow-selected-edge-stroke: var(--primary);\n    --cui-flow-edge-size: 1.5px;\n    --cui-flow-node-background: var(--card);\n    --cui-flow-node-foreground: var(--card-foreground);\n    --cui-flow-node-border-color: var(--border);\n    --cui-flow-node-hover-border-color: oklch(from var(--foreground) l c h / 0.2);\n    --cui-flow-node-selected-border-color: var(--primary);\n    --cui-flow-node-border-width: var(--control-rim-width);\n    --cui-flow-node-radius: var(--radius-lg);\n    --cui-flow-node-shadow: var(--shadow-sm);\n    --cui-flow-handle-size: calc(var(--spacing) * 2);\n    --cui-flow-handle-background: var(--background);\n    --cui-flow-handle-border-color: var(--muted-foreground);\n    --cui-flow-handle-border-width: var(--control-rim-width);\n    --cui-flow-edge-label-background: var(--background);\n    --cui-flow-edge-label-foreground: var(--muted-foreground);\n    --cui-flow-edge-label-border-color: var(--border);\n    --cui-flow-edge-label-border-width: var(--control-rim-width);\n    --cui-flow-controls-item-radius: min(var(--radius-sm), calc(var(--control-h-sm) / 2));\n    --cui-flow-controls-padding: calc(var(--spacing) * 1);\n    --cui-flow-controls-radius: calc(\n      var(--cui-flow-controls-item-radius) +\n      clamp(0px, calc(var(--cui-flow-controls-item-radius) * 1000), var(--cui-flow-controls-padding))\n    );\n    --cui-flow-controls-background: var(--card);\n    --cui-flow-controls-border-color: var(--border);\n    --cui-flow-controls-shadow: var(--shadow-sm);\n    --xy-edge-stroke: var(--cui-flow-edge-stroke);\n    --xy-edge-stroke-selected: var(--cui-flow-selected-edge-stroke);\n    --xy-connectionline-stroke: var(--cui-flow-edge-stroke);\n    --xy-connectionline-stroke-width: var(--cui-flow-edge-size);\n    --xy-selection-background-color: oklch(from var(--primary) l c h / 0.08);\n    --xy-selection-border: var(--control-rim-width) solid oklch(from var(--primary) l c h / 0.4);\n    --xy-attribution-background-color: oklch(from var(--background) l c h / 0.5);\n    background-color: var(--cui-flow-background);\n    color: var(--foreground);\n  }\n\n  :where([data-control-family=\"flow\"][data-slot=\"root\"] .react-flow__background-pattern) {\n    fill: var(--cui-flow-grid-dot-color);\n  }\n\n  :where([data-control-family=\"flow\"][data-slot=\"root\"] .react-flow__attribution a) {\n    color: var(--muted-foreground);\n  }\n\n  :where([data-control-family=\"flow\"][data-slot=\"panel\"]),\n  :where([data-control-family=\"flow\"][data-slot=\"controls\"]) {\n    margin: calc(var(--spacing) * 3);\n  }\n\n  :where([data-control-family=\"flow\"][data-slot=\"controls\"]) {\n    --nest-radius: var(--cui-flow-controls-item-radius);\n    gap: calc(var(--spacing) * 0.5);\n    padding: var(--cui-flow-controls-padding);\n    border-radius: var(--cui-flow-controls-radius);\n    background-color: var(--cui-flow-controls-background);\n    box-shadow:\n      var(--cui-flow-controls-shadow),\n      0 0 0 var(--control-rim-width) var(--cui-flow-controls-border-color);\n  }\n\n  :where([data-control-family=\"flow\"][data-slot=\"node\"]) {\n    overflow: hidden;\n    border: var(--cui-flow-node-border-width) solid var(--cui-flow-node-border-color);\n    border-radius: var(--cui-flow-node-radius);\n    background-color: var(--cui-flow-node-background);\n    color: var(--cui-flow-node-foreground);\n    box-shadow: var(--cui-flow-node-shadow);\n    transition-property: border-color, box-shadow;\n    transition-duration: var(--duration-fast);\n    transition-timing-function: var(--ease-standard);\n  }\n\n  @media (hover: hover) {\n    :where([data-control-family=\"flow\"][data-slot=\"node\"]:hover) {\n      border-color: var(--cui-flow-node-hover-border-color);\n    }\n  }\n\n  :where([data-control-family=\"flow\"][data-slot=\"node\"][data-selected]) {\n    border-color: var(--cui-flow-node-selected-border-color);\n    box-shadow:\n      var(--cui-flow-node-shadow),\n      0 0 0 calc(var(--control-rim-width) * 3) oklch(from var(--cui-flow-node-selected-border-color) l c h / 0.15);\n  }\n\n  :where([data-control-family=\"flow\"][data-slot=\"node-header\"]) {\n    gap: calc(var(--spacing) * 2);\n    min-block-size: calc(var(--spacing) * 10);\n    padding-block: calc(var(--spacing) * 1.5);\n    padding-inline: calc(var(--spacing) * 3) calc(var(--spacing) * 1.5);\n    font-size: var(--text-body);\n    font-weight: var(--font-weight-medium);\n  }\n\n  :where([data-control-family=\"flow\"][data-slot=\"node-body\"]) {\n    gap: calc(var(--spacing) * 1.5);\n    padding-inline: calc(var(--spacing) * 3);\n    padding-block-end: calc(var(--spacing) * 3);\n    font-size: var(--text-caption);\n  }\n\n  :where([data-control-family=\"flow\"][data-slot=\"handle\"]) {\n    inline-size: var(--cui-flow-handle-size);\n    block-size: var(--cui-flow-handle-size);\n    border: var(--cui-flow-handle-border-width) solid var(--cui-flow-handle-border-color);\n    border-radius: 9999px;\n    background-color: var(--cui-flow-handle-background);\n  }\n\n  :where([data-control-family=\"flow\"][data-slot=\"edge\"]) {\n    stroke: var(--cui-flow-edge-stroke);\n    stroke-width: var(--cui-flow-edge-size);\n    transition: stroke var(--duration-fast) var(--ease-standard);\n  }\n\n  :where([data-control-family=\"flow\"][data-slot=\"edge\"][data-dashed]) {\n    stroke-dasharray: 5 5;\n  }\n\n  :where(.react-flow__edge.selected [data-control-family=\"flow\"][data-slot=\"edge\"]),\n  :where(.react-flow__edge.selectable:focus-visible [data-control-family=\"flow\"][data-slot=\"edge\"]) {\n    stroke: var(--cui-flow-selected-edge-stroke);\n  }\n\n  :where([data-control-family=\"flow\"][data-slot=\"edge-label\"]) {\n    gap: calc(var(--spacing) * 1);\n  }\n\n  :where([data-control-family=\"flow\"][data-slot=\"edge-label-chip\"]) {\n    padding-block: calc(var(--spacing) * 0.5);\n    padding-inline: calc(var(--spacing) * 2);\n    border: var(--cui-flow-edge-label-border-width) solid var(--cui-flow-edge-label-border-color);\n    border-radius: 9999px;\n    background-color: var(--cui-flow-edge-label-background);\n    color: var(--cui-flow-edge-label-foreground);\n    font-size: var(--text-caption);\n    font-weight: var(--font-weight-medium);\n  }\n}\n\n@property --cui-flow-background {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-flow-grid-dot-color {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-flow-edge-stroke {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-flow-selected-edge-stroke {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-flow-edge-size {\n  syntax: \"<length>\";\n  inherits: true;\n  initial-value: 0px;\n}\n\n@property --cui-flow-node-background {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-flow-node-foreground {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-flow-node-border-color {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-flow-node-hover-border-color {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-flow-node-selected-border-color {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-flow-node-border-width {\n  syntax: \"<length>\";\n  inherits: true;\n  initial-value: 0px;\n}\n\n@property --cui-flow-node-radius {\n  syntax: \"<length-percentage>\";\n  inherits: true;\n  initial-value: 0px;\n}\n\n@property --cui-flow-node-shadow {\n  syntax: \"*\";\n  inherits: true;\n}\n\n@property --cui-flow-handle-size {\n  syntax: \"<length>\";\n  inherits: true;\n  initial-value: 0px;\n}\n\n@property --cui-flow-handle-background {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-flow-handle-border-color {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-flow-handle-border-width {\n  syntax: \"<length>\";\n  inherits: true;\n  initial-value: 0px;\n}\n\n@property --cui-flow-edge-label-background {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-flow-edge-label-foreground {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-flow-edge-label-border-color {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-flow-edge-label-border-width {\n  syntax: \"<length>\";\n  inherits: true;\n  initial-value: 0px;\n}\n\n@property --cui-flow-controls-item-radius {\n  syntax: \"<length-percentage>\";\n  inherits: true;\n  initial-value: 0px;\n}\n\n@property --cui-flow-controls-padding {\n  syntax: \"<length-percentage>\";\n  inherits: true;\n  initial-value: 0px;\n}\n\n@property --cui-flow-controls-radius {\n  syntax: \"<length-percentage>\";\n  inherits: true;\n  initial-value: 0px;\n}\n\n@property --cui-flow-controls-background {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-flow-controls-border-color {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-flow-controls-shadow {\n  syntax: \"*\";\n  inherits: true;\n}\n"
    },
    {
      "path": "src/registry/sources/control-ui/ui/flow.tsx",
      "target": "@components/control-ui/ui/flow.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport {\n  Background,\n  BackgroundVariant,\n  BaseEdge,\n  type BaseEdgeProps,\n  type Edge,\n  EdgeLabelRenderer,\n  type FitViewOptions,\n  Handle,\n  type Node,\n  Panel,\n  ReactFlow,\n  type ReactFlowProps,\n  useReactFlow,\n  useStore,\n} from \"@xyflow/react\";\nimport { MaximizeIcon, MinusIcon, PlusIcon } from \"lucide-react\";\nimport type { ComponentProps, CSSProperties } from \"react\";\nimport type { FlowKnobStyle } from \"@/components/control-ui/knob-contracts/flow-knobs\";\nimport { CANVAS_GRID_DOT_RADIUS, CANVAS_GRID_GAP } from \"@/components/control-ui/lib/canvas-grid\";\nimport { cn } from \"@/components/control-ui/lib/cn\";\nimport { prefersReducedMotion, readDurationMs } from \"@/components/control-ui/lib/motion\";\nimport { Button } from \"@/components/control-ui/ui/button\";\n\nexport type FlowProps<NodeType extends Node = Node, EdgeType extends Edge = Edge> = Omit<\n  ReactFlowProps<NodeType, EdgeType>,\n  \"colorMode\" | \"style\"\n> & { style?: CSSProperties & FlowKnobStyle };\n\nexport type FlowBackgroundProps = { id?: string; className?: string };\n\nexport type FlowPanelProps = Omit<ComponentProps<typeof Panel>, \"style\"> & { style?: CSSProperties & FlowKnobStyle };\n\nexport type FlowControlsProps = Omit<FlowPanelProps, \"children\"> & { fitViewOptions?: FitViewOptions };\n\nexport type FlowNodeProps = Omit<ComponentProps<\"div\">, \"style\"> & {\n  selected?: boolean;\n  style?: CSSProperties & FlowKnobStyle;\n};\n\nexport type FlowNodePartProps = Omit<ComponentProps<\"div\">, \"style\"> & { style?: CSSProperties & FlowKnobStyle };\n\nexport type FlowHandleProps = Omit<ComponentProps<typeof Handle>, \"style\"> & { style?: CSSProperties & FlowKnobStyle };\n\nexport type FlowEdgeProps = Omit<BaseEdgeProps, \"style\"> & { dashed?: boolean; style?: CSSProperties & FlowKnobStyle };\n\nexport type FlowEdgeLabelProps = Omit<ComponentProps<\"div\">, \"style\"> & {\n  x: number;\n  y: number;\n  style?: CSSProperties & FlowKnobStyle;\n};\n\nexport type FlowEdgeLabelChipProps = Omit<ComponentProps<\"span\">, \"style\"> & { style?: CSSProperties & FlowKnobStyle };\n\nfunction viewportTransitionMs(element: Element) {\n  return prefersReducedMotion(element) ? 0 : readDurationMs(element, \"--duration-base\");\n}\n\nexport function Flow<NodeType extends Node = Node, EdgeType extends Edge = Edge>(props: FlowProps<NodeType, EdgeType>) {\n  return (\n    <ReactFlow<NodeType, EdgeType>\n      defaultMarkerColor={null}\n      {...props}\n      data-control-ui=\"flow\"\n      data-control-family=\"flow\"\n      data-slot=\"root\"\n    />\n  );\n}\n\nexport function FlowBackground(props: FlowBackgroundProps) {\n  return <Background {...props} variant={BackgroundVariant.Dots} gap={CANVAS_GRID_GAP} size={CANVAS_GRID_DOT_RADIUS * 2} />;\n}\n\nexport function FlowPanel(props: FlowPanelProps) {\n  return <Panel {...props} data-control-ui=\"flow\" data-control-family=\"flow\" data-slot=\"panel\" />;\n}\n\nexport function FlowControls({ position = \"bottom-left\", fitViewOptions, className, ...props }: FlowControlsProps) {\n  const { zoomIn, zoomOut, fitView } = useReactFlow();\n  const atMinZoom = useStore((state) => state.transform[2] <= state.minZoom);\n  const atMaxZoom = useStore((state) => state.transform[2] >= state.maxZoom);\n  return (\n    <Panel\n      {...props}\n      position={position}\n      data-control-ui=\"flow\"\n      data-control-family=\"flow\"\n      data-slot=\"controls\"\n      className={cn(\"flex items-center\", className)}\n    >\n      <Button\n        type=\"button\"\n        size=\"sm\"\n        variant=\"ghost\"\n        iconOnly\n        disabled={atMinZoom}\n        aria-label=\"Zoom out\"\n        onClick={(event) => zoomOut({ duration: viewportTransitionMs(event.currentTarget) })}\n      >\n        <MinusIcon />\n      </Button>\n      <Button\n        type=\"button\"\n        size=\"sm\"\n        variant=\"ghost\"\n        iconOnly\n        disabled={atMaxZoom}\n        aria-label=\"Zoom in\"\n        onClick={(event) => zoomIn({ duration: viewportTransitionMs(event.currentTarget) })}\n      >\n        <PlusIcon />\n      </Button>\n      <Button\n        type=\"button\"\n        size=\"sm\"\n        variant=\"ghost\"\n        iconOnly\n        aria-label=\"Fit view\"\n        onClick={(event) => fitView({ ...fitViewOptions, duration: viewportTransitionMs(event.currentTarget) })}\n      >\n        <MaximizeIcon />\n      </Button>\n    </Panel>\n  );\n}\n\nexport function FlowNode({ selected, className, ...props }: FlowNodeProps) {\n  return (\n    <div\n      {...props}\n      data-control-ui=\"flow\"\n      data-control-family=\"flow\"\n      data-slot=\"node\"\n      data-selected={selected || undefined}\n      className={cn(\"flex flex-col\", className)}\n    />\n  );\n}\n\nexport function FlowNodeHeader({ className, ...props }: FlowNodePartProps) {\n  return (\n    <div\n      {...props}\n      data-control-ui=\"flow\"\n      data-control-family=\"flow\"\n      data-slot=\"node-header\"\n      className={cn(\"flex items-center\", className)}\n    />\n  );\n}\n\nexport function FlowNodeBody({ className, ...props }: FlowNodePartProps) {\n  return (\n    <div {...props} data-control-ui=\"flow\" data-control-family=\"flow\" data-slot=\"node-body\" className={cn(\"flex flex-col\", className)} />\n  );\n}\n\nexport function FlowHandle(props: FlowHandleProps) {\n  return <Handle {...props} data-control-ui=\"flow\" data-control-family=\"flow\" data-slot=\"handle\" />;\n}\n\nexport function FlowEdge({ dashed, ...props }: FlowEdgeProps) {\n  return <BaseEdge {...props} data-control-ui=\"flow\" data-control-family=\"flow\" data-slot=\"edge\" data-dashed={dashed || undefined} />;\n}\n\nexport function FlowEdgeLabel({ x, y, className, style, ...props }: FlowEdgeLabelProps) {\n  return (\n    <EdgeLabelRenderer>\n      <div\n        {...props}\n        data-control-ui=\"flow\"\n        data-control-family=\"flow\"\n        data-slot=\"edge-label\"\n        className={cn(\"nodrag nopan pointer-events-auto absolute flex items-center\", className)}\n        style={{ ...style, transform: `translate(-50%, -50%) translate(${x}px, ${y}px)` }}\n      />\n    </EdgeLabelRenderer>\n  );\n}\n\nexport function FlowEdgeLabelChip({ className, ...props }: FlowEdgeLabelChipProps) {\n  return (\n    <span\n      {...props}\n      data-control-ui=\"flow\"\n      data-control-family=\"flow\"\n      data-slot=\"edge-label-chip\"\n      className={cn(\"max-w-40 truncate\", className)}\n    />\n  );\n}\n"
    }
  ],
  "css": {
    "@import \"../components/control-ui/styles/recipes/flow.css\"": {}
  }
}
