{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "ai-chat-card",
  "title": "AI Chat Card",
  "description": "An AI chat card that types prompts into its composer, with send, attach and reset actions.",
  "dependencies": [
    "motion",
    "lucide-react"
  ],
  "registryDependencies": [
    "@spectrumui/use-typewriter"
  ],
  "files": [
    {
      "path": "app/registry/ai-chat-card/ai-chat-card.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { motion, useInView } from \"motion/react\";\nimport { ArrowUp, MessageCircleDashed, Plus, RefreshCw } from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\n\nimport { useTypewriter } from \"@/components/spectrumui/use-typewriter\";\n\nexport interface AIChatCardProps {\n  title?: string;\n  subtitle?: string;\n  greeting?: string;\n  prompt?: string;\n  /** Prompts the composer types out on a loop. */\n  prompts?: string[];\n  /** Turn the prompt-typing animation off. */\n  autoType?: boolean;\n  placeholder?: string;\n  icon?: React.ReactNode;\n  onSend?: (message: string) => void;\n  onReset?: () => void;\n  onAttach?: () => void;\n  className?: string;\n}\n\nconst DEFAULT_PROMPTS = [\n  \"I'm building a chat for our app and the scroll behavior is driving me nuts. Every…\",\n  \"Add a pricing table with a monthly/annual toggle\",\n  \"Make my hero section feel more premium\",\n  \"Generate a changelog page from our GitHub releases\",\n];\n\nexport function AIChatCard({\n  title = \"New Chat\",\n  subtitle = \"How can I help you today?\",\n  greeting = \"Morning, Arihant!\",\n  prompt = \"What are we working on today? Press send to start a new conversation\",\n  prompts = DEFAULT_PROMPTS,\n  autoType = true,\n  placeholder = \"Ask anything…\",\n  icon,\n  onSend,\n  onReset,\n  onAttach,\n  className,\n}: AIChatCardProps) {\n  const rootRef = React.useRef<HTMLDivElement>(null);\n  const textareaRef = React.useRef<HTMLTextAreaElement>(null);\n  const inView = useInView(rootRef, { margin: \"-10% 0px\" });\n\n  const [userActive, setUserActive] = React.useState(false);\n  const [userMessage, setUserMessage] = React.useState(\"\");\n  const [spins, setSpins] = React.useState(0);\n\n  const { text: typedMessage, phase } = useTypewriter(prompts, {\n    typeMs: 48,\n    deleteMs: 14,\n    holdMs: 3400,\n    gapMs: 900,\n    enabled: autoType && inView && !userActive,\n  });\n\n  const message = userActive || !autoType ? userMessage : typedMessage;\n\n  const takeOver = () => {\n    if (userActive || !autoType) return;\n    setUserMessage(typedMessage);\n    setUserActive(true);\n    requestAnimationFrame(() => textareaRef.current?.focus());\n  };\n\n  return (\n    <div\n      ref={rootRef}\n      className={cn(\n        \"flex w-full flex-col rounded-[24px] bg-white\",\n        \"shadow-[0_0_16.4px_1px_rgba(10,10,10,0.05),0_1px_3px_0_rgba(0,0,0,0.1),0_1px_2px_-1px_rgba(0,0,0,0.1)]\",\n        \"dark:bg-neutral-950 dark:shadow-[0_0_0_1px_rgba(255,255,255,0.12),0_1px_3px_0_rgba(0,0,0,0.5)]\",\n        className,\n      )}\n    >\n      {/* Header */}\n      <div className=\"flex items-start justify-between gap-4 border-b border-border px-5 pb-4 pt-5\">\n        <div>\n          <h3 className=\"text-[16px] font-medium leading-6 text-foreground\">\n            {title}\n          </h3>\n          <p className=\"mt-0.5 text-sm leading-5 text-muted-foreground\">\n            {subtitle}\n          </p>\n        </div>\n        <motion.button\n          type=\"button\"\n          onClick={() => {\n            setSpins((count) => count + 1);\n            setUserActive(false);\n            setUserMessage(\"\");\n            onReset?.();\n          }}\n          whileTap={{ scale: 0.9 }}\n          aria-label=\"Reset conversation\"\n          className=\"flex h-[30px] w-[30px] shrink-0 items-center justify-center rounded-[18px] border border-border bg-white text-muted-foreground transition-colors hover:text-foreground dark:bg-neutral-950\"\n        >\n          <motion.span\n            animate={{ rotate: spins * 360 }}\n            transition={{ type: \"spring\", bounce: 0.2, duration: 0.7 }}\n            className=\"flex\"\n          >\n            <RefreshCw className=\"h-4 w-4\" />\n          </motion.span>\n        </motion.button>\n      </div>\n\n      {/* Empty state */}\n      <div className=\"flex flex-1 flex-col items-center justify-center px-8 py-10 text-center\">\n        <motion.div\n          animate={{ y: [0, -3, 0] }}\n          transition={{ duration: 3, repeat: Infinity, ease: \"easeInOut\" }}\n          className=\"flex h-10 w-10 items-center justify-center rounded-[14px] bg-neutral-100 dark:bg-neutral-900\"\n        >\n          {icon ?? <MessageCircleDashed className=\"h-5 w-5 text-foreground\" />}\n        </motion.div>\n        <motion.p\n          initial={{ opacity: 0, y: 8 }}\n          whileInView={{ opacity: 1, y: 0 }}\n          viewport={{ once: true }}\n          transition={{ duration: 0.5, delay: 0.15, ease: \"easeOut\" }}\n          className=\"mt-4 text-[18px] font-medium leading-7 tracking-[-0.45px] text-foreground\"\n        >\n          {greeting}\n        </motion.p>\n        <motion.p\n          initial={{ opacity: 0, y: 8 }}\n          whileInView={{ opacity: 1, y: 0 }}\n          viewport={{ once: true }}\n          transition={{ duration: 0.5, delay: 0.25, ease: \"easeOut\" }}\n          className=\"mt-1.5 max-w-[190px] text-sm leading-[22.75px] text-muted-foreground\"\n        >\n          {prompt}\n        </motion.p>\n      </div>\n\n      {/* Composer */}\n      <div className=\"px-5 pb-5\">\n        <div className=\"rounded-[18px] bg-neutral-200/50 p-3 transition-colors focus-within:bg-neutral-200/70 dark:bg-neutral-800/50 dark:focus-within:bg-neutral-800/70\">\n          {userActive || !autoType ? (\n            <textarea\n              ref={textareaRef}\n              value={userMessage}\n              onChange={(event) => setUserMessage(event.target.value)}\n              placeholder={placeholder}\n              rows={2}\n              className=\"w-full resize-none bg-transparent text-sm leading-5 text-foreground outline-hidden placeholder:text-muted-foreground\"\n            />\n          ) : (\n            <div\n              onClick={takeOver}\n              className=\"min-h-10 w-full cursor-text text-left text-sm leading-5 text-foreground\"\n            >\n              {message}\n              <motion.span\n                aria-hidden\n                className=\"ml-px inline-block h-3.5 w-px bg-foreground align-middle\"\n                animate={{ opacity: [1, 1, 0, 0] }}\n                transition={{\n                  duration: 1,\n                  repeat: Infinity,\n                  times: [0, 0.5, 0.5, 1],\n                }}\n              />\n              {!message ? (\n                <span className=\"text-muted-foreground\">{placeholder}</span>\n              ) : null}\n            </div>\n          )}\n          <div className=\"mt-2 flex items-center justify-between\">\n            <motion.button\n              type=\"button\"\n              onClick={onAttach}\n              whileHover={{ rotate: 90 }}\n              whileTap={{ scale: 0.88 }}\n              transition={{ type: \"spring\", bounce: 0.4, duration: 0.4 }}\n              aria-label=\"Add files\"\n              className=\"flex h-[30px] w-[30px] items-center justify-center rounded-[18px] border border-border bg-white text-foreground dark:bg-neutral-950\"\n            >\n              <Plus className=\"h-4 w-4\" />\n            </motion.button>\n            <motion.button\n              type=\"button\"\n              onClick={() => onSend?.(message)}\n              whileHover={{\n                scale: 1.08,\n                transition: { type: \"spring\", bounce: 0.5, duration: 0.4 },\n              }}\n              whileTap={{\n                scale: 0.88,\n                transition: { type: \"spring\", bounce: 0.5, duration: 0.4 },\n              }}\n              animate={\n                phase === \"holding\" && !userActive\n                  ? { scale: [1, 1.14, 1] }\n                  : { scale: 1 }\n              }\n              transition={{ duration: 0.5, ease: \"easeInOut\", times: [0, 0.35, 1] }}\n              aria-label=\"Send message\"\n              className=\"group flex h-[30px] w-[30px] items-center justify-center rounded-[18px] bg-black text-white dark:bg-white dark:text-neutral-950\"\n            >\n              <ArrowUp className=\"h-4 w-4 transition-transform duration-200 group-hover:-translate-y-px\" />\n            </motion.button>\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/spectrumui/ai-chat-card.tsx"
    }
  ],
  "type": "registry:component"
}
