{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "shaderLines",
  "title": "Effects · Shader Lines",
  "description": "Effects · Shader Lines section.",
  "dependencies": [
    "three"
  ],
  "files": [
    {
      "path": "registry/talizen/effects/shaderLines/shaderLines.tsx",
      "content": "import { ShaderLinesCanvas, type ShaderLinesCanvasProps } from \"./ShaderLinesCanvas\"\n\nexport type ShaderLinesEffectProps = ShaderLinesCanvasProps\n\nexport function ShaderLinesEffect({\n  className = \"\",\n  ...props\n}: ShaderLinesEffectProps) {\n  return (\n    <section className={`relative min-h-screen w-full overflow-hidden bg-black ${className}`}>\n      <div className=\"absolute inset-0\">\n        <ShaderLinesCanvas {...props} />\n      </div>\n    </section>\n  )\n}\n\nexport default ShaderLinesEffect\n",
      "type": "registry:component"
    },
    {
      "path": "registry/talizen/effects/shaderLines/ShaderLinesCanvas.tsx",
      "content": "import { useEffect, useRef } from \"react\"\nimport * as THREE from \"three\"\n\nconst VERTEX_SHADER = `\n  void main() {\n    gl_Position = vec4(position, 1.0);\n  }\n`\n\nconst FRAGMENT_SHADER = `\n  precision highp float;\n\n  uniform vec2 resolution;\n  uniform float time;\n  uniform float bandWidthPx;\n  uniform vec4 backgroundColor;\n  uniform vec4 color;\n  uniform vec4 color1;\n  uniform vec4 color2;\n  uniform vec4 color3;\n  uniform float colorMode;\n  uniform float blendMode;\n\n  float random(in float x) {\n    return fract(sin(x) * 1e4);\n  }\n\n  void main(void) {\n    vec2 uv = (gl_FragCoord.xy * 2.0 - resolution.xy) / min(resolution.x, resolution.y);\n    float bandCenterPx = floor(gl_FragCoord.x / bandWidthPx) * bandWidthPx + bandWidthPx * 0.5;\n    uv.x = (bandCenterPx * 2.0 - resolution.x) / min(resolution.x, resolution.y);\n\n    float t = time * 0.06 + random(uv.x) * 0.4;\n    float lineWidth = 0.0008;\n    vec3 colorIntensity = vec3(0.0);\n\n    for (int j = 0; j < 3; j++) {\n      for (int i = 0; i < 5; i++) {\n        colorIntensity[j] += lineWidth * float(i * i) / abs(fract(t - 0.01 * float(j) + float(i) * 0.01) - length(uv));\n      }\n    }\n\n    vec3 finalColor;\n    float finalAlpha;\n\n    if (colorMode < 0.5) {\n      finalColor = colorIntensity * color.rgb;\n      finalAlpha = color.a;\n    } else {\n      finalColor = vec3(0.0);\n      finalColor += colorIntensity.r * color1.rgb;\n      finalColor += colorIntensity.g * color2.rgb;\n      finalColor += colorIntensity.b * color3.rgb;\n      finalAlpha = (color1.a + color2.a + color3.a) / 3.0;\n    }\n\n    float rayIntensity = max(max(finalColor.r, finalColor.g), finalColor.b);\n\n    if (rayIntensity < 0.01) {\n      finalColor = color.rgb * 0.1;\n      rayIntensity = 0.1;\n    }\n\n    vec3 bgColor = backgroundColor.rgb;\n    float bgAlpha = backgroundColor.a;\n    vec3 blendedColor;\n    float outputAlpha;\n\n    if (blendMode < 0.5) {\n      blendedColor = finalColor.rgb * rayIntensity + bgColor * bgAlpha * (1.0 - rayIntensity);\n      outputAlpha = rayIntensity + bgAlpha * (1.0 - rayIntensity);\n    } else {\n      blendedColor = finalColor.rgb + bgColor * bgAlpha;\n      outputAlpha = 1.0;\n    }\n\n    gl_FragColor = vec4(blendedColor, outputAlpha);\n  }\n`\n\nexport type ShaderLinesColorMode = \"single\" | \"gradient\"\nexport type ShaderLinesBlendMode = \"alpha\" | \"additive\"\nexport type ShaderLinesFlow = \"in-out\" | \"out-in\"\n\nexport type ShaderLinesCanvasProps = {\n  className?: string\n  backgroundColor?: string\n  bandWidth?: number\n  blendMode?: ShaderLinesBlendMode\n  color?: string\n  color1?: string\n  color2?: string\n  color3?: string\n  colorMode?: ShaderLinesColorMode\n  flow?: ShaderLinesFlow\n  speed?: number\n}\n\nfunction clamp(value: number, min: number, max: number) {\n  return Math.min(max, Math.max(min, value))\n}\n\nfunction mapLinear(\n  value: number,\n  inMin: number,\n  inMax: number,\n  outMin: number,\n  outMax: number,\n) {\n  if (inMax === inMin) return outMin\n  return outMin + ((value - inMin) / (inMax - inMin)) * (outMax - outMin)\n}\n\nfunction mapSpeed(value: number) {\n  return mapLinear(clamp(value, 0.1, 1), 0.1, 1, 1, 10)\n}\n\nfunction mapBandWidth(value: number) {\n  return mapLinear(clamp(value, 0.1, 1), 0.1, 1, 2, 60)\n}\n\nfunction parseColor(input: string) {\n  const normalized = input.trim().replace(/^#/, \"\")\n  const expanded =\n    normalized.length === 3 || normalized.length === 4\n      ? normalized\n          .split(\"\")\n          .map((character) => character + character)\n          .join(\"\")\n      : normalized\n  const valid = /^[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$/.test(expanded)\n  const value = valid ? expanded : \"000000\"\n\n  return [\n    Number.parseInt(value.slice(0, 2), 16) / 255,\n    Number.parseInt(value.slice(2, 4), 16) / 255,\n    Number.parseInt(value.slice(4, 6), 16) / 255,\n    value.length === 8 ? Number.parseInt(value.slice(6, 8), 16) / 255 : 1,\n  ] as const\n}\n\nexport function ShaderLinesCanvas({\n  className = \"\",\n  backgroundColor = \"#000000\",\n  bandWidth = 0.2,\n  blendMode = \"additive\",\n  color = \"#ffffff\",\n  color1 = \"#0008ff\",\n  color2 = \"#f55d5d\",\n  color3 = \"#cdff70\",\n  colorMode = \"single\",\n  flow = \"in-out\",\n  speed = 0.55,\n}: ShaderLinesCanvasProps) {\n  const containerRef = useRef<HTMLDivElement>(null)\n\n  useEffect(() => {\n    const container = containerRef.current\n    if (!container) return\n\n    const camera = new THREE.Camera()\n    camera.position.z = 1\n\n    const scene = new THREE.Scene()\n    const geometry = new THREE.PlaneGeometry(2, 2)\n    const uniforms = {\n      time: { value: 1 },\n      resolution: { value: new THREE.Vector2() },\n      bandWidthPx: { value: 1 },\n      backgroundColor: {\n        value: new THREE.Vector4(...parseColor(backgroundColor)),\n      },\n      color: { value: new THREE.Vector4(...parseColor(color)) },\n      color1: { value: new THREE.Vector4(...parseColor(color1)) },\n      color2: { value: new THREE.Vector4(...parseColor(color2)) },\n      color3: { value: new THREE.Vector4(...parseColor(color3)) },\n      colorMode: { value: colorMode === \"single\" ? 0 : 1 },\n      blendMode: { value: blendMode === \"alpha\" ? 0 : 1 },\n    }\n\n    const material = new THREE.ShaderMaterial({\n      uniforms,\n      vertexShader: VERTEX_SHADER,\n      fragmentShader: FRAGMENT_SHADER,\n      transparent: blendMode === \"alpha\",\n    })\n    const mesh = new THREE.Mesh(geometry, material)\n    scene.add(mesh)\n\n    const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: false })\n    renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2))\n    renderer.setClearColor(0x000000, 0)\n    renderer.domElement.className = \"absolute inset-0 block h-full w-full\"\n    container.appendChild(renderer.domElement)\n\n    const resize = () => {\n      const width = container.clientWidth || 1\n      const height = container.clientHeight || 1\n      renderer.setSize(width, height, false)\n      uniforms.resolution.value.set(\n        renderer.domElement.width,\n        renderer.domElement.height,\n      )\n    }\n\n    resize()\n    window.addEventListener(\"resize\", resize)\n    const resizeObserver = new ResizeObserver(resize)\n    resizeObserver.observe(container)\n\n    let animationFrame = 0\n    let lastTime = 0\n    const speedValue = mapSpeed(speed)\n    const flowSign = flow === \"out-in\" ? -1 : 1\n    const bandWidthValue = mapBandWidth(bandWidth)\n\n    const animate = (currentTime: number) => {\n      animationFrame = requestAnimationFrame(animate)\n      const deltaTime = lastTime ? (currentTime - lastTime) / 1000 : 0.016\n      lastTime = currentTime\n      uniforms.time.value += Math.min(deltaTime, 0.1) * speedValue * flowSign\n      uniforms.bandWidthPx.value = bandWidthValue * renderer.getPixelRatio()\n      renderer.render(scene, camera)\n    }\n\n    animate(0)\n\n    return () => {\n      cancelAnimationFrame(animationFrame)\n      resizeObserver.disconnect()\n      window.removeEventListener(\"resize\", resize)\n      scene.remove(mesh)\n      geometry.dispose()\n      material.dispose()\n      renderer.dispose()\n      renderer.domElement.remove()\n    }\n  }, [\n    backgroundColor,\n    bandWidth,\n    blendMode,\n    color,\n    color1,\n    color2,\n    color3,\n    colorMode,\n    flow,\n    speed,\n  ])\n\n  return (\n    <div\n      ref={containerRef}\n      className={`relative block h-full w-full overflow-hidden ${className}`}\n      aria-hidden=\"true\"\n    />\n  )\n}\n",
      "type": "registry:component"
    }
  ],
  "categories": [
    "effects"
  ],
  "type": "registry:block"
}