{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "chat-app-shadcnui",
  "type": "registry:component",
  "title": "Chat App",
  "description": "Fully functional chat interface with animated messages",
  "registryDependencies": [
    "button"
  ],
  "dependencies": [
    "framer-motion",
    "react"
  ],
  "files": [
    {
      "path": "@uitripled/react-shadcn/src/components/components/chat/chat-app.tsx",
      "content": "\"use client\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { Card } from \"@/components/ui/card\";\nimport { Input } from \"@/components/ui/input\";\nimport { ScrollArea } from \"@/components/ui/scroll-area\";\nimport { AnimatePresence, motion } from \"framer-motion\";\nimport { Bot, Loader2, Send, User } from \"lucide-react\";\nimport { useEffect, useRef, useState } from \"react\";\n\ntype Message = {\n  id: number;\n  text: string;\n  sender: \"user\" | \"bot\";\n  timestamp: Date;\n};\n\nconst initialMessages: Message[] = [\n  {\n    id: 1,\n    text: \"Hello! How can I help you today?\",\n    sender: \"bot\",\n    timestamp: new Date(Date.now() - 60000),\n  },\n  {\n    id: 2,\n    text: \"Hi there! I was wondering about your features.\",\n    sender: \"user\",\n    timestamp: new Date(Date.now() - 30000),\n  },\n  {\n    id: 3,\n    text: \"Of course! What would you like to know?\",\n    sender: \"bot\",\n    timestamp: new Date(Date.now() - 15000),\n  },\n];\n\nexport function ChatApp() {\n  const [messages, setMessages] = useState<Message[]>(initialMessages);\n  const [inputValue, setInputValue] = useState(\"\");\n  const [isTyping, setIsTyping] = useState(false);\n  const scrollAreaRef = useRef<HTMLDivElement>(null);\n  const inputRef = useRef<HTMLInputElement>(null);\n\n  useEffect(() => {\n    // Auto-scroll to bottom when new messages arrive\n    if (scrollAreaRef.current) {\n      const scrollContainer = scrollAreaRef.current.querySelector(\n        \"[data-radix-scroll-area-viewport]\"\n      );\n      if (scrollContainer) {\n        scrollContainer.scrollTop = scrollContainer.scrollHeight;\n      }\n    }\n  }, [messages, isTyping]);\n\n  const formatTime = (date: Date): string => {\n    const hours = date.getHours();\n    const minutes = date.getMinutes();\n    const ampm = hours >= 12 ? \"PM\" : \"AM\";\n    const displayHours = hours % 12 || 12;\n    const displayMinutes = minutes.toString().padStart(2, \"0\");\n    return `${displayHours}:${displayMinutes} ${ampm}`;\n  };\n\n  const handleSend = () => {\n    const trimmed = inputValue.trim();\n    if (!trimmed) return;\n\n    const userMessage: Message = {\n      id: Date.now(),\n      text: trimmed,\n      sender: \"user\",\n      timestamp: new Date(),\n    };\n\n    setMessages((prev) => [...prev, userMessage]);\n    setInputValue(\"\");\n    setIsTyping(true);\n\n    // Simulate bot response with typing indicator\n    setTimeout(() => {\n      const botMessage: Message = {\n        id: Date.now() + 1,\n        text: \"Thanks for your message! This is an automated response. I can help you with various tasks and answer your questions.\",\n        sender: \"bot\",\n        timestamp: new Date(),\n      };\n      setMessages((prev) => [...prev, botMessage]);\n      setIsTyping(false);\n      inputRef.current?.focus();\n    }, 1500);\n  };\n\n  const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n    if (e.key === \"Enter\" && !e.shiftKey) {\n      e.preventDefault();\n      handleSend();\n    }\n  };\n\n  return (\n    <div className=\"\">\n      <Card className=\"flex h-[700px] w-full md:w-[600px] mx-auto flex-col overflow-hidden shadow-2xl\">\n        {/* Header */}\n        <motion.div\n          initial={{ opacity: 0, y: -20 }}\n          animate={{ opacity: 1, y: 0 }}\n          className=\"border-b bg-gradient-to-r from-primary/10 via-accent/10 to-primary/10 px-6 py-4\"\n        >\n          <div className=\"flex items-center gap-4\">\n            <motion.div\n              className=\"relative flex h-12 w-12 items-center justify-center rounded-full bg-primary shadow-lg\"\n              whileHover={{ scale: 1.05 }}\n              whileTap={{ scale: 0.95 }}\n            >\n              <Bot\n                className=\"h-6 w-6 text-primary-foreground\"\n                aria-hidden=\"true\"\n              />\n              <motion.div\n                className=\"absolute -right-1 -top-1 h-3 w-3 rounded-full bg-green-500 ring-2 ring-background\"\n                initial={{ scale: 0 }}\n                animate={{ scale: 1 }}\n                transition={{ delay: 0.5 }}\n              />\n            </motion.div>\n            <div>\n              <h1 className=\"text-lg font-semibold\">AI Assistant</h1>\n              <p className=\"text-sm text-muted-foreground\">\n                Always here to help\n              </p>\n            </div>\n          </div>\n        </motion.div>\n\n        {/* Messages */}\n        <ScrollArea ref={scrollAreaRef} className=\"flex-1 px-6\">\n          <div\n            className=\"space-y-6 py-6\"\n            role=\"log\"\n            aria-live=\"polite\"\n            aria-label=\"Chat messages\"\n          >\n            <AnimatePresence mode=\"popLayout\">\n              {messages.map((message) => (\n                <motion.div\n                  key={message.id}\n                  layout\n                  initial={{ opacity: 0, y: 20, scale: 0.95 }}\n                  animate={{ opacity: 1, y: 0, scale: 1 }}\n                  exit={{ opacity: 0, scale: 0.95 }}\n                  transition={{\n                    duration: 0.3,\n                    ease: [0.4, 0, 0.2, 1],\n                  }}\n                  className={`flex gap-3 ${\n                    message.sender === \"user\" ? \"flex-row-reverse\" : \"flex-row\"\n                  }`}\n                >\n                  <motion.div\n                    initial={{ scale: 0 }}\n                    animate={{ scale: 1 }}\n                    transition={{\n                      delay: 0.1,\n                      type: \"spring\",\n                      stiffness: 260,\n                      damping: 20,\n                    }}\n                    className={`flex h-10 w-10 shrink-0 items-center justify-center rounded-full shadow-md ${\n                      message.sender === \"user\" ? \"bg-primary\" : \"bg-accent\"\n                    }`}\n                    aria-hidden=\"true\"\n                  >\n                    {message.sender === \"user\" ? (\n                      <User className=\"h-5 w-5 text-primary-foreground\" />\n                    ) : (\n                      <Bot className=\"h-5 w-5 text-accent-foreground\" />\n                    )}\n                  </motion.div>\n                  <div\n                    className={`flex max-w-[75%] flex-col ${\n                      message.sender === \"user\" ? \"items-end\" : \"items-start\"\n                    }`}\n                  >\n                    <motion.div\n                      initial={{ scale: 0.9, opacity: 0 }}\n                      animate={{ scale: 1, opacity: 1 }}\n                      transition={{ delay: 0.15 }}\n                      className={`rounded-2xl px-4 py-3 shadow-sm ${\n                        message.sender === \"user\"\n                          ? \"bg-primary text-primary-foreground\"\n                          : \"bg-accent text-accent-foreground\"\n                      }`}\n                    >\n                      <p className=\"text-sm leading-relaxed\">{message.text}</p>\n                    </motion.div>\n                    <time\n                      className=\"mt-1.5 px-1 text-xs text-muted-foreground\"\n                      dateTime={message.timestamp.toISOString()}\n                    >\n                      {formatTime(message.timestamp)}\n                    </time>\n                  </div>\n                </motion.div>\n              ))}\n            </AnimatePresence>\n\n            {/* Typing Indicator */}\n            <AnimatePresence>\n              {isTyping && (\n                <motion.div\n                  initial={{ opacity: 0, y: 10 }}\n                  animate={{ opacity: 1, y: 0 }}\n                  exit={{ opacity: 0, y: -10 }}\n                  className=\"flex gap-3\"\n                  aria-live=\"polite\"\n                  aria-label=\"AI is typing\"\n                >\n                  <div className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-accent shadow-md\">\n                    <Bot\n                      className=\"h-5 w-5 text-accent-foreground\"\n                      aria-hidden=\"true\"\n                    />\n                  </div>\n                  <div className=\"flex items-center gap-1 rounded-2xl bg-accent px-4 py-3 shadow-sm\">\n                    <motion.div\n                      className=\"h-2 w-2 rounded-full bg-accent-foreground/60\"\n                      animate={{ scale: [1, 1.2, 1] }}\n                      transition={{ duration: 0.6, repeat: Infinity, delay: 0 }}\n                    />\n                    <motion.div\n                      className=\"h-2 w-2 rounded-full bg-accent-foreground/60\"\n                      animate={{ scale: [1, 1.2, 1] }}\n                      transition={{\n                        duration: 0.6,\n                        repeat: Infinity,\n                        delay: 0.2,\n                      }}\n                    />\n                    <motion.div\n                      className=\"h-2 w-2 rounded-full bg-accent-foreground/60\"\n                      animate={{ scale: [1, 1.2, 1] }}\n                      transition={{\n                        duration: 0.6,\n                        repeat: Infinity,\n                        delay: 0.4,\n                      }}\n                    />\n                  </div>\n                </motion.div>\n              )}\n            </AnimatePresence>\n          </div>\n        </ScrollArea>\n\n        {/* Input */}\n        <motion.div\n          initial={{ opacity: 0, y: 20 }}\n          animate={{ opacity: 1, y: 0 }}\n          transition={{ delay: 0.2 }}\n          className=\"border-t bg-gradient-to-r from-background via-accent/5 to-background p-6\"\n        >\n          <div className=\"flex gap-3\" role=\"group\" aria-label=\"Message input\">\n            <Input\n              ref={inputRef}\n              value={inputValue}\n              onChange={(e) => setInputValue(e.target.value)}\n              onKeyDown={handleKeyDown}\n              placeholder=\"Type your message...\"\n              className=\"flex-1 rounded-full border-2 px-4 focus-visible:ring-2 focus-visible:ring-primary\"\n              aria-label=\"Message input\"\n              disabled={isTyping}\n            />\n            <Button\n              onClick={handleSend}\n              size=\"icon\"\n              disabled={!inputValue.trim() || isTyping}\n              aria-label=\"Send message\"\n              type=\"button\"\n              className=\"h-11 w-11 rounded-full shadow-md transition-all hover:scale-105 hover:shadow-lg active:scale-95 disabled:opacity-50\"\n            >\n              {isTyping ? (\n                <Loader2 className=\"h-5 w-5 animate-spin\" aria-hidden=\"true\" />\n              ) : (\n                <Send className=\"h-5 w-5\" aria-hidden=\"true\" />\n              )}\n            </Button>\n          </div>\n        </motion.div>\n      </Card>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/uitripled/chat-app-shadcnui.tsx"
    }
  ]
}