{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "LightPillar-TS-CSS",
	"title": "LightPillar",
	"description": "Vertical pillar of light with glow effects.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:file",
			"path": "LightPillar.css",
			"target": "@components/LightPillar.css",
			"content": ".light-pillar-fallback {\n  width: 100%;\n  height: 100%;\n  position: absolute;\n  top: 0;\n  left: 0;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  background-color: rgba(0, 0, 0, 0.1);\n  color: #888;\n  font-size: 14px;\n}\n\n.light-pillar-container {\n  width: 100%;\n  height: 100%;\n  position: absolute;\n  top: 0;\n  left: 0;\n}\n"
		},
		{
			"type": "registry:component",
			"path": "LightPillar.tsx",
			"content": "import React, { useRef, useEffect, useState } from 'react';\nimport * as THREE from 'three';\nimport './LightPillar.css';\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  lightMode?: boolean;\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  lightMode = false\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(0);\n  const rotationSpeedRef = useRef(rotationSpeed);\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    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 === 'high' ? 'high-performance' : 'low-power',\n        precision: settings.precision as 'highp' | 'mediump' | 'lowp',\n        stencil: false,\n        depth: false\n      });\n    } catch (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    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    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      precision ${settings.precision} float;\n\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 uLightMode;\n      uniform float uRotCos;\n      uniform float uRotSin;\n      uniform float uPillarRotCos;\n      uniform float uPillarRotSin;\n      uniform float uWaveSin;\n      uniform float uWaveCos;\n      varying vec2 vUv;\n\n      const float STEP_MULT = ${settings.stepMultiplier.toFixed(1)};\n      const int MAX_ITER = ${settings.iterations};\n      const int WAVE_ITER = ${settings.waveIterations};\n\n      void main() {\n        vec2 uv = (vUv * 2.0 - 1.0) * vec2(uResolution.x / uResolution.y, 1.0);\n        uv = vec2(uPillarRotCos * uv.x - uPillarRotSin * uv.y, uPillarRotSin * uv.x + uPillarRotCos * uv.y);\n\n        vec3 ro = vec3(0.0, 0.0, -10.0);\n        vec3 rd = normalize(vec3(uv, 1.0));\n\n        float rotC = uRotCos;\n        float rotS = uRotSin;\n        if(uInteractive && (uMouse.x != 0.0 || uMouse.y != 0.0)) {\n          float a = uMouse.x * 6.283185;\n          rotC = cos(a);\n          rotS = sin(a);\n        }\n\n        vec3 col = vec3(0.0);\n        float t = 0.1;\n        \n        for(int i = 0; i < MAX_ITER; i++) {\n          vec3 p = ro + rd * t;\n          p.xz = vec2(rotC * p.x - rotS * p.z, rotS * p.x + rotC * p.z);\n\n          vec3 q = p;\n          q.y = p.y * uPillarHeight + uTime;\n          \n          float freq = 1.0;\n          float amp = 1.0;\n          for(int j = 0; j < WAVE_ITER; j++) {\n            q.xz = vec2(uWaveCos * q.x - uWaveSin * q.z, uWaveSin * q.x + uWaveCos * q.z);\n            q += cos(q.zxy * freq - uTime * float(j) * 2.0) * amp;\n            freq *= 2.0;\n            amp *= 0.5;\n          }\n          \n          float d = length(cos(q.xz)) - 0.2;\n          float bound = length(p.xz) - uPillarWidth;\n          float k = 4.0;\n          float h = max(k - abs(d - bound), 0.0);\n          d = max(d, bound) + h * h * 0.0625 / k;\n          d = abs(d) * 0.15 + 0.01;\n\n          float grad = clamp((15.0 - p.y) / 30.0, 0.0, 1.0);\n          col += mix(uBottomColor, uTopColor, grad) / d;\n\n          t += d * STEP_MULT;\n          if(t > 50.0) break;\n        }\n\n        float widthNorm = uPillarWidth / 3.0;\n        col = tanh(col * uGlowAmount / widthNorm);\n        \n        col -= fract(sin(dot(gl_FragCoord.xy, vec2(12.9898, 78.233))) * 43758.5453) / 15.0 * uNoiseIntensity;\n        \n        vec3 result = clamp(col * uIntensity, 0.0, 1.0);\n        if (uLightMode > 0.5) {\n          float energy = max(result.r, max(result.g, result.b));\n          vec3 hue = result / max(energy, 0.001);\n          float coverage = smoothstep(0.025, 0.95, energy);\n          hue = pow(clamp(hue, 0.0, 1.0), vec3(1.25));\n          result = mix(vec3(1.0), hue, coverage * 0.94);\n        }\n        gl_FragColor = vec4(result, 1.0);\n      }\n    `;\n\n    const pillarRotRad = (pillarRotation * Math.PI) / 180;\n    const waveSin = Math.sin(0.4);\n    const waveCos = Math.cos(0.4);\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        uLightMode: { value: lightMode ? 1 : 0 },\n        uRotCos: { value: 1.0 },\n        uRotSin: { value: 0.0 },\n        uPillarRotCos: { value: Math.cos(pillarRotRad) },\n        uPillarRotSin: { value: Math.sin(pillarRotRad) },\n        uWaveSin: { value: waveSin },\n        uWaveCos: { value: waveCos }\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    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);\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    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 * rotationSpeedRef.current;\n        const t = timeRef.current;\n        materialRef.current.uniforms.uTime.value = t;\n        materialRef.current.uniforms.uRotCos.value = Math.cos(t * 0.3);\n        materialRef.current.uniforms.uRotSin.value = Math.sin(t * 0.3);\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    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    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  }, [webGLSupported, quality]);\n\n  useEffect(() => {\n    rotationSpeedRef.current = rotationSpeed;\n  }, [rotationSpeed]);\n\n  useEffect(() => {\n    if (!materialRef.current) return;\n    const parseColor = (hex: string) => {\n      const color = new THREE.Color(hex);\n      return new THREE.Vector3(color.r, color.g, color.b);\n    };\n    materialRef.current.uniforms.uTopColor.value = parseColor(topColor);\n  }, [topColor]);\n\n  useEffect(() => {\n    if (!materialRef.current) return;\n    const parseColor = (hex: string) => {\n      const color = new THREE.Color(hex);\n      return new THREE.Vector3(color.r, color.g, color.b);\n    };\n    materialRef.current.uniforms.uBottomColor.value = parseColor(bottomColor);\n  }, [bottomColor]);\n\n  useEffect(() => {\n    if (!materialRef.current) return;\n    materialRef.current.uniforms.uIntensity.value = intensity;\n  }, [intensity]);\n\n  useEffect(() => {\n    if (!materialRef.current) return;\n    materialRef.current.uniforms.uInteractive.value = interactive;\n  }, [interactive]);\n\n  useEffect(() => {\n    if (!materialRef.current) return;\n    materialRef.current.uniforms.uGlowAmount.value = glowAmount;\n  }, [glowAmount]);\n\n  useEffect(() => {\n    if (!materialRef.current) return;\n    materialRef.current.uniforms.uPillarWidth.value = pillarWidth;\n  }, [pillarWidth]);\n\n  useEffect(() => {\n    if (!materialRef.current) return;\n    materialRef.current.uniforms.uPillarHeight.value = pillarHeight;\n  }, [pillarHeight]);\n\n  useEffect(() => {\n    if (!materialRef.current) return;\n    materialRef.current.uniforms.uNoiseIntensity.value = noiseIntensity;\n  }, [noiseIntensity]);\n\n  useEffect(() => {\n    if (!materialRef.current) return;\n    materialRef.current.uniforms.uLightMode.value = lightMode ? 1 : 0;\n  }, [lightMode]);\n\n  useEffect(() => {\n    if (!materialRef.current) return;\n    const pillarRotRad = (pillarRotation * Math.PI) / 180;\n    materialRef.current.uniforms.uPillarRotCos.value = Math.cos(pillarRotRad);\n    materialRef.current.uniforms.uPillarRotSin.value = Math.sin(pillarRotRad);\n  }, [pillarRotation]);\n\n  if (!webGLSupported) {\n    return (\n      <div className={`light-pillar-fallback ${className}`} style={{ mixBlendMode }}>\n        WebGL not supported\n      </div>\n    );\n  }\n\n  return <div ref={containerRef} className={`light-pillar-container ${className}`} style={{ mixBlendMode }} />;\n};\n\nexport default LightPillar;\n"
		}
	],
	"registryDependencies": [],
	"dependencies": [
		"three@^0.180.0"
	]
}