{
	"$schema": "https://shadcn-svelte.com/schema/registry-item.json",
	"name": "hyper-text",
	"title": "Hyper Text",
	"type": "registry:block",
	"description": "An animated text component that creates a glitch effect by cycling through random characters before revealing the final text.",
	"dependencies": [
		"motion-sv"
	],
	"files": [
		{
			"content": "<script lang=\"ts\">\n\timport { onMount } from \"svelte\";\n\timport { motion, AnimatePresence } from \"motion-sv\";\n\timport { cn } from \"$UTILS$\";\n\n\ttype CharacterSet = string[] | readonly string[];\n\n\ttype ElementType = \"div\" | \"span\" | \"p\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\";\n\n\tinterface HyperTextProps {\n\t\ttext: string;\n\t\tclass?: string;\n\t\tduration?: number;\n\t\tdelay?: number;\n\t\tas?: ElementType;\n\t\tstartOnView?: boolean;\n\t\tanimateOnHover?: boolean;\n\t\tcharacterSet?: CharacterSet;\n\t}\n\n\tlet {\n\t\ttext,\n\t\tclass: className,\n\t\tduration = 800,\n\t\tdelay = 0,\n\t\tas = \"div\",\n\t\tstartOnView = false,\n\t\tanimateOnHover = true,\n\t\tcharacterSet = Object.freeze(\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\".split(\"\")) as readonly string[],\n\t}: HyperTextProps = $props();\n\n\tlet MotionComponent = $derived(motion[as]);\n\n\tlet displayText = $state<string[]>([]);\n\tlet isAnimating = $state(false);\n\tlet iterationCount = $state(0);\n\tlet elementRef: any = $state(null);\n\n\t// Initialize displayText when text changes\n\t$effect(() => {\n\t\tdisplayText = text.split(\"\");\n\t});\n\n\tconst getRandomInt = (max: number): number => Math.floor(Math.random() * max);\n\n\tconst handleAnimationTrigger = () => {\n\t\tif (animateOnHover && !isAnimating) {\n\t\t\titerationCount = 0;\n\t\t\tisAnimating = true;\n\t\t}\n\t};\n\n\t// Handle animation start based on view or delay\n\tonMount(() => {\n\t\tif (!startOnView) {\n\t\t\tconst startTimeout = setTimeout(() => {\n\t\t\t\tisAnimating = true;\n\t\t\t}, delay);\n\t\t\treturn () => clearTimeout(startTimeout);\n\t\t}\n\n\t\tconst observer = new IntersectionObserver(\n\t\t\t([entry]) => {\n\t\t\t\tif (entry.isIntersecting) {\n\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\tisAnimating = true;\n\t\t\t\t\t}, delay);\n\t\t\t\t\tobserver.disconnect();\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ threshold: 0.1, rootMargin: \"-30% 0px -30% 0px\" }\n\t\t);\n\n\t\tif (elementRef) {\n\t\t\tobserver.observe(elementRef);\n\t\t}\n\n\t\treturn () => observer.disconnect();\n\t});\n\n\t// Handle scramble animation\n\t$effect(() => {\n\t\tif (!isAnimating) return;\n\n\t\tconst maxIterations = text.length;\n\t\tconst startTime = performance.now();\n\t\tlet animationFrameId: number;\n\n\t\tconst animate = (currentTime: number) => {\n\t\t\tconst elapsed = currentTime - startTime;\n\t\t\tconst progress = Math.min(elapsed / duration, 1);\n\n\t\t\titerationCount = progress * maxIterations;\n\n\t\t\tdisplayText = displayText.map((letter, index) =>\n\t\t\t\tletter === \" \"\n\t\t\t\t\t? letter\n\t\t\t\t\t: index <= iterationCount\n\t\t\t\t\t\t? text[index]\n\t\t\t\t\t\t: characterSet[getRandomInt(characterSet.length)]\n\t\t\t);\n\n\t\t\tif (progress < 1) {\n\t\t\t\tanimationFrameId = requestAnimationFrame(animate);\n\t\t\t} else {\n\t\t\t\tisAnimating = false;\n\t\t\t}\n\t\t};\n\n\t\tanimationFrameId = requestAnimationFrame(animate);\n\n\t\treturn () => cancelAnimationFrame(animationFrameId);\n\t});\n</script>\n\n<MotionComponent\n\tbind:this={elementRef}\n\tclass={cn(\"overflow-hidden py-2 text-4xl font-medium\", className)}\n\tonmouseenter={handleAnimationTrigger}\n>\n\t<AnimatePresence>\n\t\t{#each displayText as letter, index (index)}\n\t\t\t<motion.span class={cn(\"font-mono\", letter === \" \" ? \"w-3\" : \"\")}>\n\t\t\t\t{letter.toUpperCase()}\n\t\t\t</motion.span>\n\t\t{/each}\n\t</AnimatePresence>\n</MotionComponent>\n",
			"type": "registry:component",
			"target": "magic/hyper-text/hyper-text.svelte"
		},
		{
			"content": "import HyperText from \"./hyper-text.svelte\";\nexport { HyperText };\n",
			"type": "registry:file",
			"target": "magic/hyper-text/index.ts"
		}
	]
}