{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "scramble-text",
  "title": "Scramble Text",
  "description": "A text animation component that reveals characters with a scramble/glitch effect, with imperative controls for manual triggering.",
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/components/unlumen/scramble-text/index.tsx",
      "content": "\"use client\";\n\nimport {\n  forwardRef,\n  useCallback,\n  useEffect,\n  useImperativeHandle,\n  useState,\n} from \"react\";\n\ninterface ScrambleTextProps {\n  text: string;\n  /** @default 50 */\n  scrambleSpeed?: number;\n  /** scrambled chars shown ahead of the revealed text — @default 2 */\n  scrambledLetterCount?: number;\n  characters?: string;\n  className?: string;\n  /** class for the scrambled (unrevealed) portion */\n  scrambledClassName?: string;\n  /** @default true */\n  autoStart?: boolean;\n  delay?: number;\n  onStart?: () => void;\n  onComplete?: () => void;\n}\n\nexport interface ScrambleTextHandle {\n  start: () => void;\n  reset: () => void;\n}\n\nconst ScrambleText = forwardRef<ScrambleTextHandle, ScrambleTextProps>(\n  (\n    {\n      text,\n      scrambleSpeed = 50,\n      scrambledLetterCount = 2,\n      characters = \"abcdefghijklmnopqrstuvwxyz!@#$%^&*()_+\",\n      className = \"\",\n      scrambledClassName = \"\",\n      autoStart = true,\n      delay = 0,\n      onStart,\n      onComplete,\n    },\n    ref,\n  ) => {\n    const [displayText, setDisplayText] = useState(\"\");\n    const [isAnimating, setIsAnimating] = useState(false);\n    const [visibleLetterCount, setVisibleLetterCount] = useState(0);\n    const [scrambleOffset, setScrambleOffset] = useState(0);\n\n    const startAnimation = useCallback(() => {\n      setIsAnimating(true);\n      setVisibleLetterCount(0);\n      setScrambleOffset(0);\n      onStart?.();\n    }, [onStart]);\n\n    const reset = useCallback(() => {\n      setIsAnimating(false);\n      setVisibleLetterCount(0);\n      setScrambleOffset(0);\n      setDisplayText(\"\");\n    }, []);\n\n    useImperativeHandle(ref, () => ({\n      start: startAnimation,\n      reset,\n    }));\n\n    useEffect(() => {\n      if (!autoStart) return;\n      if (delay > 0) {\n        const t = setTimeout(startAnimation, delay);\n        return () => clearTimeout(t);\n      }\n      startAnimation();\n    }, [autoStart, delay, startAnimation]);\n\n    useEffect(() => {\n      let interval: NodeJS.Timeout;\n\n      if (isAnimating) {\n        interval = setInterval(() => {\n          if (visibleLetterCount < text.length) {\n            setVisibleLetterCount((prev) => prev + 1);\n          } else if (scrambleOffset < scrambledLetterCount) {\n            setScrambleOffset((prev) => prev + 1);\n          } else {\n            clearInterval(interval);\n            setIsAnimating(false);\n            onComplete?.();\n          }\n\n          const remainingSpace = Math.max(0, text.length - visibleLetterCount);\n          const currentScrambleCount = Math.min(\n            remainingSpace,\n            scrambledLetterCount,\n          );\n\n          const scrambledPart = Array(currentScrambleCount)\n            .fill(0)\n            .map(\n              () => characters[Math.floor(Math.random() * characters.length)],\n            )\n            .join(\"\");\n\n          setDisplayText(text.slice(0, visibleLetterCount) + scrambledPart);\n        }, scrambleSpeed);\n      }\n\n      return () => {\n        if (interval) clearInterval(interval);\n      };\n    }, [\n      isAnimating,\n      text,\n      visibleLetterCount,\n      scrambleOffset,\n      scrambledLetterCount,\n      characters,\n      scrambleSpeed,\n      onComplete,\n    ]);\n\n    const renderText = () => {\n      const revealed = displayText.slice(0, visibleLetterCount);\n      const scrambled = displayText.slice(visibleLetterCount);\n\n      return (\n        <>\n          <span className={className}>{revealed}</span>\n          <span className={scrambledClassName}>{scrambled}</span>\n        </>\n      );\n    };\n\n    return (\n      <>\n        <span className=\"sr-only\">{text}</span>\n        <span className=\"inline-block whitespace-pre-wrap\" aria-hidden=\"true\">\n          {renderText()}\n        </span>\n      </>\n    );\n  },\n);\n\nScrambleText.displayName = \"ScrambleText\";\n\nexport { ScrambleText };\nexport default ScrambleText;\n",
      "type": "registry:ui",
      "target": "components/unlumen-ui/scramble-text.tsx"
    }
  ],
  "type": "registry:ui"
}