{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "native-typewriter-shadcnui",
  "type": "registry:component",
  "title": "Native Typewriter",
  "description": "Typewriter effect with speed control, looping, and blinking cursor.",
  "dependencies": [
    "framer-motion",
    "react"
  ],
  "files": [
    {
      "path": "@uitripled/react-carbon/src/components/native/native-typewriter-carbon.tsx",
      "content": "\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport { motion, useReducedMotion } from \"framer-motion\";\nimport { useEffect, useState } from \"react\";\n\ninterface NativeTypewriterProps {\n  /**\n   * The text content to type. Can be a string or an array of strings.\n   * If an array, it will type each string in sequence (or loop if loop=true).\n   */\n  content: string | string[];\n  /**\n   * Typing speed.\n   * 'slow' = 100ms, 'medium' = 50ms, 'fast' = 30ms.\n   * Or pass a number in ms.\n   * Default: 'medium'\n   */\n  speed?: \"slow\" | \"medium\" | \"fast\" | number;\n  /**\n   * Whether to show a blinking cursor.\n   * Default: true\n   */\n  cursor?: boolean;\n  /**\n   * Whether to loop the typing animation.\n   * Default: false\n   */\n  loop?: boolean;\n  /**\n   * Delay before starting animation in ms.\n   * Default: 0\n   */\n  startDelay?: number;\n  /**\n   * Whether to apply a blur/morph effect to each character.\n   * Default: true\n   */\n  morph?: boolean;\n  className?: string;\n}\n\nexport function NativeTypewriter({\n  content,\n  speed = \"medium\",\n  cursor = true,\n  loop = false,\n  startDelay = 0,\n  morph = true,\n  className,\n}: NativeTypewriterProps) {\n  const shouldReduceMotion = useReducedMotion();\n  const [displayedText, setDisplayedText] = useState(\"\");\n  const [isStarted, setIsStarted] = useState(false);\n\n  // Calculate delay calculation\n  const speedMap = {\n    slow: 100,\n    medium: 50,\n    fast: 30,\n  };\n  const typingSpeed = typeof speed === \"number\" ? speed : speedMap[speed];\n\n  useEffect(() => {\n    // If reduced motion is enabled, just show the full text immediately\n    if (shouldReduceMotion) {\n      setDisplayedText(Array.isArray(content) ? content.join(\" \") : content);\n      return;\n    }\n\n    let timeoutId: NodeJS.Timeout;\n    let currentIndex = 0;\n    let currentStringIndex = 0;\n\n    // Normalize content to array\n    const textArray = Array.isArray(content) ? content : [content];\n\n    // Actually, simpler logic for a first pass is often better.\n    // Let's implement a robust \"Type -> Wait -> (Delete if multi/loop) -> Next\" cycle.\n\n    let isDeleting = false;\n\n    // Unified typing loop logic\n    const runLoop = () => {\n      const currentString = textArray[currentStringIndex];\n\n      if (isDeleting) {\n        setDisplayedText(currentString.substring(0, currentIndex));\n        currentIndex--;\n        if (currentIndex < 0) {\n          isDeleting = false;\n          currentIndex = 0;\n          currentStringIndex = (currentStringIndex + 1) % textArray.length;\n          if (!loop && currentStringIndex === 0) {\n            return;\n          }\n          timeoutId = setTimeout(runLoop, 500);\n        } else {\n          timeoutId = setTimeout(runLoop, typingSpeed / 2);\n        }\n      } else {\n        setDisplayedText(currentString.substring(0, currentIndex + 1));\n        currentIndex++;\n        if (currentIndex > currentString.length) {\n          if (\n            (textArray.length > 1 &&\n              (loop || currentStringIndex < textArray.length - 1)) ||\n            (textArray.length === 1 && loop)\n          ) {\n            isDeleting = true;\n            currentIndex = currentString.length;\n            timeoutId = setTimeout(runLoop, 2000);\n          }\n        } else {\n          timeoutId = setTimeout(runLoop, typingSpeed);\n        }\n      }\n    };\n\n    const initialTimer = setTimeout(() => {\n      setIsStarted(true);\n      runLoop();\n    }, startDelay);\n\n    return () => {\n      clearTimeout(initialTimer);\n      clearTimeout(timeoutId);\n    };\n  }, [content, typingSpeed, loop, startDelay, shouldReduceMotion]);\n\n  return (\n    <div className={cn(\"inline-flex items-center\", className)}>\n      <span className=\"whitespace-pre-wrap\">\n        {displayedText.split(\"\").map((char, index) => (\n          <motion.span\n            key={index}\n            initial={\n              morph ? { opacity: 0, filter: \"blur(2px)\" } : { opacity: 1 }\n            }\n            animate={\n              morph ? { opacity: 1, filter: \"blur(0px)\" } : { opacity: 1 }\n            }\n            transition={{ duration: 0.1 }}\n          >\n            {char}\n          </motion.span>\n        ))}\n      </span>\n      {cursor && (\n        <motion.span\n          initial={{ opacity: 0 }}\n          animate={{ opacity: 1 }}\n          transition={{\n            duration: 0.5,\n            repeat: Infinity,\n            repeatType: \"reverse\",\n            ease: \"linear\",\n          }}\n          className=\"ml-0.5 inline-block h-[1.2em] w-[2px] bg-primary align-bottom\"\n          aria-hidden=\"true\"\n        />\n      )}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/uitripled/native-typewriter-shadcnui.tsx"
    }
  ]
}