{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "astral-flow",
  "title": "Astral Flow",
  "description": "A majestic, constantly breathing radial shader with deep cosmic wisps.",
  "dependencies": [
    "three",
    "@react-three/fiber",
    "clsx",
    "tailwind-merge"
  ],
  "files": [
    {
      "path": "registry/chamaac/astral-flow/astral-flow.tsx",
      "content": "\"use client\";\n\nimport { Canvas, useFrame } from \"@react-three/fiber\";\nimport { useRef, useMemo, useEffect } from \"react\";\nimport * as THREE from \"three\";\nimport { cn } from \"@/lib/utils\";\n\nconst vertexShader = `\n  varying vec2 vUv;\n  void main() {\n    vUv = uv;\n    gl_Position = vec4(position, 1.0);\n  }\n`;\n\nconst fragmentShader = `\n  uniform float uTime;\n  uniform vec2 uResolution;\n  uniform vec3 uColor1;\n  uniform vec3 uColor2;\n  uniform vec3 uColor3;\n  uniform float uFlowMin;\n  uniform float uFlowMax;\n\n  varying vec2 vUv;\n\n  // Modern Simplex/Perlin Noise Hash for smooth wispy smoke\n  vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }\n  vec2 mod289(vec2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }\n  vec3 permute(vec3 x) { return mod289(((x*34.0)+1.0)*x); }\n\n  float snoise(vec2 v) {\n      const vec4 C = vec4(0.211324865405187,  // (3.0-sqrt(3.0))/6.0\n                          0.366025403784439,  // 0.5*(sqrt(3.0)-1.0)\n                         -0.577350269189626,  // -1.0 + 2.0 * C.x\n                          0.024390243902439); // 1.0 / 41.0\n      vec2 i  = floor(v + dot(v, C.yy) );\n      vec2 x0 = v -   i + dot(i, C.xx);\n      vec2 i1;\n      i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);\n      vec4 x12 = x0.xyxy + C.xxzz;\n      x12.xy -= i1;\n      i = mod289(i); // Avoid truncation effects in permutation\n      vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))\n            + i.x + vec3(0.0, i1.x, 1.0 ));\n      vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);\n      m = m*m ;\n      m = m*m ;\n      vec3 x = 2.0 * fract(p * C.www) - 1.0;\n      vec3 h = abs(x) - 0.5;\n      vec3 ox = floor(x + 0.5);\n      vec3 a0 = x - ox;\n      m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );\n      vec3 g;\n      g.x  = a0.x  * x0.x  + h.x  * x0.y;\n      g.yz = a0.yz * x12.xz + h.yz * x12.yw;\n      return 130.0 * dot(m, g);\n  }\n\n  // Fractional Brownian Motion (fbm) to create wispy fractal smoke\n  float fbm(vec2 x) {\n      float v = 0.0;\n      float a = 0.5;\n      vec2 shift = vec2(100.0);\n      // Rotate to add spiral variation\n      mat2 rot = mat2(cos(0.5), sin(0.5), -sin(0.5), cos(0.50));\n      for (int i = 0; i < 5; ++i) { // Restored to 5 layers for maximum elegance and detail\n          v += a * snoise(x);\n          x = rot * x * 2.0 + shift;\n          a *= 0.5;\n      }\n      return v;\n  }\n\n  void main() {\n      // Fix aspect ratio for smooth scaling\n      vec2 uv = vUv;\n      vec2 p = uv * 2.0 - 1.0;\n      p.x *= uResolution.x / uResolution.y;\n\n      // Map the sine wave (-1 to 1) to the desired flow range (uFlowMin to uFlowMax)\n      float midpoint = (uFlowMax + uFlowMin) / 2.0;\n      float amplitude = (uFlowMax - uFlowMin) / 2.0;\n      \n      // Oscillate structure time to prevent radial stretching (starts at uFlowMin)\n      float flowTime = (midpoint + amplitude * sin(uTime * 0.5 - 1.5708)) * 0.25;\n      \n      // Endlessly rotating drift to keep smoke fresh without math precision breakdown\n      // By using a trig function rather than linear addition, coordinates never exceed float16 bounds!\n      vec2 drift = vec2(sin(uTime * 0.15), cos(uTime * 0.15));\n\n      // Soft radial flow outward from center (averts sharp pinch singularity)\n      float radius = length(p);\n      vec2 dir = p / (radius + 0.1); \n\n      // Primary Flow (breathing outwards and inwards smoothly)\n      vec2 q = vec2(0.);\n      q.x = fbm( p - dir * flowTime );\n      q.y = fbm( p - dir * flowTime + vec2(1.0));\n\n      // Secondary Flow (whispy detail pushing through the smoke continuously)\n      vec2 r = vec2(0.);\n      r.x = fbm( p + 1.0 * q + vec2(1.7, 9.2) - dir * flowTime * 0.8 + drift );\n      r.y = fbm( p + 1.0 * q + vec2(8.3, 2.8) - dir * flowTime * 0.6 + drift );\n\n      // Final Shape Density Map\n      float f = fbm(p + r);\n\n      // Color mapping mixing based on density depth\n      vec3 color = mix(\n          uColor1, // Darkest base\n          uColor2, // Mid-tone\n          clamp((f * f) * 4.0, 0.0, 1.0)\n      );\n\n      color = mix(\n          color,\n          uColor3, // High-light wisps\n          clamp(length(q), 0.0, 1.0)\n      );\n\n      // Enhance the wispy brightness dynamically\n      color += uColor3 * (f * f * f * 1.5) * 0.;\n\n      // Darken edges slightly for vignette\n      float vignette = length(uv - 0.5);\n      color = mix(color, vec3(0.0), smoothstep(0.4, 1.5, vignette));\n\n      // Atmospheric fade-in depth\n      float opacity = smoothstep(0.1, 0.9, f + 0.4);\n\n      gl_FragColor = vec4(color, opacity);\n  }\n`;\n\ninterface AstralFlowProps {\n  className?: string;\n  speed?: number;\n  color1?: string;\n  color2?: string;\n  color3?: string;\n  flowMin?: number;\n  flowMax?: number;\n}\n\nconst Effect = ({\n  speed,\n  color1,\n  color2,\n  color3,\n  flowMin,\n  flowMax,\n}: Required<Omit<AstralFlowProps, \"className\">>) => {\n  const material = useRef<THREE.ShaderMaterial>(null);\n\n  const uniforms = useMemo(\n    () => ({\n      uTime: { value: 0 },\n      uResolution: { value: new THREE.Vector2() },\n      uColor1: { value: new THREE.Color(color1) },\n      uColor2: { value: new THREE.Color(color2) },\n      uColor3: { value: new THREE.Color(color3) },\n      uFlowMin: { value: flowMin },\n      uFlowMax: { value: flowMax },\n    }),\n    [] // Initialize only once to prevent shader recreation\n  );\n\n  // Smoothly update uniform values\n  useEffect(() => {\n    if (material.current) {\n      material.current.uniforms.uColor1.value.set(color1);\n      material.current.uniforms.uColor2.value.set(color2);\n      material.current.uniforms.uColor3.value.set(color3);\n      material.current.uniforms.uFlowMin.value = flowMin;\n      material.current.uniforms.uFlowMax.value = flowMax;\n    }\n  }, [color1, color2, color3, flowMin, flowMax]);\n\n  useFrame((state) => {\n    if (material.current) {\n      material.current.uniforms.uTime.value =\n        (state.clock.getElapsedTime() * speed) % (40 * Math.PI);\n      material.current.uniforms.uResolution.value.set(\n        state.size.width * state.viewport.dpr,\n        state.size.height * state.viewport.dpr\n      );\n    }\n  });\n\n  return (\n    <mesh>\n      <planeGeometry args={[2, 2]} />\n      <shaderMaterial\n        ref={material}\n        vertexShader={vertexShader}\n        fragmentShader={fragmentShader}\n        uniforms={uniforms}\n        transparent={true}\n      />\n    </mesh>\n  );\n};\n\nexport default function AstralFlow({\n  className,\n  speed = 1.5,\n  color1 = \"#05070a\", // Deep void blue-black\n  color2 = \"#2e1a38\", // Moody dark plum/purple\n  color3 = \"#a0769a\", // Glowing ethereal mauve/silver\n  flowMin = 3.0,\n  flowMax = 7.0,\n}: AstralFlowProps) {\n  return (\n    <div\n      className={cn(\n        \"absolute inset-0 w-full h-full pointer-events-none overflow-hidden bg-[#05070a]\",\n        className\n      )}\n    >\n      <Canvas camera={{ position: [0, 0, 1] }} dpr={1}>\n        <Effect\n          speed={speed}\n          color1={color1}\n          color2={color2}\n          color3={color3}\n          flowMin={flowMin}\n          flowMax={flowMax}\n        />\n      </Canvas>\n      <div\n        className=\"absolute inset-0 pointer-events-none opacity-[0.4] mix-blend-overlay\"\n        style={{\n          backgroundImage: `url(\"data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E\")`,\n        }}\n      />\n    </div>\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component"
}