{
	"$schema": "https://shadcn-svelte.com/schema/registry-item.json",
	"name": "animated-beam",
	"title": "Animated Beam",
	"type": "registry:block",
	"description": "A component for creating animated beam effects between two elements with customizable gradients and paths.",
	"dependencies": [
		"motion-sv"
	],
	"files": [
		{
			"content": "<script lang=\"ts\">\n\timport { motion } from \"motion-sv\";\n\timport { cn } from \"$UTILS$\";\n\timport type { AnimatedBeamProps } from \"./types\";\n\timport { usePathCalculator } from \"./use-path-calculator.svelte\";\n\timport { useGradientCoordinates } from \"./use-gradient-coordinates.svelte\";\n\timport { useResizeObserver } from \"./use-resize-observer.svelte\";\n\n\tlet {\n\t\tclass: className,\n\t\tcontainerRef,\n\t\tfromRef,\n\t\ttoRef,\n\t\tcurvature = 0,\n\t\treverse = false,\n\t\tduration = Math.random() * 3 + 4,\n\t\tdelay = 0,\n\t\tpathColor = \"gray\",\n\t\tpathWidth = 2,\n\t\tpathOpacity = 0.2,\n\t\tgradientStartColor = \"#ffaa40\",\n\t\tgradientStopColor = \"#9c40ff\",\n\t\tstartXOffset = 0,\n\t\tstartYOffset = 0,\n\t\tendXOffset = 0,\n\t\tendYOffset = 0,\n\t}: AnimatedBeamProps = $props();\n\n\t// Generate unique ID for gradient\n\t// let id = $state(`gradient-${Math.random().toString(36).substring(2, 11)}`);\n\tlet id = $props.id();\n\n\t// Use path calculator\n\tconst pathCalculator = usePathCalculator();\n\n\t// Calculate gradient coordinates based on reverse prop\n\tconst gradientCoordinates = $derived(useGradientCoordinates(reverse));\n\n\t// Update path function\n\tconst updatePath = () => {\n\t\tpathCalculator.calculatePath(\n\t\t\tcontainerRef,\n\t\t\tfromRef,\n\t\t\ttoRef,\n\t\t\tcurvature,\n\t\t\tstartXOffset,\n\t\t\tstartYOffset,\n\t\t\tendXOffset,\n\t\t\tendYOffset\n\t\t);\n\t};\n\n\t// Setup resize observer\n\tuseResizeObserver(() => containerRef, updatePath);\n\n\t// Watch for changes in refs and offsets\n\t$effect(() => {\n\t\tupdatePath();\n\t});\n</script>\n\n<svg\n\tfill=\"none\"\n\twidth={pathCalculator.svgDimensions.width}\n\theight={pathCalculator.svgDimensions.height}\n\txmlns=\"http://www.w3.org/2000/svg\"\n\tclass={cn(\"pointer-events-none absolute top-0 left-0 transform-gpu stroke-2\", className)}\n\tviewBox={`0 0 ${pathCalculator.svgDimensions.width} ${pathCalculator.svgDimensions.height}`}\n>\n\t<path\n\t\td={pathCalculator.pathD}\n\t\tstroke={pathColor}\n\t\tstroke-width={pathWidth}\n\t\tstroke-opacity={pathOpacity}\n\t\tstroke-linecap=\"round\"\n\t/>\n\t<path\n\t\td={pathCalculator.pathD}\n\t\tstroke-width={pathWidth}\n\t\tstroke={`url(#${id})`}\n\t\tstroke-opacity=\"1\"\n\t\tstroke-linecap=\"round\"\n\t/>\n\t<defs>\n\t\t<motion.linearGradient\n\t\t\tclass=\"transform-gpu\"\n\t\t\t{id}\n\t\t\tgradientUnits=\"userSpaceOnUse\"\n\t\t\tinitial={{\n\t\t\t\tx1: \"0%\",\n\t\t\t\tx2: \"0%\",\n\t\t\t\ty1: \"0%\",\n\t\t\t\ty2: \"0%\",\n\t\t\t}}\n\t\t\tanimate={{\n\t\t\t\tx1: gradientCoordinates.x1,\n\t\t\t\tx2: gradientCoordinates.x2,\n\t\t\t\ty1: gradientCoordinates.y1,\n\t\t\t\ty2: gradientCoordinates.y2,\n\t\t\t}}\n\t\t\ttransition={{\n\t\t\t\tdelay,\n\t\t\t\tduration,\n\t\t\t\tease: [0.16, 1, 0.3, 1],\n\t\t\t\trepeat: Infinity,\n\t\t\t\trepeatDelay: 0,\n\t\t\t}}\n\t\t>\n\t\t\t<stop stop-color={gradientStartColor} stop-opacity=\"0\"></stop>\n\t\t\t<stop stop-color={gradientStartColor}></stop>\n\t\t\t<stop offset=\"32.5%\" stop-color={gradientStopColor}></stop>\n\t\t\t<stop offset=\"100%\" stop-color={gradientStopColor} stop-opacity=\"0\"></stop>\n\t\t</motion.linearGradient>\n\t</defs>\n</svg>\n",
			"type": "registry:component",
			"target": "magic/animated-beam/animated-beam.svelte"
		},
		{
			"content": "export type { AnimatedBeamProps, PathDimensions, GradientCoordinates } from \"./types\";\nimport AnimatedBeam from \"./animated-beam.svelte\";\nexport { AnimatedBeam };\n",
			"type": "registry:file",
			"target": "magic/animated-beam/index.ts"
		},
		{
			"content": "export interface AnimatedBeamProps {\n\tclass?: string;\n\tcontainerRef: HTMLElement | null;\n\tfromRef: HTMLElement | null;\n\ttoRef: HTMLElement | null;\n\tcurvature?: number;\n\treverse?: boolean;\n\tpathColor?: string;\n\tpathWidth?: number;\n\tpathOpacity?: number;\n\tgradientStartColor?: string;\n\tgradientStopColor?: string;\n\tdelay?: number;\n\tduration?: number;\n\tstartXOffset?: number;\n\tstartYOffset?: number;\n\tendXOffset?: number;\n\tendYOffset?: number;\n}\n\nexport interface PathDimensions {\n\twidth: number;\n\theight: number;\n}\n\nexport interface GradientCoordinates {\n\tx1: string[];\n\tx2: string[];\n\ty1: string[];\n\ty2: string[];\n}\n",
			"type": "registry:file",
			"target": "magic/animated-beam/types.ts"
		},
		{
			"content": "import type { GradientCoordinates } from \"./types\";\n\nexport function useGradientCoordinates(reverse: boolean): GradientCoordinates {\n\treturn reverse\n\t\t? {\n\t\t\t\tx1: [\"90%\", \"-10%\"],\n\t\t\t\tx2: [\"100%\", \"0%\"],\n\t\t\t\ty1: [\"0%\", \"0%\"],\n\t\t\t\ty2: [\"0%\", \"0%\"],\n\t\t\t}\n\t\t: {\n\t\t\t\tx1: [\"10%\", \"110%\"],\n\t\t\t\tx2: [\"0%\", \"100%\"],\n\t\t\t\ty1: [\"0%\", \"0%\"],\n\t\t\t\ty2: [\"0%\", \"0%\"],\n\t\t\t};\n}\n",
			"type": "registry:file",
			"target": "magic/animated-beam/use-gradient-coordinates.svelte.ts"
		},
		{
			"content": "import type { PathDimensions } from \"./types\";\n\nexport function usePathCalculator() {\n\tlet pathD = $state(\"\");\n\tlet svgDimensions = $state<PathDimensions>({ width: 0, height: 0 });\n\n\tfunction calculatePath(\n\t\tcontainerRef: HTMLElement | null,\n\t\tfromRef: HTMLElement | null,\n\t\ttoRef: HTMLElement | null,\n\t\tcurvature: number,\n\t\tstartXOffset: number,\n\t\tstartYOffset: number,\n\t\tendXOffset: number,\n\t\tendYOffset: number\n\t) {\n\t\tif (!containerRef || !fromRef || !toRef) return;\n\n\t\tconst containerRect = containerRef.getBoundingClientRect();\n\t\tconst rectA = fromRef.getBoundingClientRect();\n\t\tconst rectB = toRef.getBoundingClientRect();\n\n\t\tconst svgWidth = containerRect.width;\n\t\tconst svgHeight = containerRect.height;\n\t\tsvgDimensions = { width: svgWidth, height: svgHeight };\n\n\t\tconst startX = rectA.left - containerRect.left + rectA.width / 2 + startXOffset;\n\t\tconst startY = rectA.top - containerRect.top + rectA.height / 2 + startYOffset;\n\t\tconst endX = rectB.left - containerRect.left + rectB.width / 2 + endXOffset;\n\t\tconst endY = rectB.top - containerRect.top + rectB.height / 2 + endYOffset;\n\n\t\tconst controlY = startY - curvature;\n\t\tconst d = `M ${startX},${startY} Q ${(startX + endX) / 2},${controlY} ${endX},${endY}`;\n\t\tpathD = d;\n\t}\n\n\treturn {\n\t\tget pathD() {\n\t\t\treturn pathD;\n\t\t},\n\t\tget svgDimensions() {\n\t\t\treturn svgDimensions;\n\t\t},\n\t\tcalculatePath,\n\t};\n}\n",
			"type": "registry:file",
			"target": "magic/animated-beam/use-path-calculator.svelte.ts"
		},
		{
			"content": "import { onMount } from \"svelte\";\n\nexport function useResizeObserver(getContainerRef: () => HTMLElement | null, callback: () => void) {\n\tonMount(() => {\n\t\tconst containerRef = getContainerRef();\n\t\tif (!containerRef) return;\n\n\t\tconst resizeObserver = new ResizeObserver(() => {\n\t\t\tcallback();\n\t\t});\n\n\t\tresizeObserver.observe(containerRef);\n\n\t\t// Initial call\n\t\tcallback();\n\n\t\treturn () => {\n\t\t\tresizeObserver.disconnect();\n\t\t};\n\t});\n}\n",
			"type": "registry:file",
			"target": "magic/animated-beam/use-resize-observer.svelte.ts"
		}
	]
}