{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "hyper-text",
  "title": "Hyper Text",
  "description": "Scrambles through letters and resolves into the real text.",
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "src/components/velora/hyper-text.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\";\n\ninterface HyperTextProps {\n  children: string;\n  /** Milliseconds between scramble frames */\n  frame?: number;\n  /** Scramble on hover instead of on mount */\n  onHover?: boolean;\n  className?: string;\n}\n\n/**\n * Scrambles through random letters and resolves into the real text, one\n * character at a time. Renders the final text on the server.\n */\nexport function HyperText({\n  children,\n  frame = 40,\n  onHover = false,\n  className,\n}: HyperTextProps) {\n  // The scrambled frame, tagged with the text it was built from; null (or a\n  // stale tag after `children` changes) means \"show the real text\".\n  const [scrambled, setScrambled] = useState<{ of: string; text: string } | null>(\n    null\n  );\n  const timer = useRef<number | undefined>(undefined);\n\n  const scramble = () => {\n    if (window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) return;\n    window.clearInterval(timer.current);\n    let tick = 0;\n    timer.current = window.setInterval(() => {\n      tick += 1;\n      setScrambled({\n        of: children,\n        text: children\n          .split(\"\")\n          .map((char, i) => {\n            if (char === \" \") return char;\n            // Each character locks in once the wave has passed it.\n            if (i < tick / 2) return children[i];\n            return ALPHABET[Math.floor(Math.random() * ALPHABET.length)];\n          })\n          .join(\"\"),\n      });\n      if (tick / 2 >= children.length) {\n        window.clearInterval(timer.current);\n        setScrambled(null);\n      }\n    }, frame);\n  };\n\n  useEffect(() => {\n    if (!onHover) scramble();\n    return () => window.clearInterval(timer.current);\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [children, onHover]);\n\n  return (\n    <span\n      data-slot=\"hyper-text\"\n      onMouseEnter={onHover ? scramble : undefined}\n      className={cn(\"font-mono tabular-nums\", className)}\n    >\n      <span className=\"sr-only\">{children}</span>\n      <span aria-hidden>\n        {scrambled?.of === children ? scrambled.text : children}\n      </span>\n    </span>\n  );\n}\n",
      "type": "registry:ui",
      "target": "components/velora/hyper-text.tsx"
    }
  ],
  "type": "registry:ui"
}