{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "light-pillar",
  "type": "registry:block",
  "title": "Light pillar",
  "description": "Light pillar",
  "files": [
    {
      "path": "components/usages/lightpillarusage.tsx",
      "content": "import LightPillar from \"@/registry/open-source/light-pillar\";\n\nexport default function Usage() {\n    return (\n        <div style={{ width: '100%', height: '600px', position: 'relative' }}>\n            <LightPillar\n                topColor=\"#5227FF\"\n                bottomColor=\"#FF9FFC\"\n                intensity={1}\n                rotationSpeed={0.3}\n                glowAmount={0.002}\n                pillarWidth={3}\n                pillarHeight={0.4}\n                noiseIntensity={0.5}\n                pillarRotation={25}\n                interactive={false}\n                mixBlendMode=\"screen\"\n                quality=\"high\"\n            />\n        </div>\n    );\n}",
      "type": "registry:block",
      "target": "~/example.tsx"
    },
    {
      "path": "components/usages/lightpillarusage.tsx",
      "content": "import LightPillar from \"@/registry/open-source/light-pillar\";\n\nexport default function Usage() {\n    return (\n        <div style={{ width: '100%', height: '600px', position: 'relative' }}>\n            <LightPillar\n                topColor=\"#5227FF\"\n                bottomColor=\"#FF9FFC\"\n                intensity={1}\n                rotationSpeed={0.3}\n                glowAmount={0.002}\n                pillarWidth={3}\n                pillarHeight={0.4}\n                noiseIntensity={0.5}\n                pillarRotation={25}\n                interactive={false}\n                mixBlendMode=\"screen\"\n                quality=\"high\"\n            />\n        </div>\n    );\n}",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/light-pillar.tsx",
      "content": "import React, { useRef, useEffect, useState } from 'react';\nimport * as THREE from 'three';\n\ninterface LightPillarProps {\n    topColor?: string;\n    bottomColor?: string;\n    intensity?: number;\n    rotationSpeed?: number;\n    interactive?: boolean;\n    className?: string;\n    glowAmount?: number;\n    pillarWidth?: number;\n    pillarHeight?: number;\n    noiseIntensity?: number;\n    mixBlendMode?: React.CSSProperties['mixBlendMode'];\n    pillarRotation?: number;\n    quality?: 'low' | 'medium' | 'high';\n}\n\nconst LightPillar: React.FC<LightPillarProps> = ({\n    topColor = '#5227FF',\n    bottomColor = '#FF9FFC',\n    intensity = 1.0,\n    rotationSpeed = 0.3,\n    interactive = false,\n    className = '',\n    glowAmount = 0.005,\n    pillarWidth = 3.0,\n    pillarHeight = 0.4,\n    noiseIntensity = 0.5,\n    mixBlendMode = 'screen',\n    pillarRotation = 0,\n    quality = 'high'\n}) => {\n    const containerRef = useRef<HTMLDivElement>(null);\n    const rafRef = useRef<number | null>(null);\n    const rendererRef = useRef<THREE.WebGLRenderer | null>(null);\n    const materialRef = useRef<THREE.ShaderMaterial | null>(null);\n    const sceneRef = useRef<THREE.Scene | null>(null);\n    const cameraRef = useRef<THREE.OrthographicCamera | null>(null);\n    const geometryRef = useRef<THREE.PlaneGeometry | null>(null);\n    const mouseRef = useRef<THREE.Vector2>(new THREE.Vector2(0, 0));\n    const timeRef = useRef<number>(0);\n    const [webGLSupported, setWebGLSupported] = useState<boolean>(true);\n\n    // Check WebGL support\n    useEffect(() => {\n        const canvas = document.createElement('canvas');\n        const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');\n        if (!gl) {\n            setWebGLSupported(false);\n        }\n    }, []);\n\n    useEffect(() => {\n        if (!containerRef.current || !webGLSupported) return;\n\n        const container = containerRef.current;\n        const width = container.clientWidth;\n        const height = container.clientHeight;\n\n        const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);\n        const isLowEndDevice = isMobile || (navigator.hardwareConcurrency && navigator.hardwareConcurrency <= 4);\n\n        let effectiveQuality = quality;\n        if (isLowEndDevice && quality === 'high') effectiveQuality = 'medium';\n        if (isMobile && quality !== 'low') effectiveQuality = 'low';\n\n        const qualitySettings = {\n            low: { iterations: 24, waveIterations: 1, pixelRatio: 0.5, precision: 'mediump', stepMultiplier: 1.5 },\n            medium: { iterations: 40, waveIterations: 2, pixelRatio: 0.65, precision: 'mediump', stepMultiplier: 1.2 },\n            high: {\n                iterations: 80,\n                waveIterations: 4,\n                pixelRatio: Math.min(window.devicePixelRatio, 2),\n                precision: 'highp',\n                stepMultiplier: 1.0\n            }\n        };\n\n        const settings = qualitySettings[effectiveQuality] || qualitySettings.medium;\n\n        // Scene setup\n        const scene = new THREE.Scene();\n        sceneRef.current = scene;\n        const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);\n        cameraRef.current = camera;\n\n        let renderer: THREE.WebGLRenderer;\n        try {\n            renderer = new THREE.WebGLRenderer({\n                antialias: false,\n                alpha: true,\n                powerPreference: effectiveQuality === 'low' ? 'low-power' : 'high-performance',\n                precision: settings.precision,\n                stencil: false,\n                depth: false\n            });\n        } catch (error) {\n            console.error('Failed to create WebGL renderer:', error);\n            setWebGLSupported(false);\n            return;\n        }\n\n        renderer.setSize(width, height);\n        renderer.setPixelRatio(settings.pixelRatio);\n        container.appendChild(renderer.domElement);\n        rendererRef.current = renderer;\n\n        // Convert hex colors to RGB\n        const parseColor = (hex: string): THREE.Vector3 => {\n            const color = new THREE.Color(hex);\n            return new THREE.Vector3(color.r, color.g, color.b);\n        };\n\n        // Shader material\n        const vertexShader = `\n      varying vec2 vUv;\n      void main() {\n        vUv = uv;\n        gl_Position = vec4(position, 1.0);\n      }\n    `;\n\n        const fragmentShader = `\n      uniform float uTime;\n      uniform vec2 uResolution;\n      uniform vec2 uMouse;\n      uniform vec3 uTopColor;\n      uniform vec3 uBottomColor;\n      uniform float uIntensity;\n      uniform bool uInteractive;\n      uniform float uGlowAmount;\n      uniform float uPillarWidth;\n      uniform float uPillarHeight;\n      uniform float uNoiseIntensity;\n      uniform float uPillarRotation;\n      uniform float uRotCos;\n      uniform float uRotSin;\n      uniform float uPillarRotCos;\n      uniform float uPillarRotSin;\n      uniform float uWaveSin[4];\n      uniform float uWaveCos[4];\n      varying vec2 vUv;\n\n      const float PI = 3.141592653589793;\n      const float EPSILON = 0.001;\n      const float E = 2.71828182845904523536;\n\n      float noise(vec2 coord) {\n        vec2 r = (E * sin(E * coord));\n        return fract(r.x * r.y * (1.0 + coord.x));\n      }\n\n      void main() {\n        vec2 fragCoord = vUv * uResolution;\n        vec2 uv = (fragCoord * 2.0 - uResolution) / uResolution.y;\n        \n        // Apply 2D rotation to UV coordinates using pre-computed values\n        uv = vec2(\n          uv.x * uPillarRotCos - uv.y * uPillarRotSin,\n          uv.x * uPillarRotSin + uv.y * uPillarRotCos\n        );\n\n        vec3 origin = vec3(0.0, 0.0, -10.0);\n        vec3 direction = normalize(vec3(uv, 1.0));\n\n        float maxDepth = 50.0;\n        float depth = 0.1;\n\n        // Use pre-computed rotation values (or mouse-based)\n        float rotCos = uRotCos;\n        float rotSin = uRotSin;\n        if(uInteractive && length(uMouse) > 0.0) {\n          float mouseAngle = uMouse.x * PI * 2.0;\n          rotCos = cos(mouseAngle);\n          rotSin = sin(mouseAngle);\n        }\n\n        vec3 color = vec3(0.0);\n        \n        const int ITERATIONS = ${settings.iterations};\n        const int WAVE_ITERATIONS = ${settings.waveIterations};\n        const float STEP_MULT = ${settings.stepMultiplier.toFixed(1)};\n        \n        for(int i = 0; i < ITERATIONS; i++) {\n          vec3 pos = origin + direction * depth;\n          \n          // Inline rotation: pos.xz *= rotMat\n          float newX = pos.x * rotCos - pos.z * rotSin;\n          float newZ = pos.x * rotSin + pos.z * rotCos;\n          pos.x = newX;\n          pos.z = newZ;\n\n          // Apply vertical scaling and wave deformation\n          vec3 deformed = pos;\n          deformed.y *= uPillarHeight;\n          deformed = deformed + vec3(0.0, uTime, 0.0);\n          \n          // Inlined wave deformation\n          float frequency = 1.0;\n          float amplitude = 1.0;\n          for(int j = 0; j < WAVE_ITERATIONS; j++) {\n            // Inline rotation: deformed.xz *= rot(0.4) using pre-computed\n            float wx = deformed.x * uWaveCos[j] - deformed.z * uWaveSin[j];\n            float wz = deformed.x * uWaveSin[j] + deformed.z * uWaveCos[j];\n            deformed.x = wx;\n            deformed.z = wz;\n            \n            float phase = uTime * float(j) * 2.0;\n            vec3 oscillation = cos(deformed.zxy * frequency - phase);\n            deformed += oscillation * amplitude;\n            frequency *= 2.0;\n            amplitude *= 0.5;\n          }\n          \n          // Calculate distance field using cosine pattern\n          vec2 cosinePair = cos(deformed.xz);\n          float fieldDistance = length(cosinePair) - 0.2;\n          \n          // Radial boundary constraint (inlined blendMax)\n          float radialBound = length(pos.xz) - uPillarWidth;\n          float k = 4.0;\n          float h = max(k - abs(-radialBound - (-fieldDistance)), 0.0);\n          fieldDistance = -(min(-radialBound, -fieldDistance) - h * h * 0.25 / k);\n          \n          fieldDistance = abs(fieldDistance) * 0.15 + 0.01;\n\n          vec3 gradient = mix(uBottomColor, uTopColor, smoothstep(15.0, -15.0, pos.y));\n          color += gradient / fieldDistance;\n\n          if(fieldDistance < EPSILON || depth > maxDepth) break;\n          depth += fieldDistance * STEP_MULT;\n        }\n\n        // Normalize by pillar width to maintain consistent glow regardless of size\n        float widthNormalization = uPillarWidth / 3.0;\n        color = tanh(color * uGlowAmount / widthNormalization);\n        \n        // Add noise postprocessing\n        float rnd = noise(gl_FragCoord.xy);\n        color -= rnd / 15.0 * uNoiseIntensity;\n        \n        gl_FragColor = vec4(color * uIntensity, 1.0);\n      }\n    `;\n\n        // Pre-compute wave rotation values\n        const waveAngle = 0.4;\n        const waveSinValues = new Float32Array(4);\n        const waveCosValues = new Float32Array(4);\n        for (let i = 0; i < 4; i++) {\n            waveSinValues[i] = Math.sin(waveAngle);\n            waveCosValues[i] = Math.cos(waveAngle);\n        }\n\n        // Pre-compute pillar rotation\n        const pillarRotRad = (pillarRotation * Math.PI) / 180.0;\n        const pillarRotCos = Math.cos(pillarRotRad);\n        const pillarRotSin = Math.sin(pillarRotRad);\n\n        const material = new THREE.ShaderMaterial({\n            vertexShader,\n            fragmentShader,\n            uniforms: {\n                uTime: { value: 0 },\n                uResolution: { value: new THREE.Vector2(width, height) },\n                uMouse: { value: mouseRef.current },\n                uTopColor: { value: parseColor(topColor) },\n                uBottomColor: { value: parseColor(bottomColor) },\n                uIntensity: { value: intensity },\n                uInteractive: { value: interactive },\n                uGlowAmount: { value: glowAmount },\n                uPillarWidth: { value: pillarWidth },\n                uPillarHeight: { value: pillarHeight },\n                uNoiseIntensity: { value: noiseIntensity },\n                uPillarRotation: { value: pillarRotation },\n                uRotCos: { value: 1.0 },\n                uRotSin: { value: 0.0 },\n                uPillarRotCos: { value: pillarRotCos },\n                uPillarRotSin: { value: pillarRotSin },\n                uWaveSin: { value: waveSinValues },\n                uWaveCos: { value: waveCosValues }\n            },\n            transparent: true,\n            depthWrite: false,\n            depthTest: false\n        });\n        materialRef.current = material;\n\n        const geometry = new THREE.PlaneGeometry(2, 2);\n        geometryRef.current = geometry;\n        const mesh = new THREE.Mesh(geometry, material);\n        scene.add(mesh);\n\n        // Mouse interaction - throttled for performance\n        let mouseMoveTimeout: number | null = null;\n        const handleMouseMove = (event: MouseEvent) => {\n            if (!interactive) return;\n\n            if (mouseMoveTimeout) return;\n\n            mouseMoveTimeout = window.setTimeout(() => {\n                mouseMoveTimeout = null;\n            }, 16); // ~60fps throttle\n\n            const rect = container.getBoundingClientRect();\n            const x = ((event.clientX - rect.left) / rect.width) * 2 - 1;\n            const y = -((event.clientY - rect.top) / rect.height) * 2 + 1;\n            mouseRef.current.set(x, y);\n        };\n\n        if (interactive) {\n            container.addEventListener('mousemove', handleMouseMove, { passive: true });\n        }\n\n        // Animation loop with fixed timestep\n        let lastTime = performance.now();\n        const targetFPS = effectiveQuality === 'low' ? 30 : 60;\n        const frameTime = 1000 / targetFPS;\n\n        const animate = (currentTime: number) => {\n            if (!materialRef.current || !rendererRef.current || !sceneRef.current || !cameraRef.current) return;\n\n            const deltaTime = currentTime - lastTime;\n\n            if (deltaTime >= frameTime) {\n                timeRef.current += 0.016 * rotationSpeed;\n                materialRef.current.uniforms.uTime.value = timeRef.current;\n\n                // Pre-compute rotation on CPU\n                const rotAngle = timeRef.current * 0.3;\n                materialRef.current.uniforms.uRotCos.value = Math.cos(rotAngle);\n                materialRef.current.uniforms.uRotSin.value = Math.sin(rotAngle);\n\n                rendererRef.current.render(sceneRef.current, cameraRef.current);\n                lastTime = currentTime - (deltaTime % frameTime);\n            }\n\n            rafRef.current = requestAnimationFrame(animate);\n        };\n        rafRef.current = requestAnimationFrame(animate);\n\n        // Handle resize with debouncing\n        let resizeTimeout: number | null = null;\n        const handleResize = () => {\n            if (resizeTimeout) {\n                clearTimeout(resizeTimeout);\n            }\n\n            resizeTimeout = window.setTimeout(() => {\n                if (!rendererRef.current || !materialRef.current || !containerRef.current) return;\n                const newWidth = containerRef.current.clientWidth;\n                const newHeight = containerRef.current.clientHeight;\n                rendererRef.current.setSize(newWidth, newHeight);\n                materialRef.current.uniforms.uResolution.value.set(newWidth, newHeight);\n            }, 150);\n        };\n\n        window.addEventListener('resize', handleResize, { passive: true });\n\n        // Cleanup\n        return () => {\n            window.removeEventListener('resize', handleResize);\n            if (interactive) {\n                container.removeEventListener('mousemove', handleMouseMove);\n            }\n            if (rafRef.current) {\n                cancelAnimationFrame(rafRef.current);\n            }\n            if (rendererRef.current) {\n                rendererRef.current.dispose();\n                rendererRef.current.forceContextLoss();\n                if (container.contains(rendererRef.current.domElement)) {\n                    container.removeChild(rendererRef.current.domElement);\n                }\n            }\n            if (materialRef.current) {\n                materialRef.current.dispose();\n            }\n            if (geometryRef.current) {\n                geometryRef.current.dispose();\n            }\n\n            rendererRef.current = null;\n            materialRef.current = null;\n            sceneRef.current = null;\n            cameraRef.current = null;\n            geometryRef.current = null;\n            rafRef.current = null;\n        };\n    }, [\n        topColor,\n        bottomColor,\n        intensity,\n        rotationSpeed,\n        interactive,\n        glowAmount,\n        pillarWidth,\n        pillarHeight,\n        noiseIntensity,\n        pillarRotation,\n        webGLSupported,\n        quality\n    ]);\n\n    if (!webGLSupported) {\n        return (\n            <div\n                className={`w-full h-full absolute top-0 left-0 flex items-center justify-center bg-black/10 text-gray-500 text-sm ${className}`}\n                style={{ mixBlendMode }}\n            >\n                WebGL not supported\n            </div>\n        );\n    }\n\n    return (\n        <div ref={containerRef} className={`w-full h-full absolute top-0 left-0 ${className}`} style={{ mixBlendMode }} />\n    );\n};\n\nexport default LightPillar;\n",
      "type": "registry:ui"
    }
  ]
}