{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"dependencies": ["framer-motion"],
	"description": "Typewriter effect that cycles through phrases with variable typing speed and blinking cursor.",
	"files": [
		{
			"content": "\"use client\";\n\nimport { motion } from \"framer-motion\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\ninterface TypewriterProps {\n  phrases: readonly string[];\n  className?: string;\n  cursorClassName?: string;\n  trigger?: \"mount\" | \"inView\";\n  typeSpeed?: number;\n  deleteSpeed?: number;\n  pauseDuration?: number;\n  cursorBlinkDuration?: number;\n  cursorBlinkEasing?: \"easeInOut\" | \"easeIn\" | \"easeOut\" | \"linear\" | [number, number, number, number];\n  inViewThreshold?: number;\n}\n\nexport function Typewriter({\n  phrases,\n  className,\n  cursorClassName,\n  trigger = \"inView\",\n  typeSpeed = 100,\n  deleteSpeed = 50,\n  pauseDuration = 2000,\n  cursorBlinkDuration = 0.5,\n  cursorBlinkEasing = \"easeInOut\",\n  inViewThreshold = 0.3,\n}: TypewriterProps) {\n  const [phraseIndex, setPhraseIndex] = useState(0);\n  const [display, setDisplay] = useState(\"\");\n  const [deleting, setDeleting] = useState(false);\n  const [inView, setInView] = useState(trigger === \"mount\");\n  const ref = useRef<HTMLSpanElement>(null);\n\n  useEffect(() => {\n    if (trigger !== \"inView\") {\n      return;\n    }\n    const el = ref.current;\n    if (!el) {\n      return;\n    }\n    const obs = new IntersectionObserver(([e]) => setInView(e?.isIntersecting ?? false), {\n      threshold: inViewThreshold,\n    });\n    obs.observe(el);\n    return () => obs.disconnect();\n  }, [trigger, inViewThreshold]);\n\n  const shouldRun = trigger === \"mount\" ? true : inView;\n\n  useEffect(() => {\n    if (!shouldRun) {\n      return;\n    }\n    const phrase = phrases[phraseIndex] ?? \"\";\n    if (deleting) {\n      if (display.length > 0) {\n        const t = setTimeout(() => setDisplay(display.slice(0, -1)), deleteSpeed);\n        return () => clearTimeout(t);\n      }\n      setDeleting(false);\n      setPhraseIndex((phraseIndex + 1) % phrases.length);\n      return;\n    }\n    if (display.length < phrase.length) {\n      const t = setTimeout(() => setDisplay(phrase.slice(0, display.length + 1)), typeSpeed + Math.random() * 40);\n      return () => clearTimeout(t);\n    }\n    const t = setTimeout(() => setDeleting(true), pauseDuration);\n    return () => clearTimeout(t);\n  }, [shouldRun, display, deleting, phraseIndex, phrases, typeSpeed, deleteSpeed, pauseDuration]);\n\n  const cursorTransition = {\n    duration: cursorBlinkDuration,\n    ease: cursorBlinkEasing,\n    repeat: Number.POSITIVE_INFINITY,\n  };\n\n  return (\n    <span ref={ref} className={cn(className)}>\n      {display}\n      <motion.span\n        animate={{ opacity: [1, 0] }}\n        transition={cursorTransition}\n        className={cn(\"inline-block min-h-[1em] w-0.5 shrink-0 bg-current align-middle\", cursorClassName)}\n        aria-hidden={true}\n      />\n    </span>\n  );\n}\n",
			"path": "registry/new-york/typewriter/typewriter.tsx",
			"type": "registry:component"
		}
	],
	"name": "typewriter",
	"title": "Typewriter",
	"type": "registry:component"
}
