{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "ai-response-typing-shadcnui",
  "type": "registry:component",
  "title": "AI Response Typing Stream",
  "description": "Character-by-character typing animation with natural pauses and thinking states",
  "registryDependencies": [
    "button"
  ],
  "dependencies": [
    "framer-motion",
    "react"
  ],
  "files": [
    {
      "path": "@uitripled/react-shadcn/src/components/motion-core/ai-response-typing.tsx",
      "content": "\"use client\";\n\nimport { motion } from \"framer-motion\";\nimport { useEffect, useRef, useState } from \"react\";\n\ntype AIResponseTypingProps = {\n  text?: string;\n  speed?: number;\n  showCursor?: boolean;\n  onComplete?: () => void;\n  thinkingState?: \"idle\" | \"thinking\" | \"typing\";\n};\n\nexport function AIResponseTyping({\n  text = \"Hello! I'm an AI assistant. I can help you with questions, provide information, and assist with various tasks. Feel free to ask me anything!\",\n  speed = 30,\n  showCursor = true,\n  onComplete,\n  thinkingState = \"typing\",\n}: AIResponseTypingProps) {\n  const [displayedText, setDisplayedText] = useState(\"\");\n  const [isThinking, setIsThinking] = useState(false);\n  const [isTyping, setIsTyping] = useState(false);\n  const intervalRef = useRef<NodeJS.Timeout | null>(null);\n\n  useEffect(() => {\n    if (thinkingState === \"thinking\") {\n      setIsThinking(true);\n      setIsTyping(false);\n      setDisplayedText(\"\");\n      return;\n    }\n\n    if (thinkingState === \"typing\" && text) {\n      setIsThinking(false);\n      setIsTyping(true);\n      setDisplayedText(\"\");\n\n      let currentIndex = 0;\n      const chars = text.split(\"\");\n\n      const typeNextChar = () => {\n        if (currentIndex < chars.length) {\n          const nextChar = chars[currentIndex];\n\n          // Simulate natural typing pauses at punctuation\n          const isPause =\n            nextChar === \".\" ||\n            nextChar === \",\" ||\n            nextChar === \"!\" ||\n            nextChar === \"?\" ||\n            nextChar === \"\\n\";\n\n          setDisplayedText((prev) => prev + nextChar);\n          currentIndex++;\n\n          // Schedule next character with pause if needed\n          if (isPause) {\n            intervalRef.current = setTimeout(() => {\n              typeNextChar();\n            }, speed * 3);\n          } else {\n            intervalRef.current = setTimeout(() => {\n              typeNextChar();\n            }, speed);\n          }\n        } else {\n          if (intervalRef.current) clearTimeout(intervalRef.current);\n          setIsTyping(false);\n          onComplete?.();\n        }\n      };\n\n      // Start typing\n      typeNextChar();\n\n      return () => {\n        if (intervalRef.current) clearTimeout(intervalRef.current);\n      };\n    }\n  }, [text, speed, thinkingState, onComplete]);\n\n  return (\n    <div className=\"w-full max-w-2xl\">\n      <div className=\"relative rounded-2xl border border-border bg-card p-6 min-h-[100px]\">\n        {/* Thinking state shimmer */}\n        {isThinking && (\n          <motion.div\n            initial={{ opacity: 0 }}\n            animate={{ opacity: 1 }}\n            exit={{ opacity: 0 }}\n            className=\"flex items-center gap-2\"\n          >\n            <span className=\"text-muted-foreground\">Thinking</span>\n            <div className=\"flex gap-1\">\n              {[0, 1, 2].map((i) => (\n                <motion.div\n                  key={i}\n                  className=\"w-2 h-2 rounded-full bg-primary\"\n                  animate={{\n                    scale: [1, 1.2, 1],\n                    opacity: [0.5, 1, 0.5],\n                  }}\n                  transition={{\n                    duration: 1,\n                    repeat: Infinity,\n                    delay: i * 0.2,\n                    ease: \"easeInOut\",\n                  }}\n                />\n              ))}\n            </div>\n          </motion.div>\n        )}\n\n        {/* Typing output */}\n        {displayedText && (\n          <motion.p\n            initial={{ opacity: 0 }}\n            animate={{ opacity: 1 }}\n            className=\"text-foreground leading-relaxed whitespace-pre-wrap break-words\"\n          >\n            {displayedText}\n            {showCursor && isTyping && (\n              <motion.span\n                className=\"inline-block w-0.5 h-5 bg-primary ml-1 align-middle\"\n                animate={{ opacity: [1, 1, 1, 0, 0] }}\n                transition={{\n                  duration: 1,\n                  repeat: Infinity,\n                  repeatType: \"loop\",\n                  ease: \"linear\",\n                }}\n              />\n            )}\n            {showCursor && !isTyping && !isThinking && displayedText && (\n              <motion.span\n                className=\"inline-block w-0.5 h-5 bg-primary ml-1 align-middle opacity-50\"\n                animate={{ opacity: [0.5, 0.5, 0, 0] }}\n                transition={{\n                  duration: 1,\n                  repeat: Infinity,\n                  repeatType: \"loop\",\n                  ease: \"linear\",\n                  repeatDelay: 0.5,\n                }}\n              />\n            )}\n          </motion.p>\n        )}\n\n        {/* Shimmer effect overlay */}\n        {(isTyping || isThinking) && (\n          <motion.div\n            className=\"absolute inset-0 pointer-events-none overflow-hidden rounded-2xl\"\n            initial={{ x: \"-100%\" }}\n            animate={{ x: \"100%\" }}\n            transition={{\n              duration: 2,\n              repeat: Infinity,\n              ease: \"linear\",\n            }}\n            style={{\n              background:\n                \"linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent)\",\n            }}\n          />\n        )}\n\n        {/* Decorative gradient border */}\n        {(isTyping || isThinking) && (\n          <div className=\"absolute inset-0 rounded-2xl pointer-events-none overflow-hidden\">\n            <motion.div\n              className=\"absolute inset-0 rounded-2xl\"\n              animate={{\n                background: [\n                  \"linear-gradient(135deg, transparent, transparent)\",\n                  \"linear-gradient(135deg, rgba(139, 92, 246, 0.1), transparent)\",\n                  \"linear-gradient(135deg, transparent, transparent)\",\n                ],\n              }}\n              transition={{\n                duration: 2,\n                repeat: Infinity,\n                ease: \"easeInOut\",\n              }}\n            />\n          </div>\n        )}\n      </div>\n\n      {/* Status indicator */}\n      <div className=\"mt-3 flex items-center gap-2\">\n        <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n          <motion.div\n            animate={{\n              scale: isTyping ? [1, 1.2, 1] : 1,\n            }}\n            transition={{\n              duration: 0.5,\n              repeat: isTyping ? Infinity : 0,\n              ease: \"easeInOut\",\n            }}\n            className=\"w-2 h-2 rounded-full bg-green-500\"\n          />\n          <span>\n            {isThinking\n              ? \"AI is thinking...\"\n              : isTyping\n                ? \"AI is typing...\"\n                : \"Ready\"}\n          </span>\n        </div>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/uitripled/ai-response-typing-shadcnui.tsx"
    }
  ]
}