{
  "name": "liquid-metal-button",
  "dependencies": [
    "lucide-react",
    "@paper-design/shaders"
  ],
  "files": [
    {
      "path": "ui/liquid-metal-button.tsx",
      "content": "\"use client\";\n\nimport { Sparkles } from \"lucide-react\";\nimport type React from \"react\";\nimport { useEffect, useMemo, useRef, useState } from \"react\";\n\ninterface LiquidMetalButtonProps {\n  label?: string;\n  onClick?: () => void;\n  viewMode?: \"text\" | \"icon\";\n}\n\nexport function LiquidMetalButton({\n  label = \"Get Started\",\n  onClick,\n  viewMode = \"text\",\n}: LiquidMetalButtonProps) {\n  const [isHovered, setIsHovered] = useState(false);\n  const [isPressed, setIsPressed] = useState(false);\n  const [ripples, setRipples] = useState<\n    Array<{ x: number; y: number; id: number }>\n  >([]);\n  const shaderRef = useRef<HTMLDivElement>(null);\n  // biome-ignore lint/suspicious/noExplicitAny: External library without types\n  const shaderMount = useRef<any>(null);\n  const buttonRef = useRef<HTMLButtonElement>(null);\n  const rippleId = useRef(0);\n\n  const dimensions = useMemo(() => {\n    if (viewMode === \"icon\") {\n      return {\n        width: 46,\n        height: 46,\n        innerWidth: 42,\n        innerHeight: 42,\n        shaderWidth: 46,\n        shaderHeight: 46,\n      };\n    } else {\n      return {\n        width: 142,\n        height: 46,\n        innerWidth: 138,\n        innerHeight: 42,\n        shaderWidth: 142,\n        shaderHeight: 46,\n      };\n    }\n  }, [viewMode]);\n\n  useEffect(() => {\n    const styleId = \"shader-canvas-style-exploded\";\n    if (!document.getElementById(styleId)) {\n      const style = document.createElement(\"style\");\n      style.id = styleId;\n      style.textContent = `\n        .shader-container-exploded canvas {\n          width: 100% !important;\n          height: 100% !important;\n          display: block !important;\n          position: absolute !important;\n          top: 0 !important;\n          left: 0 !important;\n          border-radius: 100px !important;\n        }\n        @keyframes ripple-animation {\n          0% {\n            transform: translate(-50%, -50%) scale(0);\n            opacity: 0.6;\n          }\n          100% {\n            transform: translate(-50%, -50%) scale(4);\n            opacity: 0;\n          }\n        }\n      `;\n      document.head.appendChild(style);\n    }\n\n    const loadShader = async () => {\n      try {\n        const { liquidMetalFragmentShader, ShaderMount } = await import(\n          \"@paper-design/shaders\"\n        );\n\n        if (shaderRef.current) {\n          if (shaderMount.current?.destroy) {\n            shaderMount.current.destroy();\n          }\n\n          shaderMount.current = new ShaderMount(\n            shaderRef.current,\n            liquidMetalFragmentShader,\n            {\n              u_repetition: 4,\n              u_softness: 0.5,\n              u_shiftRed: 0.3,\n              u_shiftBlue: 0.3,\n              u_distortion: 0,\n              u_contour: 0,\n              u_angle: 45,\n              u_scale: 8,\n              u_shape: 1,\n              u_offsetX: 0.1,\n              u_offsetY: -0.1,\n            },\n            undefined,\n            0.6,\n          );\n        }\n      } catch (error) {\n        console.error(\"[v0] Failed to load shader:\", error);\n      }\n    };\n\n    loadShader();\n\n    return () => {\n      if (shaderMount.current?.destroy) {\n        shaderMount.current.destroy();\n        shaderMount.current = null;\n      }\n    };\n  }, []);\n\n  const handleMouseEnter = () => {\n    setIsHovered(true);\n    shaderMount.current?.setSpeed?.(1);\n  };\n\n  const handleMouseLeave = () => {\n    setIsHovered(false);\n    setIsPressed(false);\n    shaderMount.current?.setSpeed?.(0.6);\n  };\n\n  const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {\n    if (shaderMount.current?.setSpeed) {\n      shaderMount.current.setSpeed(2.4);\n      setTimeout(() => {\n        if (isHovered) {\n          shaderMount.current?.setSpeed?.(1);\n        } else {\n          shaderMount.current?.setSpeed?.(0.6);\n        }\n      }, 300);\n    }\n\n    if (buttonRef.current) {\n      const rect = buttonRef.current.getBoundingClientRect();\n      const x = e.clientX - rect.left;\n      const y = e.clientY - rect.top;\n      const ripple = { x, y, id: rippleId.current++ };\n\n      setRipples((prev) => [...prev, ripple]);\n      setTimeout(() => {\n        setRipples((prev) => prev.filter((r) => r.id !== ripple.id));\n      }, 600);\n    }\n\n    onClick?.();\n  };\n\n  return (\n    <div className=\"relative inline-block\">\n      <div\n        style={{\n          perspective: \"1000px\",\n          perspectiveOrigin: \"50% 50%\",\n        }}\n      >\n        <div\n          style={{\n            position: \"relative\",\n            width: `${dimensions.width}px`,\n            height: `${dimensions.height}px`,\n            transformStyle: \"preserve-3d\",\n            transition:\n              \"all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1), width 0.4s ease, height 0.4s ease\",\n            transform: \"none\",\n          }}\n        >\n          <div\n            style={{\n              position: \"absolute\",\n              top: 0,\n              left: 0,\n              width: `${dimensions.width}px`,\n              height: `${dimensions.height}px`,\n              display: \"flex\",\n              alignItems: \"center\",\n              justifyContent: \"center\",\n              gap: \"6px\",\n              transformStyle: \"preserve-3d\",\n              transition:\n                \"all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1), width 0.4s ease, height 0.4s ease, gap 0.4s ease\",\n              transform: \"translateZ(20px)\",\n              zIndex: 30,\n              pointerEvents: \"none\",\n            }}\n          >\n            {viewMode === \"icon\" && (\n              <Sparkles\n                size={16}\n                style={{\n                  color: \"#666666\",\n                  filter: \"drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.5))\",\n                  transition: \"all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1)\",\n                  transform: \"scale(1)\",\n                }}\n              />\n            )}\n            {viewMode === \"text\" && (\n              <span\n                style={{\n                  fontSize: \"14px\",\n                  color: \"#666666\",\n                  fontWeight: 400,\n                  textShadow: \"0px 1px 2px rgba(0, 0, 0, 0.5)\",\n                  transition: \"all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1)\",\n                  transform: \"scale(1)\",\n                  whiteSpace: \"nowrap\",\n                }}\n              >\n                {label}\n              </span>\n            )}\n          </div>\n\n          <div\n            style={{\n              position: \"absolute\",\n              top: 0,\n              left: 0,\n              width: `${dimensions.width}px`,\n              height: `${dimensions.height}px`,\n              transformStyle: \"preserve-3d\",\n              transition:\n                \"all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1), width 0.4s ease, height 0.4s ease\",\n              transform: `translateZ(10px) ${isPressed ? \"translateY(1px) scale(0.98)\" : \"translateY(0) scale(1)\"}`,\n              zIndex: 20,\n            }}\n          >\n            <div\n              style={{\n                width: `${dimensions.innerWidth}px`,\n                height: `${dimensions.innerHeight}px`,\n                margin: \"2px\",\n                borderRadius: \"100px\",\n                background: \"linear-gradient(180deg, #202020 0%, #000000 100%)\",\n                boxShadow: isPressed\n                  ? \"inset 0px 2px 4px rgba(0, 0, 0, 0.4), inset 0px 1px 2px rgba(0, 0, 0, 0.3)\"\n                  : \"none\",\n                transition:\n                  \"all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1), width 0.4s ease, height 0.4s ease, box-shadow 0.15s cubic-bezier(0.4, 0, 0.2, 1)\",\n              }}\n            />\n          </div>\n\n          <div\n            style={{\n              position: \"absolute\",\n              top: 0,\n              left: 0,\n              width: `${dimensions.width}px`,\n              height: `${dimensions.height}px`,\n              transformStyle: \"preserve-3d\",\n              transition:\n                \"all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1), width 0.4s ease, height 0.4s ease\",\n              transform: `translateZ(0px) ${isPressed ? \"translateY(1px) scale(0.98)\" : \"translateY(0) scale(1)\"}`,\n              zIndex: 10,\n            }}\n          >\n            <div\n              style={{\n                height: `${dimensions.height}px`,\n                width: `${dimensions.width}px`,\n                borderRadius: \"100px\",\n                boxShadow: isPressed\n                  ? \"0px 0px 0px 1px rgba(0, 0, 0, 0.5), 0px 1px 2px 0px rgba(0, 0, 0, 0.3)\"\n                  : isHovered\n                    ? \"0px 0px 0px 1px rgba(0, 0, 0, 0.4), 0px 12px 6px 0px rgba(0, 0, 0, 0.05), 0px 8px 5px 0px rgba(0, 0, 0, 0.1), 0px 4px 4px 0px rgba(0, 0, 0, 0.15), 0px 1px 2px 0px rgba(0, 0, 0, 0.2)\"\n                    : \"0px 0px 0px 1px rgba(0, 0, 0, 0.3), 0px 36px 14px 0px rgba(0, 0, 0, 0.02), 0px 20px 12px 0px rgba(0, 0, 0, 0.08), 0px 9px 9px 0px rgba(0, 0, 0, 0.12), 0px 2px 5px 0px rgba(0, 0, 0, 0.15)\",\n                transition:\n                  \"all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1), width 0.4s ease, height 0.4s ease, box-shadow 0.15s cubic-bezier(0.4, 0, 0.2, 1)\",\n                background: \"rgb(0 0 0 / 0)\",\n              }}\n            >\n              <div\n                ref={shaderRef}\n                className=\"shader-container-exploded\"\n                style={{\n                  borderRadius: \"100px\",\n                  overflow: \"hidden\",\n                  position: \"relative\",\n                  width: `${dimensions.shaderWidth}px`,\n                  maxWidth: `${dimensions.shaderWidth}px`,\n                  height: `${dimensions.shaderHeight}px`,\n                  transition: \"width 0.4s ease, height 0.4s ease\",\n                }}\n              />\n            </div>\n          </div>\n\n          <button\n            ref={buttonRef}\n            onClick={handleClick}\n            onMouseEnter={handleMouseEnter}\n            onMouseLeave={handleMouseLeave}\n            onMouseDown={() => setIsPressed(true)}\n            onMouseUp={() => setIsPressed(false)}\n            style={{\n              position: \"absolute\",\n              top: 0,\n              left: 0,\n              width: `${dimensions.width}px`,\n              height: `${dimensions.height}px`,\n              background: \"transparent\",\n              border: \"none\",\n              cursor: \"pointer\",\n              outline: \"none\",\n              zIndex: 40,\n              transformStyle: \"preserve-3d\",\n              transform: \"translateZ(25px)\",\n              transition:\n                \"all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1), width 0.4s ease, height 0.4s ease\",\n              overflow: \"hidden\",\n              borderRadius: \"100px\",\n            }}\n            aria-label={label}\n          >\n            {ripples.map((ripple) => (\n              <span\n                key={ripple.id}\n                style={{\n                  position: \"absolute\",\n                  left: `${ripple.x}px`,\n                  top: `${ripple.y}px`,\n                  width: \"20px\",\n                  height: \"20px\",\n                  borderRadius: \"50%\",\n                  background:\n                    \"radial-gradient(circle, rgba(255, 255, 255, 0.4) 0%, rgba(255, 255, 255, 0) 70%)\",\n                  pointerEvents: \"none\",\n                  animation: \"ripple-animation 0.6s ease-out\",\n                }}\n              />\n            ))}\n          </button>\n        </div>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:ui",
      "target": ""
    }
  ],
  "type": "registry:ui"
}