{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "RippleGrid-JS-TW",
	"title": "RippleGrid",
	"description": "A grid that continuously animates with a ripple effect.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:component",
			"path": "RippleGrid/RippleGrid.jsx",
			"content": "import { useRef, useEffect } from 'react';\nimport { Renderer, Program, Triangle, Mesh } from 'ogl';\n\nconst RippleGrid = ({\n  enableRainbow = false,\n  gridColor = '#ffffff',\n  rippleIntensity = 0.05,\n  gridSize = 10.0,\n  gridThickness = 15.0,\n  fadeDistance = 1.5,\n  vignetteStrength = 2.0,\n  glowIntensity = 0.1,\n  opacity = 1.0,\n  gridRotation = 0,\n  mouseInteraction = true,\n  mouseInteractionRadius = 1,\n  lightMode = false\n}) => {\n  const containerRef = useRef(null);\n  const mousePositionRef = useRef({ x: 0.5, y: 0.5 });\n  const targetMouseRef = useRef({ x: 0.5, y: 0.5 });\n  const mouseInfluenceRef = useRef(0);\n  const uniformsRef = useRef(null);\n\n  useEffect(() => {\n    if (!containerRef.current) return;\n\n    const hexToRgb = hex => {\n      const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex);\n      return result\n        ? [parseInt(result[1], 16) / 255, parseInt(result[2], 16) / 255, parseInt(result[3], 16) / 255]\n        : [1, 1, 1];\n    };\n\n    const renderer = new Renderer({\n      dpr: Math.min(window.devicePixelRatio, 2),\n      alpha: true\n    });\n    const gl = renderer.gl;\n    gl.enable(gl.BLEND);\n    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);\n    gl.canvas.style.width = '100%';\n    gl.canvas.style.height = '100%';\n    containerRef.current.appendChild(gl.canvas);\n\n    const vert = `\nattribute vec2 position;\nvarying vec2 vUv;\nvoid main() {\n    vUv = position * 0.5 + 0.5;\n    gl_Position = vec4(position, 0.0, 1.0);\n}`;\n\n    const frag = `precision highp float;\nuniform float iTime;\nuniform vec2 iResolution;\nuniform bool enableRainbow;\nuniform vec3 gridColor;\nuniform float rippleIntensity;\nuniform float gridSize;\nuniform float gridThickness;\nuniform float fadeDistance;\nuniform float vignetteStrength;\nuniform float glowIntensity;\nuniform float opacity;\nuniform float gridRotation;\nuniform bool mouseInteraction;\nuniform vec2 mousePosition;\nuniform float mouseInfluence;\nuniform float mouseInteractionRadius;\nuniform bool lightMode;\nvarying vec2 vUv;\n\nfloat pi = 3.141592;\n\nmat2 rotate(float angle) {\n    float s = sin(angle);\n    float c = cos(angle);\n    return mat2(c, -s, s, c);\n}\n\nvoid main() {\n    vec2 uv = vUv * 2.0 - 1.0;\n    uv.x *= iResolution.x / iResolution.y;\n\n    if (gridRotation != 0.0) {\n        uv = rotate(gridRotation * pi / 180.0) * uv;\n    }\n\n    float dist = length(uv);\n    float func = sin(pi * (iTime - dist));\n    vec2 rippleUv = uv + uv * func * rippleIntensity;\n\n    if (mouseInteraction && mouseInfluence > 0.0) {\n        vec2 mouseUv = (mousePosition * 2.0 - 1.0);\n        mouseUv.x *= iResolution.x / iResolution.y;\n        float mouseDist = length(uv - mouseUv);\n        \n        float influence = mouseInfluence * exp(-mouseDist * mouseDist / (mouseInteractionRadius * mouseInteractionRadius));\n        \n        float mouseWave = sin(pi * (iTime * 2.0 - mouseDist * 3.0)) * influence;\n        rippleUv += normalize(uv - mouseUv) * mouseWave * rippleIntensity * 0.3;\n    }\n\n    vec2 a = sin(gridSize * 0.5 * pi * rippleUv - pi / 2.0);\n    vec2 b = abs(a);\n\n    float aaWidth = 0.5;\n    vec2 smoothB = vec2(\n        smoothstep(0.0, aaWidth, b.x),\n        smoothstep(0.0, aaWidth, b.y)\n    );\n\n    vec3 color = vec3(0.0);\n    color += exp(-gridThickness * smoothB.x * (0.8 + 0.5 * sin(pi * iTime)));\n    color += exp(-gridThickness * smoothB.y);\n    color += 0.5 * exp(-(gridThickness / 4.0) * sin(smoothB.x));\n    color += 0.5 * exp(-(gridThickness / 3.0) * smoothB.y);\n\n    if (glowIntensity > 0.0) {\n        color += glowIntensity * exp(-gridThickness * 0.5 * smoothB.x);\n        color += glowIntensity * exp(-gridThickness * 0.5 * smoothB.y);\n    }\n\n    float ddd = exp(-2.0 * clamp(pow(dist, fadeDistance), 0.0, 1.0));\n    \n    vec2 vignetteCoords = vUv - 0.5;\n    float vignetteDistance = length(vignetteCoords);\n    float vignette = 1.0 - pow(vignetteDistance * 2.0, vignetteStrength);\n    vignette = clamp(vignette, 0.0, 1.0);\n    \n    vec3 t;\n    if (enableRainbow) {\n        t = vec3(\n            uv.x * 0.5 + 0.5 * sin(iTime),\n            uv.y * 0.5 + 0.5 * cos(iTime),\n            pow(cos(iTime), 4.0)\n        ) + 0.5;\n    } else {\n        t = gridColor;\n    }\n\n    float finalFade = ddd * vignette;\n    float alpha = length(color) * finalFade * opacity;\n    vec3 effect = color * t * finalFade * opacity;\n    if (lightMode) {\n        float peak = max(effect.r, max(effect.g, effect.b));\n        vec3 chroma = pow(clamp(effect / max(peak, 0.0001), 0.0, 1.0), vec3(1.2));\n        gl_FragColor = vec4(mix(vec3(1.0), chroma, clamp(alpha * 0.94, 0.0, 0.94)), 1.0);\n    } else {\n        gl_FragColor = vec4(effect, alpha);\n    }\n}`;\n\n    const uniforms = {\n      iTime: { value: 0 },\n      iResolution: { value: [1, 1] },\n      enableRainbow: { value: enableRainbow },\n      gridColor: { value: hexToRgb(gridColor) },\n      rippleIntensity: { value: rippleIntensity },\n      gridSize: { value: gridSize },\n      gridThickness: { value: gridThickness },\n      fadeDistance: { value: fadeDistance },\n      vignetteStrength: { value: vignetteStrength },\n      glowIntensity: { value: glowIntensity },\n      opacity: { value: opacity },\n      gridRotation: { value: gridRotation },\n      mouseInteraction: { value: mouseInteraction },\n      mousePosition: { value: [0.5, 0.5] },\n      mouseInfluence: { value: 0 },\n      mouseInteractionRadius: { value: mouseInteractionRadius },\n      lightMode: { value: lightMode }\n    };\n\n    uniformsRef.current = uniforms;\n\n    const geometry = new Triangle(gl);\n    const program = new Program(gl, { vertex: vert, fragment: frag, uniforms });\n    const mesh = new Mesh(gl, { geometry, program });\n\n    const resize = () => {\n      const { clientWidth: w, clientHeight: h } = containerRef.current;\n      renderer.setSize(w, h);\n      uniforms.iResolution.value = [w, h];\n    };\n\n    const handleMouseMove = e => {\n      if (!mouseInteraction || !containerRef.current) return;\n      const rect = containerRef.current.getBoundingClientRect();\n      const x = (e.clientX - rect.left) / rect.width;\n      const y = 1.0 - (e.clientY - rect.top) / rect.height; // Flip Y coordinate\n      targetMouseRef.current = { x, y };\n    };\n\n    const handleMouseEnter = () => {\n      if (!mouseInteraction) return;\n      mouseInfluenceRef.current = 1.0;\n    };\n\n    const handleMouseLeave = () => {\n      if (!mouseInteraction) return;\n      mouseInfluenceRef.current = 0.0;\n    };\n\n    window.addEventListener('resize', resize);\n    if (mouseInteraction) {\n      containerRef.current.addEventListener('mousemove', handleMouseMove);\n      containerRef.current.addEventListener('mouseenter', handleMouseEnter);\n      containerRef.current.addEventListener('mouseleave', handleMouseLeave);\n    }\n    resize();\n\n    let animationFrameId;\n    const render = t => {\n      uniforms.iTime.value = t * 0.001;\n\n      const lerpFactor = 0.1;\n      mousePositionRef.current.x += (targetMouseRef.current.x - mousePositionRef.current.x) * lerpFactor;\n      mousePositionRef.current.y += (targetMouseRef.current.y - mousePositionRef.current.y) * lerpFactor;\n\n      const currentInfluence = uniforms.mouseInfluence.value;\n      const targetInfluence = mouseInfluenceRef.current;\n      uniforms.mouseInfluence.value += (targetInfluence - currentInfluence) * 0.05;\n\n      uniforms.mousePosition.value = [mousePositionRef.current.x, mousePositionRef.current.y];\n\n      renderer.render({ scene: mesh });\n      animationFrameId = requestAnimationFrame(render);\n    };\n\n    animationFrameId = requestAnimationFrame(render);\n\n    const container = containerRef.current;\n    return () => {\n      cancelAnimationFrame(animationFrameId);\n      window.removeEventListener('resize', resize);\n      if (mouseInteraction && container) {\n        container.removeEventListener('mousemove', handleMouseMove);\n        container.removeEventListener('mouseenter', handleMouseEnter);\n        container.removeEventListener('mouseleave', handleMouseLeave);\n      }\n      renderer.gl.getExtension('WEBGL_lose_context')?.loseContext();\n      container?.removeChild(gl.canvas);\n    };\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  useEffect(() => {\n    if (!uniformsRef.current) return;\n\n    const hexToRgb = hex => {\n      const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex);\n      return result\n        ? [parseInt(result[1], 16) / 255, parseInt(result[2], 16) / 255, parseInt(result[3], 16) / 255]\n        : [1, 1, 1];\n    };\n\n    uniformsRef.current.enableRainbow.value = enableRainbow;\n    uniformsRef.current.gridColor.value = hexToRgb(gridColor);\n    uniformsRef.current.rippleIntensity.value = rippleIntensity;\n    uniformsRef.current.gridSize.value = gridSize;\n    uniformsRef.current.gridThickness.value = gridThickness;\n    uniformsRef.current.fadeDistance.value = fadeDistance;\n    uniformsRef.current.vignetteStrength.value = vignetteStrength;\n    uniformsRef.current.glowIntensity.value = glowIntensity;\n    uniformsRef.current.opacity.value = opacity;\n    uniformsRef.current.gridRotation.value = gridRotation;\n    uniformsRef.current.mouseInteraction.value = mouseInteraction;\n    uniformsRef.current.mouseInteractionRadius.value = mouseInteractionRadius;\n    uniformsRef.current.lightMode.value = lightMode;\n  }, [\n    enableRainbow,\n    gridColor,\n    rippleIntensity,\n    gridSize,\n    gridThickness,\n    fadeDistance,\n    vignetteStrength,\n    glowIntensity,\n    opacity,\n    gridRotation,\n    mouseInteraction,\n    mouseInteractionRadius,\n    lightMode\n  ]);\n\n  return <div ref={containerRef} className=\"w-full h-full relative overflow-hidden [&_canvas]:block\" />;\n};\n\nexport default RippleGrid;\n"
		}
	],
	"registryDependencies": [],
	"dependencies": [
		"ogl@^1.0.11"
	]
}