{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "animated-beam",
  "title": "Animated Beam",
  "description": "An animated gradient beam that travels along a path connecting two separate nodes.",
  "dependencies": ["framer-motion"],
  "registryDependencies": ["@godui/godui-theme"],
  "files": [
    {
      "path": "packages/components/src/animated-beam/animated-beam.tsx",
      "content": "\"use client\";\n\nimport { motion, useReducedMotion } from \"framer-motion\";\nimport * as React from \"react\";\n\nexport type AnimatedBeamProps = {\n  /** The element the SVG overlays — both refs must live inside it. */\n  containerRef: React.RefObject<HTMLElement | null>;\n  /** Element the beam starts from. */\n  fromRef: React.RefObject<HTMLElement | null>;\n  /** Element the beam ends at. */\n  toRef: React.RefObject<HTMLElement | null>;\n  /** Bow of the curve in pixels (positive bends upward). */\n  curvature?: number;\n  /** Animate from the end toward the start. */\n  reverse?: boolean;\n  /** Seconds for one travel of the gradient. */\n  duration?: number;\n  /** Delay before the first travel, in seconds. */\n  delay?: number;\n  /** Color of the static resting path. */\n  pathColor?: string;\n  /** Width of the path in pixels. */\n  pathWidth?: number;\n  /** Opacity of the static resting path. */\n  pathOpacity?: number;\n  /**\n   * SVG `stroke-dasharray` for the resting path — e.g. `\"4 5\"` for dashes or\n   * `\"0.1 8\"` (with the round cap) for dots. Omit for a solid line.\n   */\n  pathDashArray?: string;\n  /** Leading color of the travelling gradient. */\n  gradientStartColor?: string;\n  /** Trailing color of the travelling gradient. */\n  gradientStopColor?: string;\n  startXOffset?: number;\n  startYOffset?: number;\n  endXOffset?: number;\n  endYOffset?: number;\n  className?: string;\n};\n\nexport function AnimatedBeam({\n  containerRef,\n  fromRef,\n  toRef,\n  curvature = 0,\n  reverse = false,\n  duration = 3,\n  delay = 0,\n  pathColor = \"var(--border)\",\n  pathWidth = 2,\n  pathOpacity = 0.2,\n  pathDashArray,\n  gradientStartColor = \"var(--primary)\",\n  gradientStopColor = \"color-mix(in oklch, var(--primary) 40%, transparent)\",\n  startXOffset = 0,\n  startYOffset = 0,\n  endXOffset = 0,\n  endYOffset = 0,\n  className,\n}: AnimatedBeamProps) {\n  const id = React.useId();\n  const reduceMotion = useReducedMotion();\n  const [path, setPath] = React.useState(\"\");\n  const [box, setBox] = React.useState({ width: 0, height: 0 });\n\n  const gradient = reverse\n    ? { x1: [\"90%\", \"-10%\"], x2: [\"100%\", \"0%\"] }\n    : { x1: [\"10%\", \"110%\"], x2: [\"0%\", \"100%\"] };\n\n  React.useEffect(() => {\n    const update = () => {\n      const container = containerRef.current;\n      const from = fromRef.current;\n      const to = toRef.current;\n      if (!container || !from || !to) return;\n\n      const c = container.getBoundingClientRect();\n      const a = from.getBoundingClientRect();\n      const b = to.getBoundingClientRect();\n\n      setBox({ width: c.width, height: c.height });\n\n      const startX = a.left - c.left + a.width / 2 + startXOffset;\n      const startY = a.top - c.top + a.height / 2 + startYOffset;\n      const endX = b.left - c.left + b.width / 2 + endXOffset;\n      const endY = b.top - c.top + b.height / 2 + endYOffset;\n      const controlX = (startX + endX) / 2;\n      const controlY = (startY + endY) / 2 - curvature;\n\n      setPath(\n        `M ${startX},${startY} Q ${controlX},${controlY} ${endX},${endY}`,\n      );\n    };\n\n    update();\n    const ro = new ResizeObserver(update);\n    if (containerRef.current) ro.observe(containerRef.current);\n    window.addEventListener(\"resize\", update);\n    return () => {\n      ro.disconnect();\n      window.removeEventListener(\"resize\", update);\n    };\n  }, [\n    containerRef,\n    fromRef,\n    toRef,\n    curvature,\n    startXOffset,\n    startYOffset,\n    endXOffset,\n    endYOffset,\n  ]);\n\n  return (\n    <svg\n      fill=\"none\"\n      width={box.width}\n      height={box.height}\n      viewBox={`0 0 ${box.width} ${box.height}`}\n      aria-hidden=\"true\"\n      role=\"presentation\"\n      className={`pointer-events-none absolute left-0 top-0 [transform:translateZ(0)] ${className ?? \"\"}`}\n    >\n      <path\n        d={path}\n        stroke={pathColor}\n        strokeWidth={pathWidth}\n        strokeOpacity={pathOpacity}\n        strokeDasharray={pathDashArray}\n        strokeLinecap=\"round\"\n      />\n      <path\n        d={path}\n        stroke={`url(#${id})`}\n        strokeWidth={pathWidth}\n        strokeLinecap=\"round\"\n      />\n      <defs>\n        <motion.linearGradient\n          id={id}\n          gradientUnits=\"userSpaceOnUse\"\n          initial={{ x1: \"0%\", x2: \"0%\", y1: \"0%\", y2: \"0%\" }}\n          animate={\n            reduceMotion\n              ? { x1: \"0%\", x2: \"100%\", y1: \"0%\", y2: \"0%\" }\n              : {\n                  x1: gradient.x1,\n                  x2: gradient.x2,\n                  y1: [\"0%\", \"0%\"],\n                  y2: [\"0%\", \"0%\"],\n                }\n          }\n          transition={\n            reduceMotion\n              ? { duration: 0 }\n              : {\n                  duration,\n                  delay,\n                  repeat: Number.POSITIVE_INFINITY,\n                  ease: \"linear\",\n                }\n          }\n        >\n          <stop stopColor={gradientStartColor} stopOpacity=\"0\" />\n          <stop stopColor={gradientStartColor} />\n          <stop offset=\"32.5%\" stopColor={gradientStopColor} />\n          <stop offset=\"100%\" stopColor={gradientStopColor} stopOpacity=\"0\" />\n        </motion.linearGradient>\n      </defs>\n    </svg>\n  );\n}\n",
      "type": "registry:ui",
      "target": "components/godui/animated-beam.tsx"
    }
  ],
  "type": "registry:ui"
}
