{
  "name": "animated-gradient",
  "type": "registry:ui",
  "dependencies": [],
  "registryDependencies": [],
  "description": "Component for animated-gradient",
  "files": [
    {
      "path": "components/ui/animated-gradient.tsx",
      "content": "\"use client\";\n\nimport { useRef, useEffect, useMemo, useState, CSSProperties } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { WebGLErrorBoundary, WebGLFallback } from \"./webgl-error-boundary\";\n\ntype PatternShape = \"Checks\" | \"Stripes\" | \"Edge\";\n\nconst PatternShapes: Record<PatternShape, number> = {\n    Checks: 0,\n    Stripes: 1,\n    Edge: 2,\n};\n\ninterface PresetParams {\n    color1: string;\n    color2: string;\n    color3: string;\n    rotation: number;\n    proportion: number;\n    scale: number;\n    speed: number;\n    distortion: number;\n    swirl: number;\n    swirlIterations: number;\n    softness: number;\n    offset: number;\n    shape: PatternShape;\n    shapeSize: number;\n}\n\nconst presets: Record<PresetName, PresetParams> = {\n    Aurora: {\n        color1: \"#0a001a\",\n        color2: \"#1a0b2e\",\n        color3: \"#f20089\",\n        rotation: -45,\n        proportion: 60,\n        scale: 0.6,\n        speed: 15,\n        distortion: 40,\n        swirl: 80,\n        swirlIterations: 10,\n        softness: 100,\n        offset: 200,\n        shape: \"Edge\",\n        shapeSize: 50,\n    },\n    Oceanic: {\n        color1: \"#000814\",\n        color2: \"#001d3d\",\n        color3: \"#00b4d8\",\n        rotation: 0,\n        proportion: 70,\n        scale: 0.4,\n        speed: 10,\n        distortion: 15,\n        swirl: 50,\n        swirlIterations: 12,\n        softness: 80,\n        offset: 150,\n        shape: \"Checks\",\n        shapeSize: 30,\n    },\n    Amber: {\n        color1: \"#140c00\",\n        color2: \"#4a2500\",\n        color3: \"#f57c00\",\n        rotation: 120,\n        proportion: 80,\n        scale: 0.8,\n        speed: 20,\n        distortion: 25,\n        swirl: 60,\n        swirlIterations: 8,\n        softness: 90,\n        offset: 500,\n        shape: \"Stripes\",\n        shapeSize: 40,\n    },\n    Toxic: {\n        color1: \"#050d05\",\n        color2: \"#0a240a\",\n        color3: \"#39ff14\",\n        rotation: -90,\n        proportion: 55,\n        scale: 0.5,\n        speed: 25,\n        distortion: 60,\n        swirl: 100,\n        swirlIterations: 15,\n        softness: 70,\n        offset: -100,\n        shape: \"Edge\",\n        shapeSize: 20,\n    },\n    Ghost: {\n        color1: \"#0a0a0a\",\n        color2: \"#1c1c1c\",\n        color3: \"#a3a3a3\",\n        rotation: 45,\n        proportion: 50,\n        scale: 0.3,\n        speed: 8,\n        distortion: 10,\n        swirl: 30,\n        swirlIterations: 5,\n        softness: 100,\n        offset: 0,\n        shape: \"Checks\",\n        shapeSize: 60,\n    },\n};\n\ntype PresetName = \"Aurora\" | \"Oceanic\" | \"Amber\" | \"Toxic\" | \"Ghost\";\n\nexport interface CustomConfig {\n    preset: \"custom\";\n    color1: string;\n    color2: string;\n    color3: string;\n    rotation?: number;\n    proportion?: number;\n    scale?: number;\n    speed?: number;\n    distortion?: number;\n    swirl?: number;\n    swirlIterations?: number;\n    softness?: number;\n    offset?: number;\n    shape?: PatternShape;\n    shapeSize?: number;\n}\n\nexport interface PresetConfig {\n    preset: PresetName;\n    speed?: number;\n}\n\nexport type GradientConfig = CustomConfig | PresetConfig;\n\nexport interface NoiseConfig {\n    opacity: number;\n    scale?: number;\n}\n\nexport interface AnimatedGradientProps {\n    config?: GradientConfig;\n    noise?: NoiseConfig;\n    radius?: string;\n    style?: CSSProperties;\n    className?: string;\n}\n\nexport function AnimatedGradient({\n    config = { preset: \"Aurora\" },\n    noise,\n    radius = \"0px\",\n    style,\n    className,\n}: AnimatedGradientProps) {\n    const canvasRef = useRef<HTMLCanvasElement>(null);\n    const containerRef = useRef<HTMLDivElement>(null);\n    const frameIdRef = useRef<number | undefined>(undefined);\n    const startTimeRef = useRef<number>(0);\n\n    const [isMounted, setIsMounted] = useState(false);\n    const [hasWebGLError, setHasWebGLError] = useState(false);\n\n    useEffect(() => {\n        setIsMounted(true);\n        return () => setIsMounted(false);\n    }, []);\n\n    const params = useMemo((): PresetParams => {\n        if (config.preset === \"custom\") {\n            return {\n                color1: config.color1,\n                color2: config.color2,\n                color3: config.color3,\n                rotation: config.rotation ?? 0,\n                proportion: config.proportion ?? 35,\n                scale: config.scale ?? 1,\n                speed: config.speed ?? 25,\n                distortion: config.distortion ?? 12,\n                swirl: config.swirl ?? 80,\n                swirlIterations: config.swirlIterations ?? 10,\n                softness: config.softness ?? 100,\n                offset: config.offset ?? 0,\n                shape: config.shape ?? \"Checks\",\n                shapeSize: config.shapeSize ?? 10,\n            };\n        }\n        const preset = presets[config.preset] || presets.Aurora;\n        return {\n            ...preset,\n            speed: config.speed ?? preset.speed,\n        };\n    }, [config]);\n\n    useEffect(() => {\n        if (hasWebGLError) return;\n\n        const canvas = canvasRef.current;\n        const container = containerRef.current;\n        if (!canvas || !container || !isMounted) return;\n\n        try {\n            const gl = canvas.getContext(\"webgl2\", {\n                premultipliedAlpha: true,\n                alpha: true,\n                antialias: true,\n            });\n            if (!gl) {\n                setHasWebGLError(true);\n                return;\n            }\n\n            const vertexShaderSource = `#version 300 es\n    in vec4 a_position;\n    void main() {\n      gl_Position = a_position;\n    }`;\n\n            const vertexShader = gl.createShader(gl.VERTEX_SHADER)!;\n            gl.shaderSource(vertexShader, vertexShaderSource);\n            gl.compileShader(vertexShader);\n            if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {\n                gl.deleteShader(vertexShader);\n                setHasWebGLError(true);\n                return;\n            }\n\n            const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)!;\n            gl.shaderSource(fragmentShader, FRAGMENT_SHADER);\n            gl.compileShader(fragmentShader);\n            if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {\n                gl.deleteShader(vertexShader);\n                gl.deleteShader(fragmentShader);\n                setHasWebGLError(true);\n                return;\n            }\n\n            const program = gl.createProgram()!;\n            gl.attachShader(program, vertexShader);\n            gl.attachShader(program, fragmentShader);\n            gl.linkProgram(program);\n            if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n                gl.deleteProgram(program);\n                gl.deleteShader(vertexShader);\n                gl.deleteShader(fragmentShader);\n                setHasWebGLError(true);\n                return;\n            }\n            gl.useProgram(program);\n\n            const positionBuffer = gl.createBuffer();\n            gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);\n            gl.bufferData(\n                gl.ARRAY_BUFFER,\n                new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]),\n                gl.STATIC_DRAW\n            );\n\n            const positionLocation = gl.getAttribLocation(program, \"a_position\");\n            gl.enableVertexAttribArray(positionLocation);\n            gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);\n\n            const uniforms = {\n                u_time: gl.getUniformLocation(program, \"u_time\"),\n                u_resolution: gl.getUniformLocation(program, \"u_resolution\"),\n                u_pixelRatio: gl.getUniformLocation(program, \"u_pixelRatio\"),\n                u_scale: gl.getUniformLocation(program, \"u_scale\"),\n                u_rotation: gl.getUniformLocation(program, \"u_rotation\"),\n                u_color1: gl.getUniformLocation(program, \"u_color1\"),\n                u_color2: gl.getUniformLocation(program, \"u_color2\"),\n                u_color3: gl.getUniformLocation(program, \"u_color3\"),\n                u_proportion: gl.getUniformLocation(program, \"u_proportion\"),\n                u_softness: gl.getUniformLocation(program, \"u_softness\"),\n                u_shape: gl.getUniformLocation(program, \"u_shape\"),\n                u_shapeScale: gl.getUniformLocation(program, \"u_shapeScale\"),\n                u_distortion: gl.getUniformLocation(program, \"u_distortion\"),\n                u_swirl: gl.getUniformLocation(program, \"u_swirl\"),\n                u_swirlIterations: gl.getUniformLocation(program, \"u_swirlIterations\"),\n            };\n\n            const resize = () => {\n                const width = container.clientWidth;\n                const height = container.clientHeight;\n                const pixelRatio = window.devicePixelRatio || 1;\n                canvas.width = width * pixelRatio;\n                canvas.height = height * pixelRatio;\n                canvas.style.width = `${width}px`;\n                canvas.style.height = `${height}px`;\n                gl.viewport(0, 0, canvas.width, canvas.height);\n            };\n\n            resize();\n            const resizeObserver = new ResizeObserver(resize);\n            resizeObserver.observe(container);\n\n            startTimeRef.current = performance.now();\n\n            const animate = (time: number) => {\n                const elapsed = (time - startTimeRef.current) / 1000;\n                const speed = (params.speed / 100) * 5;\n\n                gl.uniform1f(uniforms.u_time, elapsed * speed + params.offset * 0.01);\n                gl.uniform2f(uniforms.u_resolution, canvas.width, canvas.height);\n                gl.uniform1f(uniforms.u_pixelRatio, window.devicePixelRatio || 1);\n                gl.uniform1f(uniforms.u_scale, params.scale);\n                gl.uniform1f(uniforms.u_rotation, (params.rotation * Math.PI) / 180);\n\n                const c1 = hexToRgba(params.color1);\n                const c2 = hexToRgba(params.color2);\n                const c3 = hexToRgba(params.color3);\n                gl.uniform4f(uniforms.u_color1, c1[0], c1[1], c1[2], c1[3]);\n                gl.uniform4f(uniforms.u_color2, c2[0], c2[1], c2[2], c2[3]);\n                gl.uniform4f(uniforms.u_color3, c3[0], c3[1], c3[2], c3[3]);\n\n                gl.uniform1f(uniforms.u_proportion, params.proportion / 100);\n                gl.uniform1f(uniforms.u_softness, params.softness / 100);\n                gl.uniform1f(uniforms.u_shape, PatternShapes[params.shape]);\n                gl.uniform1f(uniforms.u_shapeScale, params.shapeSize / 100);\n                gl.uniform1f(uniforms.u_distortion, params.distortion / 50);\n                gl.uniform1f(uniforms.u_swirl, params.swirl / 100);\n                gl.uniform1f(\n                    uniforms.u_swirlIterations,\n                    params.swirl === 0 ? 0 : params.swirlIterations\n                );\n\n                gl.drawArrays(gl.TRIANGLES, 0, 6);\n                frameIdRef.current = requestAnimationFrame(animate);\n            };\n\n            frameIdRef.current = requestAnimationFrame(animate);\n\n            return () => {\n                if (frameIdRef.current !== undefined) {\n                    cancelAnimationFrame(frameIdRef.current);\n                }\n                resizeObserver.disconnect();\n                gl.deleteProgram(program);\n                gl.deleteShader(vertexShader);\n                gl.deleteShader(fragmentShader);\n                gl.deleteBuffer(positionBuffer);\n            };\n        } catch {\n            setHasWebGLError(true);\n            return;\n        }\n    }, [hasWebGLError, isMounted, params]);\n\n    if (hasWebGLError) {\n        return <WebGLFallback className={cn(\"absolute inset-0 overflow-hidden\", className)} />;\n    }\n\n    return (\n        <WebGLErrorBoundary\n            fallback={<WebGLFallback className={cn(\"absolute inset-0 overflow-hidden\", className)} />}\n        >\n            <div\n                ref={containerRef}\n                className={cn(\"absolute inset-0 overflow-hidden\", className)}\n                style={{\n                    borderRadius: radius,\n                    ...style,\n                    // eslint-disable-next-line @typescript-eslint/no-explicit-any\n                } as any}\n            >\n                <canvas\n                    ref={canvasRef}\n                    style={{\n                        display: \"block\",\n                        width: \"100%\",\n                        height: \"100%\",\n                    }}\n                />\n                {noise && noise.opacity > 0 && (\n                    <div\n                        style={{\n                            position: \"absolute\",\n                            inset: 0,\n                            backgroundImage: `url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAElBMVEUAAAAAAAAAAAAAAAAAAAAAAADgKxmiAAAABnRSTlMCCgkGBAVJOAVJAAAASklEQVQ4y2NgGAWjYBSMglEwCgY/YGRgZBQUYmJiZGQEkYwMjIyMgoKCjIyMIJKBgRFIMjIyAklGRkYGRkFBYEcwMDIyMjAOUQAA1I4HwVwZAkYAAAAASUVORK5CYII=\")`,\n                            backgroundSize: (noise.scale ?? 1) * 200,\n                            backgroundRepeat: \"repeat\",\n                            opacity: noise.opacity / 2,\n                            pointerEvents: \"none\",\n                        }}\n                    />\n                )}\n            </div>\n        </WebGLErrorBoundary>\n    );\n}\n\nfunction hexToRgba(hex: string): [number, number, number, number] {\n    let r = 0,\n        g = 0,\n        b = 0,\n        a = 1;\n\n    if (hex.startsWith(\"rgba(\")) {\n        const parts = hex.slice(5, -1).split(\",\");\n        r = parseInt(parts[0] ?? \"0\") / 255;\n        g = parseInt(parts[1] ?? \"0\") / 255;\n        b = parseInt(parts[2] ?? \"0\") / 255;\n        a = parseFloat(parts[3] ?? \"1\");\n    } else if (hex.startsWith(\"rgb(\")) {\n        const parts = hex.slice(4, -1).split(\",\");\n        r = parseInt(parts[0] ?? \"0\") / 255;\n        g = parseInt(parts[1] ?? \"0\") / 255;\n        b = parseInt(parts[2] ?? \"0\") / 255;\n    } else if (hex.startsWith(\"hsla(\") || hex.startsWith(\"hsl(\")) {\n        const isHsla = hex.startsWith(\"hsla(\");\n        const parts = hex.slice(isHsla ? 5 : 4, -1).split(\",\");\n        const h = parseFloat(parts[0] ?? \"0\") / 360;\n        const s = parseFloat(parts[1] ?? \"0\") / 100;\n        const l = parseFloat(parts[2] ?? \"0\") / 100;\n        a = isHsla ? parseFloat(parts[3] ?? \"1\") : 1;\n        [r, g, b] = hslToRgb(h, s, l);\n    } else if (hex.startsWith(\"#\")) {\n        const c = hex.slice(1);\n        if (c.length === 3) {\n            r = parseInt(c.charAt(0) + c.charAt(0), 16) / 255;\n            g = parseInt(c.charAt(1) + c.charAt(1), 16) / 255;\n            b = parseInt(c.charAt(2) + c.charAt(2), 16) / 255;\n        } else if (c.length >= 6) {\n            r = parseInt(c.slice(0, 2), 16) / 255;\n            g = parseInt(c.slice(2, 4), 16) / 255;\n            b = parseInt(c.slice(4, 6), 16) / 255;\n            if (c.length === 8) {\n                a = parseInt(c.slice(6, 8), 16) / 255;\n            }\n        }\n    }\n\n    return [r, g, b, a];\n}\n\nfunction hslToRgb(h: number, s: number, l: number): [number, number, number] {\n    let r: number, g: number, b: number;\n\n    if (s === 0) {\n        r = g = b = l;\n    } else {\n        const hue2rgb = (p: number, q: number, t: number) => {\n            if (t < 0) t += 1;\n            if (t > 1) t -= 1;\n            if (t < 1 / 6) return p + (q - p) * 6 * t;\n            if (t < 1 / 2) return q;\n            if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;\n            return p;\n        };\n\n        const q = l < 0.5 ? l * (1 + s) : l + s - l * s;\n        const p = 2 * l - q;\n        r = hue2rgb(p, q, h + 1 / 3);\n        g = hue2rgb(p, q, h);\n        b = hue2rgb(p, q, h - 1 / 3);\n    }\n\n    return [r, g, b];\n}\n\nconst FRAGMENT_SHADER = `#version 300 es\nprecision highp float;\n\nuniform float u_time;\nuniform float u_pixelRatio;\nuniform vec2 u_resolution;\n\nuniform float u_scale;\nuniform float u_rotation;\nuniform vec4 u_color1;\nuniform vec4 u_color2;\nuniform vec4 u_color3;\nuniform float u_proportion;\nuniform float u_softness;\nuniform float u_shape;\nuniform float u_shapeScale;\nuniform float u_distortion;\nuniform float u_swirl;\nuniform float u_swirlIterations;\n\nout vec4 fragColor;\n\n#define TWO_PI 6.28318530718\n#define PI 3.14159265358979323846\n\nvec2 rotate(vec2 uv, float th) {\n  return mat2(cos(th), sin(th), -sin(th), cos(th)) * uv;\n}\n\nfloat random(vec2 st) {\n  return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);\n}\n\nfloat noise(vec2 st) {\n  vec2 i = floor(st);\n  vec2 f = fract(st);\n  float a = random(i);\n  float b = random(i + vec2(1.0, 0.0));\n  float c = random(i + vec2(0.0, 1.0));\n  float d = random(i + vec2(1.0, 1.0));\n\n  vec2 u = f * f * (3.0 - 2.0 * f);\n\n  float x1 = mix(a, b, u.x);\n  float x2 = mix(c, d, u.x);\n  return mix(x1, x2, u.y);\n}\n\nvec4 blend_colors(vec4 c1, vec4 c2, vec4 c3, float mixer, float edgesWidth, float edge_blur) {\n    vec3 color1 = c1.rgb * c1.a;\n    vec3 color2 = c2.rgb * c2.a;\n    vec3 color3 = c3.rgb * c3.a;\n\n    float r1 = smoothstep(.0 + .35 * edgesWidth, .7 - .35 * edgesWidth + .5 * edge_blur, mixer);\n    float r2 = smoothstep(.3 + .35 * edgesWidth, 1. - .35 * edgesWidth + edge_blur, mixer);\n\n    vec3 blended_color_2 = mix(color1, color2, r1);\n    float blended_opacity_2 = mix(c1.a, c2.a, r1);\n\n    vec3 c = mix(blended_color_2, color3, r2);\n    float o = mix(blended_opacity_2, c3.a, r2);\n    return vec4(c, o);\n}\n\nvoid main() {\n    vec2 uv = gl_FragCoord.xy / u_resolution.xy;\n\n    float t = .5 * u_time;\n\n    float noise_scale = .0005 + .006 * u_scale;\n\n    uv -= .5;\n    uv *= (noise_scale * u_resolution);\n    uv = rotate(uv, u_rotation * .5 * PI);\n    uv /= u_pixelRatio;\n    uv += .5;\n\n    float n1 = noise(uv * 1. + t);\n    float n2 = noise(uv * 2. - t);\n    float angle = n1 * TWO_PI;\n    uv.x += 4. * u_distortion * n2 * cos(angle);\n    uv.y += 4. * u_distortion * n2 * sin(angle);\n\n    float iterations_number = ceil(clamp(u_swirlIterations, 1., 30.));\n    for (float i = 1.; i <= iterations_number; i++) {\n        uv.x += clamp(u_swirl, 0., 2.) / i * cos(t + i * 1.5 * uv.y);\n        uv.y += clamp(u_swirl, 0., 2.) / i * cos(t + i * 1. * uv.x);\n    }\n\n    float proportion = clamp(u_proportion, 0., 1.);\n\n    float shape = 0.;\n    float mixer = 0.;\n    if (u_shape < .5) {\n      vec2 checks_shape_uv = uv * (.5 + 3.5 * u_shapeScale);\n      shape = .5 + .5 * sin(checks_shape_uv.x) * cos(checks_shape_uv.y);\n      mixer = shape + .48 * sign(proportion - .5) * pow(abs(proportion - .5), .5);\n    } else if (u_shape < 1.5) {\n      vec2 stripes_shape_uv = uv * (.25 + 3. * u_shapeScale);\n      float f = fract(stripes_shape_uv.y);\n      shape = smoothstep(.0, .55, f) * smoothstep(1., .45, f);\n      mixer = shape + .48 * sign(proportion - .5) * pow(abs(proportion - .5), .5);\n    } else {\n      float sh = 1. - uv.y;\n      sh -= .5;\n      sh /= (noise_scale * u_resolution.y);\n      sh += .5;\n      float shape_scaling = .2 * (1. - u_shapeScale);\n      shape = smoothstep(.45 - shape_scaling, .55 + shape_scaling, sh + .3 * (proportion - .5));\n      mixer = shape;\n    }\n\n    vec4 color_mix = blend_colors(u_color1, u_color2, u_color3, mixer, 1. - clamp(u_softness, 0., 1.), .01 + .01 * u_scale);\n\n    fragColor = vec4(color_mix.rgb, color_mix.a);\n}\n`;\n",
      "type": "registry:ui"
    },
    {
      "path": "components/ui/webgl-error-boundary.tsx",
      "content": "\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport * as React from \"react\";\n\ninterface WebGLErrorBoundaryProps {\n  children: React.ReactNode;\n  fallback?: React.ReactNode;\n  onError?: (error: Error, errorInfo: React.ErrorInfo) => void;\n}\n\ninterface WebGLErrorBoundaryState {\n  hasError: boolean;\n}\n\nexport class WebGLErrorBoundary extends React.Component<\n  WebGLErrorBoundaryProps,\n  WebGLErrorBoundaryState\n> {\n  public state: WebGLErrorBoundaryState = { hasError: false };\n\n  static getDerivedStateFromError(): WebGLErrorBoundaryState {\n    return { hasError: true };\n  }\n\n  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n    this.props.onError?.(error, errorInfo);\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return this.props.fallback ?? <WebGLFallback />;\n    }\n    return this.props.children;\n  }\n}\n\ninterface WebGLFallbackProps {\n  className?: string;\n  message?: string;\n}\n\nexport function WebGLFallback({\n  className,\n  message = \"Interactive WebGL content is unavailable on this device/browser.\",\n}: WebGLFallbackProps) {\n  return (\n    <div\n      className={cn(\n        \"flex h-full w-full items-center justify-center bg-gradient-to-br from-zinc-950 via-slate-900 to-zinc-900 px-4 text-center text-sm text-white/75\",\n        className,\n      )}\n      role=\"status\"\n      aria-live=\"polite\"\n    >\n      <p>{message}</p>\n    </div>\n  );\n}\n",
      "type": "registry:ui"
    }
  ],
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "title": "Animated Gradient",
  "devDependencies": []
}