{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "scramble-text",
  "title": "Scramble Text",
  "description": "Text that scrambles through random characters before resolving.",
  "dependencies": [
    "clsx",
    "tailwind-merge"
  ],
  "files": [
    {
      "path": "registry/wise-ui/components/scramble-text.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { cn } from \"@/registry/wise-ui/lib/utils\"\n\ninterface ScrambleTextProps {\n  text: string\n  trigger?: \"mount\" | \"hover\"\n  speed?: number\n  characterSet?: string\n  sequential?: boolean\n  className?: string\n}\n\nconst DEFAULT_CHARS = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%&*\"\n\nfunction ScrambleText({\n  text,\n  trigger = \"mount\",\n  speed = 50,\n  characterSet = DEFAULT_CHARS,\n  sequential = true,\n  className,\n}: ScrambleTextProps) {\n  const [display, setDisplay] = React.useState(trigger === \"mount\" ? \"\" : text)\n  const rafRef = React.useRef<number>(0)\n  const startTimeRef = React.useRef<number>(0)\n\n  const scramble = React.useCallback(() => {\n    cancelAnimationFrame(rafRef.current)\n    startTimeRef.current = 0\n\n    const chars = text.split(\"\")\n    const totalDuration = sequential\n      ? chars.length * speed + speed * 3\n      : speed * 8\n\n    const step = (timestamp: number) => {\n      if (!startTimeRef.current) startTimeRef.current = timestamp\n      const elapsed = timestamp - startTimeRef.current\n\n      let result = \"\"\n      let allResolved = true\n\n      for (let i = 0; i < chars.length; i++) {\n        if (chars[i] === \" \") {\n          result += \" \"\n          continue\n        }\n\n        const resolveAt = sequential ? i * speed + speed * 2 : speed * 4\n        if (elapsed >= resolveAt) {\n          result += chars[i]\n        } else {\n          allResolved = false\n          result += characterSet[Math.floor(Math.random() * characterSet.length)]\n        }\n      }\n\n      setDisplay(result)\n\n      if (!allResolved && elapsed < totalDuration) {\n        rafRef.current = requestAnimationFrame(step)\n      } else {\n        setDisplay(text)\n      }\n    }\n\n    rafRef.current = requestAnimationFrame(step)\n  }, [text, speed, characterSet, sequential])\n\n  React.useEffect(() => {\n    if (trigger === \"mount\") {\n      scramble()\n    }\n    return () => cancelAnimationFrame(rafRef.current)\n  }, [trigger, scramble])\n\n  const handleMouseEnter = () => {\n    if (trigger === \"hover\") {\n      scramble()\n    }\n  }\n\n  return (\n    <span\n      className={cn(\"inline-block whitespace-pre\", className)}\n      onMouseEnter={handleMouseEnter}\n    >\n      {display || \"\\u00A0\"}\n    </span>\n  )\n}\nScrambleText.displayName = \"ScrambleText\"\n\nexport { ScrambleText }\nexport type { ScrambleTextProps }\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component"
}