{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "typing-indicator",
  "type": "registry:ui",
  "title": "Typing Indicator",
  "description": "Pre-first-token dots; bounce becomes an opacity pulse under reduced motion.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "https://seamui.dev/r/utils.json",
    "https://seamui.dev/r/motion.json"
  ],
  "files": [
    {
      "path": "registry/seam/ui/typing-indicator.tsx",
      "content": "\"use client\"\n\nimport type * as React from \"react\"\nimport { motion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { springs, fades, useReducedMotion } from \"@/lib/motion\"\n\n// The pre-first-token state — distinct from Spinner (generic loading): this\n// says someone is about to talk. It's the reference case for reduced motion as\n// a *variant*: the dots bounce (springs.bouncy, the one sanctioned use), and\n// under reduced motion the bounce becomes a staggered opacity pulse. The\n// feedback is never removed — it just stops traveling.\nfunction TypingIndicator({\n  className,\n  ...props\n}: React.ComponentProps<\"span\">) {\n  const reduceMotion = useReducedMotion()\n\n  return (\n    <span\n      data-slot=\"typing-indicator\"\n      role=\"status\"\n      aria-label=\"Assistant is typing\"\n      className={cn(\"inline-flex items-center gap-1\", className)}\n      {...props}\n    >\n      {[0, 1, 2].map((i) => (\n        <motion.span\n          key={i}\n          aria-hidden\n          className=\"bg-muted-foreground/50 size-1.5 rounded-full\"\n          animate={reduceMotion ? { opacity: 0.3 } : { y: -5 }}\n          transition={{\n            ...(reduceMotion ? fades.normal : springs.bouncy),\n            repeat: Infinity,\n            repeatType: \"reverse\",\n            delay: i * 0.15,\n          }}\n        />\n      ))}\n    </span>\n  )\n}\n\nexport { TypingIndicator }\n",
      "type": "registry:ui",
      "target": "components/ui/typing-indicator.tsx"
    }
  ]
}