{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "animated-beam",
  "type": "registry:component",
  "title": "Animated beam",
  "description": "Light travels along curved paths from sources into a hub and out to a result, measured from the real DOM. Includes a Beam primitive to connect any two elements.",
  "dependencies": [
    "lucide-react",
    "motion"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/premium/animated-beam.tsx",
      "type": "registry:ui",
      "target": "components/ui/animated-beam.tsx",
      "content": "\"use client\"\nimport * as React from \"react\"\nimport { useReducedMotion } from \"motion/react\"\nimport { BotIcon, DatabaseIcon, FileTextIcon, MailIcon, MessageSquareIcon, UserIcon, type LucideIcon } from \"lucide-react\"\nimport { cn } from \"@/lib/utils\"\n\n/**\n * Light that travels along curved paths between elements: sources on the\n * left flow into a hub, the hub flows out to a result. Paths are measured\n * from the real DOM, so the diagram reflows with its container. Use the\n * `Beam` primitive to connect any two refs in your own layout.\n */\n\ntype BeamProps = {\n  container: React.RefObject<HTMLElement | null>\n  from: React.RefObject<HTMLElement | null>\n  to: React.RefObject<HTMLElement | null>\n  /** Vertical bend of the curve, in pixels. */\n  curvature?: number\n  duration?: number\n  delay?: number\n  reverse?: boolean\n}\n\nfunction Beam({ container, from, to, curvature = 0, duration = 3, delay = 0, reverse = false }: BeamProps) {\n  const reduce = useReducedMotion()\n  const id = React.useId().replace(/:/g, \"\")\n  const [d, setD] = React.useState(\"\")\n  const [box, setBox] = React.useState({ w: 0, h: 0 })\n  // useEffect, not useLayoutEffect: the container ref is attached after child layout effects run.\n  React.useEffect(() => {\n    const measure = () => {\n      const c = container.current?.getBoundingClientRect()\n      const a = from.current?.getBoundingClientRect()\n      const b = to.current?.getBoundingClientRect()\n      if (!c || !a || !b) return\n      const x1 = a.left - c.left + a.width / 2\n      const y1 = a.top - c.top + a.height / 2\n      const x2 = b.left - c.left + b.width / 2\n      const y2 = b.top - c.top + b.height / 2\n      const mx = (x1 + x2) / 2\n      setBox({ w: c.width, h: c.height })\n      setD(`M ${x1},${y1} C ${mx},${y1 - curvature} ${mx},${y2 - curvature} ${x2},${y2}`)\n    }\n    measure()\n    const raf = requestAnimationFrame(measure)\n    const ro = new ResizeObserver(measure)\n    if (container.current) ro.observe(container.current)\n    return () => {\n      cancelAnimationFrame(raf)\n      ro.disconnect()\n    }\n  }, [container, from, to, curvature])\n  if (!d) return null\n  return (\n    <svg aria-hidden className=\"pointer-events-none absolute inset-0 overflow-visible\" width={box.w} height={box.h} viewBox={`0 0 ${box.w} ${box.h}`}>\n      <defs>\n        <linearGradient id={`beam-${id}`} gradientUnits=\"userSpaceOnUse\" x1=\"0\" y1=\"0\" x2={box.w} y2=\"0\">\n          <stop offset=\"0\" stopColor=\"var(--accent-2)\" />\n          <stop offset=\"1\" stopColor=\"var(--accent)\" />\n        </linearGradient>\n      </defs>\n      <path d={d} fill=\"none\" stroke=\"var(--border-strong)\" strokeWidth={1.5} strokeLinecap=\"round\" />\n      {!reduce ? (\n        <path\n          d={d}\n          fill=\"none\"\n          stroke={`url(#beam-${id})`}\n          strokeWidth={2.25}\n          strokeLinecap=\"round\"\n          pathLength={100}\n          strokeDasharray=\"14 300\"\n          style={{ animation: `beam-travel ${duration}s cubic-bezier(0.45,0,0.2,1) ${delay}s infinite ${reverse ? \"reverse\" : \"normal\"}` }}\n        />\n      ) : null}\n      <style>{`@keyframes beam-travel{from{stroke-dashoffset:14}to{stroke-dashoffset:-100}}`}</style>\n    </svg>\n  )\n}\n\ntype Node = { icon: LucideIcon; label: string }\n\ntype AnimatedBeamProps = {\n  inputs?: Node[]\n  hub?: Node\n  output?: Node\n  className?: string\n}\n\nfunction NodeView({ n, r, big }: { n: Node; r: React.RefObject<HTMLDivElement | null>; big?: boolean }) {\n  const Icon = n.icon\n  return (\n    <div className=\"relative z-10 flex flex-col items-center gap-1.5\">\n      <div ref={r} className={cn(\"grid place-items-center rounded-2xl border border-border\", big ? \"size-16 bg-ink text-on-ink shadow-ink\" : \"size-11 bg-raised text-fg shadow-raised\")}>\n        <Icon className={big ? \"size-7\" : \"size-5\"} strokeWidth={1.75} />\n      </div>\n      <span className=\"text-[11px] text-fg-muted\">{n.label}</span>\n    </div>\n  )\n}\n\nfunction AnimatedBeam({\n  inputs = [\n    { icon: MailIcon, label: \"Email\" },\n    { icon: MessageSquareIcon, label: \"Chat\" },\n    { icon: FileTextIcon, label: \"Docs\" },\n    { icon: DatabaseIcon, label: \"CRM\" },\n  ],\n  hub = { icon: BotIcon, label: \"Agent\" },\n  output = { icon: UserIcon, label: \"Customer\" },\n  className,\n}: AnimatedBeamProps) {\n  const box = React.useRef<HTMLDivElement>(null)\n  const hubRef = React.useRef<HTMLDivElement>(null)\n  const outRef = React.useRef<HTMLDivElement>(null)\n  const refs = React.useRef<React.RefObject<HTMLDivElement | null>[]>([])\n  refs.current = inputs.map((_, i) => refs.current[i] ?? React.createRef<HTMLDivElement>())\n  return (\n    <div data-slot=\"animated-beam\" ref={box} className={cn(\"relative flex w-full max-w-xl items-center justify-between px-2 py-4\", className)}>\n      <div className=\"flex flex-col gap-5\">\n        {inputs.map((n, i) => (\n          <NodeView key={n.label} n={n} r={refs.current[i]} />\n        ))}\n      </div>\n      <NodeView n={hub} r={hubRef} big />\n      <NodeView n={output} r={outRef} />\n      {inputs.map((n, i) => (\n        <Beam key={n.label} container={box} from={refs.current[i]} to={hubRef} curvature={(i - (inputs.length - 1) / 2) * -18} delay={i * 0.35} />\n      ))}\n      <Beam container={box} from={hubRef} to={outRef} duration={2.4} delay={0.8} />\n      <span className=\"sr-only\">{`${inputs.map((n) => n.label).join(\", \")} flow into ${hub.label}, which responds to ${output.label}.`}</span>\n    </div>\n  )\n}\n\nexport { AnimatedBeam, Beam }\nexport type { AnimatedBeamProps, BeamProps }\n"
    }
  ]
}
