{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "text-outline-glow",
  "title": "Text Outline Glow",
  "description": "A very smooth and interactive trail of images that follows your mouse cursor with discrete step interpolation.",
  "dependencies": [
    "gsap",
    "@gsap/react"
  ],
  "files": [
    {
      "path": "registry/radix-nova/text-outline-glow/text-outline-glow.tsx",
      "content": "import React, { useRef, useState, useEffect } from \"react\"\r\nimport { useGSAP } from \"@gsap/react\"\r\nimport gsap from \"gsap\"\r\n\r\nexport const TextHoverEffect = ({\r\n  text,\r\n  duration,\r\n  fontSize = 56,\r\n  colors = [\"#eab308\", \"#ef4444\", \"#3b82f6\", \"#06b6d4\", \"#8b5cf6\"],\r\n  textFill,\r\n}: {\r\n  text: string\r\n  duration?: number\r\n  fontSize?: number\r\n  colors?: string[]\r\n  textFill?: string\r\n}) => {\r\n  const svgRef = useRef<SVGSVGElement>(null)\r\n  const maskGradientRef = useRef(null)\r\n  const animatedTextRef = useRef(null)\r\n  const [cursor, setCursor] = useState({ x: 0, y: 0 })\r\n  const [hovered, setHovered] = useState(false)\r\n  const [maskPosition, setMaskPosition] = useState({ cx: \"50%\", cy: \"50%\" })\r\n\r\n  useGSAP(\r\n    () => {\r\n      gsap.fromTo(\r\n        animatedTextRef.current,\r\n        { strokeDashoffset: 1000, strokeDasharray: 1000 },\r\n        {\r\n          strokeDashoffset: 0,\r\n          strokeDasharray: 1000,\r\n          duration: 4,\r\n          ease: \"power2.inOut\",\r\n        }\r\n      )\r\n    },\r\n    { scope: svgRef }\r\n  )\r\n\r\n  const updateCursorPosition = (x: number, y: number) => {\r\n    if (svgRef.current && x !== null && y !== null) {\r\n      const svgRect = svgRef.current.getBoundingClientRect()\r\n      const cxPercentage = ((x - svgRect.left) / svgRect.width) * 100\r\n      const cyPercentage = ((y - svgRect.top) / svgRect.height) * 100\r\n\r\n      const newPosition = {\r\n        cx: `${cxPercentage}%`,\r\n        cy: `${cyPercentage}%`,\r\n      }\r\n\r\n      setMaskPosition(newPosition)\r\n\r\n      gsap.to(maskGradientRef.current, {\r\n        attr: newPosition,\r\n        duration: duration ?? 0,\r\n        ease: \"power2.out\",\r\n      })\r\n    }\r\n  }\r\n\r\n  useEffect(() => {\r\n    updateCursorPosition(cursor.x, cursor.y)\r\n  }, [cursor, duration])\r\n\r\n  const handleMouseEnter = () => setHovered(true)\r\n  const handleMouseLeave = () => setHovered(false)\r\n  const handleMouseMove = (e: React.MouseEvent) => {\r\n    setCursor({ x: e.clientX, y: e.clientY })\r\n  }\r\n\r\n  const handleTouchStart = (e: React.TouchEvent) => {\r\n    e.preventDefault() \r\n    setHovered(true)\r\n    if (e.touches.length > 0) {\r\n      const touch = e.touches[0]\r\n      if (touch) {\r\n        setCursor({ x: touch.clientX, y: touch.clientY })\r\n      }\r\n    }\r\n  }\r\n\r\n  const handleTouchMove = (e: React.TouchEvent) => {\r\n    e.preventDefault()\r\n    if (e.touches.length > 0) {\r\n      const touch = e.touches[0]\r\n      if (touch) {\r\n        setCursor({ x: touch.clientX, y: touch.clientY })\r\n      }\r\n    }\r\n  }\r\n\r\n  const handleTouchEnd = (e: React.TouchEvent) => {\r\n    e.preventDefault()\r\n    setHovered(false)\r\n  }\r\n\r\n  return (\r\n    <svg\r\n      ref={svgRef}\r\n      width=\"100%\"\r\n      height=\"100%\"\r\n      viewBox=\"0 0 300 100\"\r\n      xmlns=\"http://www.w3.org/2000/svg\"\r\n      onMouseEnter={handleMouseEnter}\r\n      onMouseLeave={handleMouseLeave}\r\n      onMouseMove={handleMouseMove}\r\n      onTouchStart={handleTouchStart}\r\n      onTouchMove={handleTouchMove}\r\n      onTouchEnd={handleTouchEnd}\r\n      onTouchCancel={handleTouchEnd}\r\n      className=\"select-none\"\r\n    >\r\n      <defs>\r\n        <linearGradient\r\n          id=\"textGradient\"\r\n          gradientUnits=\"userSpaceOnUse\"\r\n          cx=\"50%\"\r\n          cy=\"50%\"\r\n          r=\"20%\"\r\n        >\r\n          {hovered && (\r\n            <>\r\n              {colors.map((color, idx) => (\r\n                <stop\r\n                  key={idx}\r\n                  offset={\r\n                    colors.length === 1\r\n                      ? \"0%\"\r\n                      : `${(idx / (colors.length - 1)) * 100}%`\r\n                  }\r\n                  stopColor={color}\r\n                />\r\n              ))}\r\n            </>\r\n          )}\r\n        </linearGradient>\r\n        <radialGradient\r\n          id=\"revealMask\"\r\n          ref={maskGradientRef}\r\n          gradientUnits=\"userSpaceOnUse\"\r\n          r=\"25%\"\r\n          cx={maskPosition.cx}\r\n          cy={maskPosition.cy}\r\n        >\r\n          <stop offset=\"0%\" stopColor=\"white\" />\r\n          <stop offset=\"100%\" stopColor=\"black\" />\r\n        </radialGradient>\r\n        <mask id=\"textMask\">\r\n          <rect\r\n            x=\"0\"\r\n            y=\"0\"\r\n            width=\"100%\"\r\n            height=\"100%\"\r\n            fill=\"url(#revealMask)\"\r\n          />\r\n        </mask>\r\n      </defs>\r\n\r\n      {[0, 1, 2].map((_, idx) => (\r\n        <text\r\n          key={idx}\r\n          ref={idx === 1 ? animatedTextRef : undefined}\r\n          x=\"50%\"\r\n          y=\"50%\"\r\n          textAnchor=\"middle\"\r\n          dominantBaseline=\"middle\"\r\n          strokeWidth=\"0.8\"\r\n          className={`${textFill ? '' : 'fill-muted'} font-[helvetica] font-bold ${\r\n            idx === 0\r\n              ? \"stroke-neutral-300 dark:stroke-neutral-800\"\r\n              : idx === 1\r\n                ? \"stroke-neutral-300 dark:stroke-neutral-800\"\r\n                : \"\"\r\n          }`}\r\n          stroke={idx === 2 ? \"url(#textGradient)\" : undefined}\r\n          mask={idx === 2 ? \"url(#textMask)\" : undefined}\r\n          style={{\r\n            ...(textFill ? { fill: textFill } : {}),\r\n            fontSize,\r\n            opacity: idx === 0 && !hovered ? 0 : idx === 0 ? 0.7 : 1,\r\n          }}\r\n        >\r\n          {text}\r\n        </text>\r\n      ))}\r\n    </svg>\r\n  )\r\n}\r\n",
      "type": "registry:component",
      "target": "@components/grootstudio/text-outline-glow.tsx"
    }
  ],
  "type": "registry:block"
}