{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "ElectricBorder-JS-TW",
	"title": "ElectricBorder",
	"description": "Jittery electric energy border with animated arcs, glow and adjustable intensity.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:component",
			"path": "ElectricBorder/ElectricBorder.jsx",
			"content": "import { useEffect, useRef, useCallback } from 'react';\n\nfunction hexToRgba(hex, alpha = 1) {\n  if (!hex) return `rgba(0,0,0,${alpha})`;\n  let h = hex.replace('#', '');\n  if (h.length === 3) {\n    h = h\n      .split('')\n      .map(c => c + c)\n      .join('');\n  }\n  const int = parseInt(h.slice(0, 6), 16);\n  const r = (int >> 16) & 255;\n  const g = (int >> 8) & 255;\n  const b = int & 255;\n  return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n}\n\nconst ElectricBorder = ({\n  children,\n  color = '#5227FF',\n  speed = 1,\n  chaos = 0.12,\n  borderRadius = 24,\n  className,\n  style\n}) => {\n  const canvasRef = useRef(null);\n  const containerRef = useRef(null);\n  const animationRef = useRef(null);\n  const timeRef = useRef(0);\n  const lastFrameTimeRef = useRef(0);\n\n  const random = useCallback(x => {\n    return (Math.sin(x * 12.9898) * 43758.5453) % 1;\n  }, []);\n\n  const noise2D = useCallback(\n    (x, y) => {\n      const i = Math.floor(x);\n      const j = Math.floor(y);\n      const fx = x - i;\n      const fy = y - j;\n\n      const a = random(i + j * 57);\n      const b = random(i + 1 + j * 57);\n      const c = random(i + (j + 1) * 57);\n      const d = random(i + 1 + (j + 1) * 57);\n\n      const ux = fx * fx * (3.0 - 2.0 * fx);\n      const uy = fy * fy * (3.0 - 2.0 * fy);\n\n      return a * (1 - ux) * (1 - uy) + b * ux * (1 - uy) + c * (1 - ux) * uy + d * ux * uy;\n    },\n    [random]\n  );\n\n  const octavedNoise = useCallback(\n    (x, octaves, lacunarity, gain, baseAmplitude, baseFrequency, time, seed, baseFlatness) => {\n      let y = 0;\n      let amplitude = baseAmplitude;\n      let frequency = baseFrequency;\n\n      for (let i = 0; i < octaves; i++) {\n        let octaveAmplitude = amplitude;\n        if (i === 0) {\n          octaveAmplitude *= baseFlatness;\n        }\n        y += octaveAmplitude * noise2D(frequency * x + seed * 100, time * frequency * 0.3);\n        frequency *= lacunarity;\n        amplitude *= gain;\n      }\n\n      return y;\n    },\n    [noise2D]\n  );\n\n  const getCornerPoint = useCallback((centerX, centerY, radius, startAngle, arcLength, progress) => {\n    const angle = startAngle + progress * arcLength;\n    return {\n      x: centerX + radius * Math.cos(angle),\n      y: centerY + radius * Math.sin(angle)\n    };\n  }, []);\n\n  const getRoundedRectPoint = useCallback(\n    (t, left, top, width, height, radius) => {\n      const straightWidth = width - 2 * radius;\n      const straightHeight = height - 2 * radius;\n      const cornerArc = (Math.PI * radius) / 2;\n      const totalPerimeter = 2 * straightWidth + 2 * straightHeight + 4 * cornerArc;\n      const distance = t * totalPerimeter;\n\n      let accumulated = 0;\n\n      if (distance <= accumulated + straightWidth) {\n        const progress = (distance - accumulated) / straightWidth;\n        return { x: left + radius + progress * straightWidth, y: top };\n      }\n      accumulated += straightWidth;\n\n      if (distance <= accumulated + cornerArc) {\n        const progress = (distance - accumulated) / cornerArc;\n        return getCornerPoint(left + width - radius, top + radius, radius, -Math.PI / 2, Math.PI / 2, progress);\n      }\n      accumulated += cornerArc;\n\n      if (distance <= accumulated + straightHeight) {\n        const progress = (distance - accumulated) / straightHeight;\n        return { x: left + width, y: top + radius + progress * straightHeight };\n      }\n      accumulated += straightHeight;\n\n      if (distance <= accumulated + cornerArc) {\n        const progress = (distance - accumulated) / cornerArc;\n        return getCornerPoint(left + width - radius, top + height - radius, radius, 0, Math.PI / 2, progress);\n      }\n      accumulated += cornerArc;\n\n      if (distance <= accumulated + straightWidth) {\n        const progress = (distance - accumulated) / straightWidth;\n        return { x: left + width - radius - progress * straightWidth, y: top + height };\n      }\n      accumulated += straightWidth;\n\n      if (distance <= accumulated + cornerArc) {\n        const progress = (distance - accumulated) / cornerArc;\n        return getCornerPoint(left + radius, top + height - radius, radius, Math.PI / 2, Math.PI / 2, progress);\n      }\n      accumulated += cornerArc;\n\n      if (distance <= accumulated + straightHeight) {\n        const progress = (distance - accumulated) / straightHeight;\n        return { x: left, y: top + height - radius - progress * straightHeight };\n      }\n      accumulated += straightHeight;\n\n      const progress = (distance - accumulated) / cornerArc;\n      return getCornerPoint(left + radius, top + radius, radius, Math.PI, Math.PI / 2, progress);\n    },\n    [getCornerPoint]\n  );\n\n  useEffect(() => {\n    const canvas = canvasRef.current;\n    const container = containerRef.current;\n    if (!canvas || !container) return;\n\n    const ctx = canvas.getContext('2d');\n    if (!ctx) return;\n\n    const octaves = 10;\n    const lacunarity = 1.6;\n    const gain = 0.7;\n    const amplitude = chaos;\n    const frequency = 10;\n    const baseFlatness = 0;\n    const displacement = 60;\n    const borderOffset = 60;\n\n    const updateSize = () => {\n      const rect = container.getBoundingClientRect();\n      const width = rect.width + borderOffset * 2;\n      const height = rect.height + borderOffset * 2;\n\n      const dpr = Math.min(window.devicePixelRatio || 1, 2);\n      canvas.width = width * dpr;\n      canvas.height = height * dpr;\n      canvas.style.width = `${width}px`;\n      canvas.style.height = `${height}px`;\n      ctx.scale(dpr, dpr);\n\n      return { width, height };\n    };\n\n    let { width, height } = updateSize();\n    let lastDpr = Math.min(window.devicePixelRatio || 1, 2);\n\n    const drawElectricBorder = currentTime => {\n      if (!canvas || !ctx) return;\n\n      const dpr = Math.min(window.devicePixelRatio || 1, 2);\n      if (dpr !== lastDpr) {\n        lastDpr = dpr;\n        const newSize = updateSize();\n        width = newSize.width;\n        height = newSize.height;\n      }\n\n      const deltaTime = (currentTime - lastFrameTimeRef.current) / 1000;\n      timeRef.current += deltaTime * speed;\n      lastFrameTimeRef.current = currentTime;\n\n      ctx.setTransform(1, 0, 0, 1, 0, 0);\n      ctx.clearRect(0, 0, canvas.width, canvas.height);\n      ctx.scale(dpr, dpr);\n\n      ctx.strokeStyle = color;\n      ctx.lineWidth = 1;\n      ctx.lineCap = 'round';\n      ctx.lineJoin = 'round';\n\n      const scale = displacement;\n      const left = borderOffset;\n      const top = borderOffset;\n      const borderWidth = width - 2 * borderOffset;\n      const borderHeight = height - 2 * borderOffset;\n      const maxRadius = Math.min(borderWidth, borderHeight) / 2;\n      const radius = Math.min(borderRadius, maxRadius);\n\n      const approximatePerimeter = 2 * (borderWidth + borderHeight) + 2 * Math.PI * radius;\n      const sampleCount = Math.floor(approximatePerimeter / 2);\n\n      ctx.beginPath();\n\n      for (let i = 0; i <= sampleCount; i++) {\n        const progress = i / sampleCount;\n\n        const point = getRoundedRectPoint(progress, left, top, borderWidth, borderHeight, radius);\n\n        const xNoise = octavedNoise(\n          progress * 8,\n          octaves,\n          lacunarity,\n          gain,\n          amplitude,\n          frequency,\n          timeRef.current,\n          0,\n          baseFlatness\n        );\n        const yNoise = octavedNoise(\n          progress * 8,\n          octaves,\n          lacunarity,\n          gain,\n          amplitude,\n          frequency,\n          timeRef.current,\n          1,\n          baseFlatness\n        );\n\n        const displacedX = point.x + xNoise * scale;\n        const displacedY = point.y + yNoise * scale;\n\n        if (i === 0) {\n          ctx.moveTo(displacedX, displacedY);\n        } else {\n          ctx.lineTo(displacedX, displacedY);\n        }\n      }\n\n      ctx.closePath();\n      ctx.stroke();\n\n      animationRef.current = requestAnimationFrame(drawElectricBorder);\n    };\n\n    const resizeObserver = new ResizeObserver(() => {\n      const newSize = updateSize();\n      width = newSize.width;\n      height = newSize.height;\n    });\n    resizeObserver.observe(container);\n\n    animationRef.current = requestAnimationFrame(drawElectricBorder);\n\n    return () => {\n      if (animationRef.current) {\n        cancelAnimationFrame(animationRef.current);\n      }\n      resizeObserver.disconnect();\n    };\n  }, [color, speed, chaos, borderRadius, octavedNoise, getRoundedRectPoint]);\n\n  return (\n    <div\n      ref={containerRef}\n      className={`relative overflow-visible isolate ${className ?? ''}`}\n      style={{ '--electric-border-color': color, borderRadius, ...style }}\n    >\n      <div className=\"absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 pointer-events-none z-[2]\">\n        <canvas ref={canvasRef} className=\"block\" />\n      </div>\n      <div className=\"absolute inset-0 rounded-[inherit] pointer-events-none z-0\">\n        <div\n          className=\"absolute inset-0 rounded-[inherit] pointer-events-none\"\n          style={{ border: `2px solid ${hexToRgba(color, 0.6)}`, filter: 'blur(1px)' }}\n        />\n        <div\n          className=\"absolute inset-0 rounded-[inherit] pointer-events-none\"\n          style={{ border: `2px solid ${color}`, filter: 'blur(4px)' }}\n        />\n        <div\n          className=\"absolute inset-0 rounded-[inherit] pointer-events-none -z-[1] scale-110 opacity-30\"\n          style={{\n            filter: 'blur(32px)',\n            background: `linear-gradient(-30deg, ${color}, transparent, ${color})`\n          }}\n        />\n      </div>\n      <div className=\"relative rounded-[inherit] z-[1]\">{children}</div>\n    </div>\n  );\n};\n\nexport default ElectricBorder;\n"
		}
	],
	"registryDependencies": [],
	"dependencies": []
}