{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "shader",
  "title": "Shader",
  "description": "Shader component with two variants for gradient and hurricane both can be configured.",
  "files": [
    {
      "path": "registry/blocks/shader/index.tsx",
      "content": "'use client';\n\nimport { cn } from '@/lib/utils';\nimport React from 'react';\nexport type ShaderColor = string;\n\ninterface BaseShaderProps extends React.ComponentPropsWithRef<'canvas'> {\n  colors?: [ShaderColor, ShaderColor, ShaderColor];\n  intensity?: number;\n  density?: number;\n  className?: string;\n}\n\nexport interface GradientShaderProps extends BaseShaderProps {\n  animate?: boolean;\n}\n\nexport interface HurricaneShaderProps extends BaseShaderProps {\n  background?: ShaderColor;\n  speed?: number;\n}\n\nfunction resolveColor(\n  raw: string,\n  contextEl: Element,\n): [number, number, number] {\n  let color = raw.trim();\n\n  if (color.startsWith('var(')) {\n    const varName = color.slice(4, -1).trim();\n    color = getComputedStyle(contextEl).getPropertyValue(varName).trim();\n  }\n\n  const rgbMatch = color.match(/rgba?\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)/);\n  if (rgbMatch) {\n    return [\n      parseInt(rgbMatch[1], 10) / 255,\n      parseInt(rgbMatch[2], 10) / 255,\n      parseInt(rgbMatch[3], 10) / 255,\n    ];\n  }\n\n  const hex = color.replace('#', '');\n  const full =\n    hex.length === 3\n      ? hex\n          .split('')\n          .map((c) => c + c)\n          .join('')\n      : hex;\n  return [\n    parseInt(full.slice(0, 2), 16) / 255,\n    parseInt(full.slice(2, 4), 16) / 255,\n    parseInt(full.slice(4, 6), 16) / 255,\n  ];\n}\n\nfunction compileShader(\n  gl: WebGL2RenderingContext,\n  type: number,\n  source: string,\n): WebGLShader | null {\n  const shader = gl.createShader(type);\n  if (!shader) return null;\n  gl.shaderSource(shader, source);\n  gl.compileShader(shader);\n  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n    console.error('[shader] Compile error:', gl.getShaderInfoLog(shader));\n    gl.deleteShader(shader);\n    return null;\n  }\n  return shader;\n}\n\nfunction linkProgram(\n  gl: WebGL2RenderingContext,\n  vsSrc: string,\n  fsSrc: string,\n): WebGLProgram | null {\n  const vs = compileShader(gl, gl.VERTEX_SHADER, vsSrc);\n  const fs = compileShader(gl, gl.FRAGMENT_SHADER, fsSrc);\n  if (!vs || !fs) return null;\n  const prog = gl.createProgram();\n  if (!prog) return null;\n  gl.attachShader(prog, vs);\n  gl.attachShader(prog, fs);\n  gl.linkProgram(prog);\n  gl.deleteShader(vs);\n  gl.deleteShader(fs);\n  if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {\n    console.error('[shader] Link error:', gl.getProgramInfoLog(prog));\n    gl.deleteProgram(prog);\n    return null;\n  }\n  return prog;\n}\n\nconst QUAD = new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]);\n\nconst GRADIENT_VS = /* glsl */ `#version 300 es\nin vec4 a_position;\nout vec2 v_uv;\n\nvoid main() {\n  v_uv = a_position.xy * 0.5 + 0.5;\n  gl_Position = a_position;\n}\n`;\n\nconst GRADIENT_FS = /* glsl */ `#version 300 es\nprecision highp float;\n\nin vec2 v_uv;\nout vec4 fragColor;\n\nuniform float u_time;\nuniform vec2  u_resolution;\nuniform vec3  u_color1;\nuniform vec3  u_color2;\nuniform vec3  u_color3;\n// ─── new ───────────────────────────────────────────\nuniform float u_intensity; // scales wave amplitude & noise brightness (0–2)\nuniform float u_density;   // scales noise grain frequency (0–2)\n// ───────────────────────────────────────────────────\n\n// Bayer 4×4 matrix for ordered dithering\nconst mat4 bayer = mat4(\n   0.0/64.0, 32.0/64.0,  8.0/64.0, 40.0/64.0,\n  48.0/64.0, 16.0/64.0, 56.0/64.0, 24.0/64.0,\n  12.0/64.0, 44.0/64.0,  4.0/64.0, 36.0/64.0,\n  60.0/64.0, 28.0/64.0, 52.0/64.0, 20.0/64.0\n);\n\nfloat hash(vec2 p) {\n  p = fract(p * vec2(123.34, 456.21));\n  p += dot(p, p + 45.32);\n  return fract(p.x * p.y);\n}\n\nfloat noise(vec2 p) {\n  vec2 i = floor(p);\n  vec2 f = fract(p);\n  f = f * f * (3.0 - 2.0 * f);\n  float a = hash(i);\n  float b = hash(i + vec2(1.0, 0.0));\n  float c = hash(i + vec2(0.0, 1.0));\n  float d = hash(i + vec2(1.0, 1.0));\n  return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);\n}\n\nfloat bayerDither(vec2 fragCoord) {\n  int x = int(mod(fragCoord.x, 4.0));\n  int y = int(mod(fragCoord.y, 4.0));\n  return bayer[x][y];\n}\n\nvoid main() {\n  vec2 uv        = v_uv;\n  vec2 fragCoord = uv * u_resolution;\n  float timeShift = u_time * 0.25;\n\n  // Wave patterns — amplitude scaled by u_intensity\n  float waveAmp = 0.15 * u_intensity;\n  float wave1 = sin(uv.x * 3.0 + timeShift)        * cos(uv.y * 2.0 + timeShift * 0.8);\n  float wave2 = sin(uv.y * 2.5 - timeShift * 0.6)  * cos(uv.x * 3.5 - timeShift);\n  float wave3 = sin((uv.x + uv.y) * 2.0 + timeShift * 1.2);\n  float wavePattern = (wave1 + wave2 + wave3) * waveAmp;\n\n  // Animated diagonal gradient\n  vec2 gradientDir = vec2(\n    cos(timeShift * 0.4) * 0.8 + sin(timeShift * 0.3) * 0.5,\n    sin(timeShift * 0.4) * 0.8 + cos(timeShift * 0.3) * 0.5\n  );\n  float gradientValue = dot(uv - 0.5, gradientDir) + 0.5;\n\n  // Pulsing radial component\n  float radial = length(uv - vec2(\n    0.5 + sin(timeShift * 0.3) * 0.2,\n    0.5 + cos(timeShift * 0.4) * 0.2\n  ));\n  radial *= (0.8 + sin(timeShift * 0.5) * 0.3);\n  gradientValue = mix(gradientValue, radial, 0.4);\n\n  // Wave distortion + slow drift — both scaled by u_intensity\n  gradientValue += wavePattern;\n  gradientValue += sin(u_time * 0.2 + uv.x * 2.0) * waveAmp;\n  gradientValue += cos(u_time * 0.15 + uv.y * 2.5) * waveAmp;\n  gradientValue  = clamp(gradientValue, 0.0, 1.0);\n\n  // Smooth three-color blend\n  float t1 = smoothstep(0.0, 0.5, gradientValue);\n  vec3 blend1 = mix(u_color1, u_color2, t1);\n  float t2 = smoothstep(0.3, 1.0, gradientValue);\n  vec3 finalColor = mix(blend1, u_color3, t2);\n\n  // Noise grain — frequency scaled by u_density, brightness by u_intensity\n  float noiseScale = 200.0 * u_density;\n  float noiseValue  = noise(uv * noiseScale + u_time * 0.05) * (0.015 * u_intensity);\n  finalColor += noiseValue;\n\n  // Bayer dither — strength scaled by u_intensity\n  float ditherValue = bayerDither(fragCoord) - 0.5;\n  finalColor += ditherValue * (0.004 * u_intensity);\n\n  // Subtle gamma lift\n  finalColor = pow(finalColor, vec3(0.95));\n\n  fragColor = vec4(finalColor, 1.0);\n}\n`;\n\nconst DEFAULT_GRADIENT_COLORS: [string, string, string] = [\n  '#8C59D9', // soft purple\n  '#D973A6', // soft pink\n  '#73A6F2', // soft blue\n];\n\nexport function ShaderGradient({\n  colors = DEFAULT_GRADIENT_COLORS,\n  animate = true,\n  intensity = 1,\n  density = 1,\n  className,\n  ...props\n}: GradientShaderProps) {\n  const canvasRef = React.useRef<HTMLCanvasElement>(null);\n\n  const safeIntensity = Math.max(0, intensity);\n  const safeDensity = Math.max(0.01, density);\n\n  React.useEffect(() => {\n    const canvas = canvasRef.current;\n    if (!canvas) return;\n\n    const gl = canvas.getContext('webgl2', {\n      alpha: false,\n      antialias: true,\n      powerPreference: 'high-performance',\n    });\n    if (!gl) {\n      console.error('[GradientShader] WebGL2 not supported');\n      return;\n    }\n\n    const program = linkProgram(gl, GRADIENT_VS, GRADIENT_FS);\n    if (!program) return;\n    gl.useProgram(program);\n\n    const posLoc = gl.getAttribLocation(program, 'a_position');\n    const buf = gl.createBuffer();\n    gl.bindBuffer(gl.ARRAY_BUFFER, buf);\n    gl.bufferData(gl.ARRAY_BUFFER, QUAD, gl.STATIC_DRAW);\n    const vao = gl.createVertexArray();\n    gl.bindVertexArray(vao);\n    gl.enableVertexAttribArray(posLoc);\n    gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);\n\n    const uResolution = gl.getUniformLocation(program, 'u_resolution');\n    const uTime = gl.getUniformLocation(program, 'u_time');\n    const uColor1 = gl.getUniformLocation(program, 'u_color1');\n    const uColor2 = gl.getUniformLocation(program, 'u_color2');\n    const uColor3 = gl.getUniformLocation(program, 'u_color3');\n    const uIntensity = gl.getUniformLocation(program, 'u_intensity');\n    const uDensity = gl.getUniformLocation(program, 'u_density');\n\n    const [c1, c2, c3] = colors.map((c) => resolveColor(c, canvas));\n    gl.uniform3f(uColor1, ...c1);\n    gl.uniform3f(uColor2, ...c2);\n    gl.uniform3f(uColor3, ...c3);\n    gl.uniform1f(uIntensity, safeIntensity);\n    gl.uniform1f(uDensity, safeDensity);\n\n    const handleResize = () => {\n      const dpr = window.devicePixelRatio || 1;\n      const w = Math.floor(canvas.clientWidth * dpr);\n      const h = Math.floor(canvas.clientHeight * dpr);\n      if (canvas.width !== w || canvas.height !== h) {\n        canvas.width = w;\n        canvas.height = h;\n        gl.viewport(0, 0, w, h);\n        gl.uniform2f(uResolution, w, h);\n      }\n    };\n    handleResize();\n\n    const ro = new ResizeObserver(() => {\n      handleResize();\n      if (!animate) renderFrame(0);\n    });\n    ro.observe(canvas);\n\n    let rafId = 0;\n\n    const renderFrame = (ts: number) => {\n      gl.clearColor(0, 0, 0, 1);\n      gl.clear(gl.COLOR_BUFFER_BIT);\n      gl.uniform1f(uTime, ts * 0.001);\n      gl.drawArrays(gl.TRIANGLES, 0, 6);\n    };\n\n    if (animate) {\n      const loop = (ts: number) => {\n        renderFrame(ts);\n        rafId = requestAnimationFrame(loop);\n      };\n      rafId = requestAnimationFrame(loop);\n    } else {\n      renderFrame(0);\n    }\n\n    return () => {\n      cancelAnimationFrame(rafId);\n      ro.disconnect();\n      gl.deleteBuffer(buf);\n      gl.deleteVertexArray(vao);\n      gl.deleteProgram(program);\n    };\n  }, [colors, animate, safeIntensity, safeDensity]);\n\n  return (\n    <canvas\n      ref={canvasRef}\n      className={cn('block size-full', className)}\n      {...props}\n    />\n  );\n}\n\nconst PARTICLE_VS = /* glsl */ `#version 300 es\nprecision highp float;\n\nin vec2  a_position;\nin vec3  a_color;\nin float a_size;\nin float a_alpha;\n\nout vec3  v_color;\nout float v_alpha;\n\nuniform vec2  u_resolution;\nuniform float u_dpr;\n\nvoid main() {\n  vec2 clip = (a_position / u_resolution) * 2.0 - 1.0;\n  gl_Position = vec4(clip * vec2(1.0, -1.0), 0.0, 1.0);\n  gl_PointSize = a_size * u_dpr * 1.8;\n  v_color = a_color;\n  v_alpha = a_alpha;\n}\n`;\n\nconst PARTICLE_FS = /* glsl */ `#version 300 es\nprecision highp float;\n\nin vec3  v_color;\nin float v_alpha;\nout vec4 fragColor;\n\nvoid main() {\n  vec2  coord = gl_PointCoord - vec2(0.5);\n  float dist  = length(coord);\n\n  float alpha = (1.0 - smoothstep(0.0, 0.5, dist)) * v_alpha;\n  alpha = pow(alpha, 1.3);\n\n  float core = 1.0 - smoothstep(0.0, 0.2, dist);\n  vec3 finalColor = mix(v_color, vec3(1.0), core * 0.4);\n\n  if (alpha <= 0.01) discard;\n  fragColor = vec4(finalColor * alpha, alpha); // pre-multiplied alpha\n}\n`;\n\nconst FADE_VS = /* glsl */ `#version 300 es\nprecision highp float;\nin vec2 a_position;\nvoid main() { gl_Position = vec4(a_position, 0.0, 1.0); }\n`;\n\nconst FADE_FS = /* glsl */ `#version 300 es\nprecision highp float;\nuniform vec3 u_background;\nout vec4 fragColor;\nvoid main() { fragColor = vec4(u_background, 0.25); }\n`;\n\nclass Particle {\n  x = 0;\n  y = 0;\n  angle = 0;\n  radius = 0;\n  radiusVel = 0;\n  angularVel = 0;\n  spiralSpeed = 0;\n  r = 0;\n  g = 0;\n  b = 0;\n  size = 0;\n  life = 0;\n  maxLife = 0;\n  layer = 0;\n\n  constructor(\n    width: number,\n    height: number,\n    colors: [number, number, number][],\n    sizeMultiplier: number,\n    speedMultiplier: number,\n  ) {\n    this.spawn(width, height, colors, sizeMultiplier, speedMultiplier);\n  }\n\n  spawn(\n    width: number,\n    height: number,\n    colors: [number, number, number][],\n    sizeMultiplier: number,\n    speedMultiplier: number,\n  ) {\n    this.angle = Math.random() * Math.PI * 2;\n    this.radius = Math.random() * 80 + 20;\n    this.x = width / 2 + Math.cos(this.angle) * this.radius;\n    this.y = height / 2 + Math.sin(this.angle) * this.radius;\n    this.layer = Math.random();\n\n    // Pick a resolved color\n    const [r, g, b] = colors[Math.floor(Math.random() * colors.length)];\n    this.r = r;\n    this.g = g;\n    this.b = b;\n\n    if (this.layer < 0.4) {\n      // Inner rays — larger, faster\n      this.size = (Math.random() * 3 + 5) * sizeMultiplier;\n      this.radiusVel = (Math.random() * 50 + 70) * speedMultiplier;\n      this.angularVel = (Math.random() * 1.8 + 1.8) * speedMultiplier;\n      this.spiralSpeed = (Math.random() * 0.25 + 0.25) * speedMultiplier;\n      this.maxLife = Math.random() * 1.5 + 2;\n    } else {\n      // Outer swirl — smaller, slower\n      this.size = (Math.random() * 2 + 2) * sizeMultiplier;\n      this.radiusVel = (Math.random() * 30 + 40) * speedMultiplier;\n      this.angularVel = (Math.random() * 2.2 + 1.2) * speedMultiplier;\n      this.spiralSpeed = (Math.random() * 0.35 + 0.2) * speedMultiplier;\n      this.maxLife = Math.random() * 2.5 + 2;\n    }\n    this.life = this.maxLife;\n  }\n}\n\nconst DEFAULT_HURRICANE_COLORS: [string, string, string] = [\n  '#00f2fe',\n  '#4facfe',\n  '#ffd700',\n];\nconst DEFAULT_BACKGROUND = '#0a0a0a';\nconst BASE_PARTICLE_COUNT = 100;\n\nexport function ShaderHurricane({\n  colors = DEFAULT_HURRICANE_COLORS,\n  background = DEFAULT_BACKGROUND,\n  intensity = 1,\n  density = 1,\n  speed = 0.2,\n  className,\n  ...props\n}: HurricaneShaderProps) {\n  const canvasRef = React.useRef<HTMLCanvasElement>(null);\n\n  const safeIntensity = Math.max(0, intensity);\n  const safeDensity = Math.max(0.1, density);\n  const safeSpeed = Math.max(0, speed);\n\n  React.useEffect(() => {\n    const canvas = canvasRef.current;\n    if (!canvas) return;\n\n    const gl = canvas.getContext('webgl2', {\n      alpha: false,\n      antialias: false,\n      depth: false,\n      preserveDrawingBuffer: true,\n      powerPreference: 'high-performance',\n    });\n    if (!gl) {\n      console.error('[HurricaneShader] WebGL2 not supported');\n      return;\n    }\n\n    const particleProg = linkProgram(gl, PARTICLE_VS, PARTICLE_FS);\n    const fadeProg = linkProgram(gl, FADE_VS, FADE_FS);\n    if (!particleProg || !fadeProg) return;\n\n    const pLoc = {\n      position: gl.getAttribLocation(particleProg, 'a_position'),\n      color: gl.getAttribLocation(particleProg, 'a_color'),\n      size: gl.getAttribLocation(particleProg, 'a_size'),\n      alpha: gl.getAttribLocation(particleProg, 'a_alpha'),\n      resolution: gl.getUniformLocation(particleProg, 'u_resolution'),\n      dpr: gl.getUniformLocation(particleProg, 'u_dpr'),\n    };\n    const fLoc = {\n      position: gl.getAttribLocation(fadeProg, 'a_position'),\n      background: gl.getUniformLocation(fadeProg, 'u_background'),\n    };\n\n    const quadBuf = gl.createBuffer()!;\n    gl.bindBuffer(gl.ARRAY_BUFFER, quadBuf);\n    gl.bufferData(gl.ARRAY_BUFFER, QUAD, gl.STATIC_DRAW);\n\n    const resolvedColors = colors.map((c) => resolveColor(c, canvas)) as [\n      number,\n      number,\n      number,\n    ][];\n\n    const bgColor = resolveColor(background, canvas);\n    const isMobile = window.innerWidth < 768;\n    const baseCount = isMobile ? BASE_PARTICLE_COUNT / 2 : BASE_PARTICLE_COUNT;\n    const particleCount = Math.round(baseCount * safeDensity);\n\n    const particles = Array.from(\n      { length: particleCount },\n      () =>\n        new Particle(\n          window.innerWidth,\n          window.innerHeight,\n          resolvedColors,\n          safeIntensity,\n          safeSpeed,\n        ),\n    );\n\n    const STRIDE = 7;\n    const particleData = new Float32Array(particleCount * STRIDE);\n    const particleBuf = gl.createBuffer()!;\n    gl.bindBuffer(gl.ARRAY_BUFFER, particleBuf);\n    gl.bufferData(gl.ARRAY_BUFFER, particleData.byteLength, gl.DYNAMIC_DRAW);\n\n    let width = canvas.clientWidth;\n    let height = canvas.clientHeight;\n\n    const handleResize = () => {\n      const dpr = Math.min(window.devicePixelRatio || 1, 2);\n      width = canvas.clientWidth;\n      height = canvas.clientHeight;\n      canvas.width = width * dpr;\n      canvas.height = height * dpr;\n      gl.viewport(0, 0, canvas.width, canvas.height);\n      gl.clearColor(bgColor[0], bgColor[1], bgColor[2], 1);\n      gl.clear(gl.COLOR_BUFFER_BIT);\n    };\n    const ro = new ResizeObserver(handleResize);\n    ro.observe(canvas);\n    handleResize();\n\n    const scroll = { velocity: 0 };\n    let lastScrollY = window.scrollY;\n    const onScroll = () => {\n      scroll.velocity = window.scrollY - lastScrollY;\n      lastScrollY = window.scrollY;\n    };\n    window.addEventListener('scroll', onScroll, { passive: true });\n\n    let rafId = 0;\n    let lastTime = performance.now();\n\n    const render = (now: number) => {\n      const dt = Math.min((now - lastTime) / 1000, 0.1);\n      lastTime = now;\n\n      scroll.velocity *= 0.95;\n      const scrollBoost = Math.abs(scroll.velocity) * 0.05;\n      const cx = width / 2;\n      const cy = height / 2;\n      const maxRadius = Math.max(width, height) * 0.7;\n\n      for (let i = 0; i < particleCount; i++) {\n        const p = particles[i];\n        p.radius += p.radiusVel * dt * (1 + scrollBoost * 0.5);\n        p.angle += p.angularVel * dt * (1 + scrollBoost * 0.3);\n        p.angle += p.spiralSpeed * (p.radius / 100) * dt;\n        p.x = cx + Math.cos(p.angle) * p.radius;\n        p.y = cy + Math.sin(p.angle) * p.radius;\n        p.life -= dt;\n\n        if (p.life <= 0 || p.radius > maxRadius) {\n          p.spawn(width, height, resolvedColors, safeIntensity, safeSpeed);\n        }\n\n        const lifePct = p.life / p.maxLife;\n        const alpha = Math.min(1, lifePct * 3) * Math.min(1, (1 - lifePct) * 5);\n        const off = i * STRIDE;\n        particleData[off + 0] = p.x;\n        particleData[off + 1] = p.y;\n        particleData[off + 2] = p.r;\n        particleData[off + 3] = p.g;\n        particleData[off + 4] = p.b;\n        particleData[off + 5] = p.size;\n        particleData[off + 6] = alpha;\n      }\n\n      gl.useProgram(fadeProg);\n      gl.uniform3f(fLoc.background, bgColor[0], bgColor[1], bgColor[2]);\n      gl.bindBuffer(gl.ARRAY_BUFFER, quadBuf);\n      gl.enableVertexAttribArray(fLoc.position);\n      gl.vertexAttribPointer(fLoc.position, 2, gl.FLOAT, false, 0, 0);\n      gl.enable(gl.BLEND);\n      gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);\n      gl.drawArrays(gl.TRIANGLES, 0, 6);\n\n      gl.useProgram(particleProg);\n      gl.bindBuffer(gl.ARRAY_BUFFER, particleBuf);\n      gl.bufferSubData(gl.ARRAY_BUFFER, 0, particleData);\n\n      const stride4 = STRIDE * 4;\n      gl.enableVertexAttribArray(pLoc.position);\n      gl.enableVertexAttribArray(pLoc.color);\n      gl.enableVertexAttribArray(pLoc.size);\n      gl.enableVertexAttribArray(pLoc.alpha);\n      gl.vertexAttribPointer(pLoc.position, 2, gl.FLOAT, false, stride4, 0);\n      gl.vertexAttribPointer(pLoc.color, 3, gl.FLOAT, false, stride4, 2 * 4);\n      gl.vertexAttribPointer(pLoc.size, 1, gl.FLOAT, false, stride4, 5 * 4);\n      gl.vertexAttribPointer(pLoc.alpha, 1, gl.FLOAT, false, stride4, 6 * 4);\n\n      gl.uniform2f(pLoc.resolution, width, height);\n      gl.uniform1f(pLoc.dpr, Math.min(window.devicePixelRatio || 1, 2));\n\n      gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);\n      gl.drawArrays(gl.POINTS, 0, particleCount);\n\n      rafId = requestAnimationFrame(render);\n    };\n\n    rafId = requestAnimationFrame(render);\n\n    return () => {\n      cancelAnimationFrame(rafId);\n      ro.disconnect();\n      window.removeEventListener('scroll', onScroll);\n      gl.deleteProgram(particleProg);\n      gl.deleteProgram(fadeProg);\n      gl.deleteBuffer(quadBuf);\n      gl.deleteBuffer(particleBuf);\n    };\n  }, [colors, background, safeIntensity, safeDensity, safeSpeed]);\n\n  return (\n    <canvas\n      ref={canvasRef}\n      className={cn('size-full touch-none', className)}\n      {...props}\n    />\n  );\n}\n",
      "type": "registry:block",
      "target": "components/systaliko-ui/shader.tsx"
    }
  ],
  "type": "registry:block"
}