{
  "$schema": "https://ui.shadcn.com/schema/registry.json",
  "name": "uifiles",
  "homepage": "https://uifiles.dev",
  "items": [
    {
      "name": "base",
      "title": "uifiles",
      "author": "Jamie Thompson <jamie@jamiethompson.design> (https://jamiethompson.design)",
      "description": "The uifiles design system: shadcn on Base UI (Nova), the uifiles tokens, Geist type, and project config, with the @uifiles registry registered in components.json. Run `shadcn init https://uifiles.dev/r/base.json` in a new project, then add components.",
      "dependencies": [
        "cn",
        "class-variance-authority",
        "lucide-react",
        "@base-ui/react"
      ],
      "devDependencies": [
        "tw-animate-css"
      ],
      "registryDependencies": [
        "utils",
        "font-geist",
        "font-heading-geist"
      ],
      "files": [],
      "cssVars": {
        "theme": {
          "radius-sm": "calc(var(--radius) * 0.6)",
          "radius-md": "calc(var(--radius) * 0.8)",
          "radius-lg": "var(--radius)",
          "radius-xl": "calc(var(--radius) * 1.4)",
          "radius-2xl": "calc(var(--radius) * 1.8)",
          "radius-3xl": "calc(var(--radius) * 2.2)",
          "radius-4xl": "calc(var(--radius) * 2.6)"
        },
        "light": {
          "background": "oklch(1 0 0)",
          "foreground": "oklch(0.145 0 0)",
          "card": "oklch(1 0 0)",
          "card-foreground": "oklch(0.145 0 0)",
          "popover": "oklch(1 0 0)",
          "popover-foreground": "oklch(0.145 0 0)",
          "primary": "oklch(0.205 0 0)",
          "primary-foreground": "oklch(0.985 0 0)",
          "secondary": "oklch(0.97 0 0)",
          "secondary-foreground": "oklch(0.205 0 0)",
          "muted": "oklch(0.97 0 0)",
          "muted-foreground": "oklch(0.53 0 0)",
          "accent": "oklch(0.97 0 0)",
          "accent-foreground": "oklch(0.205 0 0)",
          "destructive": "oklch(0.45 0.245 27.325)",
          "border": "oklch(0.922 0 0)",
          "input": "oklch(0.922 0 0)",
          "ring": "oklch(0.64 0 0)",
          "chart-1": "oklch(0.87 0 0)",
          "chart-2": "oklch(0.556 0 0)",
          "chart-3": "oklch(0.439 0 0)",
          "chart-4": "oklch(0.371 0 0)",
          "chart-5": "oklch(0.269 0 0)",
          "radius": "0.625rem",
          "sidebar": "oklch(0.985 0 0)",
          "sidebar-foreground": "oklch(0.145 0 0)",
          "sidebar-primary": "oklch(0.205 0 0)",
          "sidebar-primary-foreground": "oklch(0.985 0 0)",
          "sidebar-accent": "oklch(0.97 0 0)",
          "sidebar-accent-foreground": "oklch(0.205 0 0)",
          "sidebar-border": "oklch(0.922 0 0)",
          "sidebar-ring": "oklch(0.708 0 0)"
        },
        "dark": {
          "background": "oklch(0.145 0 0)",
          "foreground": "oklch(0.985 0 0)",
          "card": "oklch(0.205 0 0)",
          "card-foreground": "oklch(0.985 0 0)",
          "popover": "oklch(0.205 0 0)",
          "popover-foreground": "oklch(0.985 0 0)",
          "primary": "oklch(0.922 0 0)",
          "primary-foreground": "oklch(0.205 0 0)",
          "secondary": "oklch(0.269 0 0)",
          "secondary-foreground": "oklch(0.985 0 0)",
          "muted": "oklch(0.269 0 0)",
          "muted-foreground": "oklch(0.708 0 0)",
          "accent": "oklch(0.269 0 0)",
          "accent-foreground": "oklch(0.985 0 0)",
          "destructive": "oklch(0.74 0.191 22.216)",
          "border": "oklch(1 0 0 / 10%)",
          "input": "oklch(1 0 0 / 15%)",
          "ring": "oklch(0.556 0 0)",
          "chart-1": "oklch(0.87 0 0)",
          "chart-2": "oklch(0.556 0 0)",
          "chart-3": "oklch(0.439 0 0)",
          "chart-4": "oklch(0.371 0 0)",
          "chart-5": "oklch(0.269 0 0)",
          "sidebar": "oklch(0.205 0 0)",
          "sidebar-foreground": "oklch(0.985 0 0)",
          "sidebar-primary": "oklch(0.488 0.243 264.376)",
          "sidebar-primary-foreground": "oklch(0.985 0 0)",
          "sidebar-accent": "oklch(0.269 0 0)",
          "sidebar-accent-foreground": "oklch(0.985 0 0)",
          "sidebar-border": "oklch(1 0 0 / 10%)",
          "sidebar-ring": "oklch(0.556 0 0)"
        }
      },
      "css": {
        "@layer base": {
          "*": {
            "@apply border-border outline-ring/50": {}
          },
          "body": {
            "@apply bg-background text-foreground": {}
          },
          "html": {
            "@apply font-sans": {}
          },
          "@media (prefers-reduced-motion: reduce)": {
            "*, ::before, ::after": {
              "animation-duration": "0.01ms !important",
              "animation-iteration-count": "1 !important",
              "transition-duration": "0.01ms !important",
              "scroll-behavior": "auto !important"
            }
          }
        }
      },
      "docs": "Dark mode is class-based: add `dark` to `<html>`, for example with next-themes (not installed by this item). `@layer base` ships a `prefers-reduced-motion` guard that collapses every animation and transition. The cssVars are generated from the registry's app/globals.css; do not edit them by hand.",
      "type": "registry:base",
      "config": {
        "style": "base-nova",
        "tailwind": {
          "baseColor": "neutral"
        },
        "iconLibrary": "lucide",
        "menuColor": "default",
        "menuAccent": "subtle",
        "registries": {
          "@uifiles": "https://uifiles.dev/r/{name}.json"
        }
      }
    },
    {
      "name": "accordion",
      "title": "Accordion",
      "description": "Collapsible accordion component. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "accordion"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "alert",
      "title": "Alert",
      "description": "Alert component for messages and notifications. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "alert"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "alert-dialog",
      "title": "Alert Dialog",
      "description": "Alert dialog for confirmation prompts. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "alert-dialog"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "aspect-ratio",
      "title": "Aspect Ratio",
      "description": "Container that maintains aspect ratio. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "aspect-ratio"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "attachment",
      "title": "Attachment",
      "description": "File and image attachment display for chat and forms. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "attachment"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "avatar",
      "title": "Avatar",
      "description": "Avatar component for user profiles. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "avatar"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "badge",
      "title": "Badge",
      "description": "Badge component for labels and status indicators. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "badge"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "breadcrumb",
      "title": "Breadcrumb",
      "description": "Breadcrumb navigation component. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "breadcrumb"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "bubble",
      "title": "Bubble",
      "description": "Chat bubble surface for message content and reactions. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "bubble"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "button",
      "title": "Button",
      "description": "Button component with multiple variants. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "button"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "button-group",
      "title": "Button Group",
      "description": "Group multiple buttons together. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "button-group"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "calendar",
      "title": "Calendar",
      "description": "Calendar component for date selection. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "calendar"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "card",
      "title": "Card",
      "description": "Card container component. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "card"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "carousel",
      "title": "Carousel",
      "description": "Carousel component using Embla Carousel. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "carousel"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "chart",
      "title": "Chart",
      "description": "Chart components using Recharts. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "chart"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "checkbox",
      "title": "Checkbox",
      "description": "Checkbox input component. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "checkbox"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "collapsible",
      "title": "Collapsible",
      "description": "Collapsible container component. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "collapsible"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "combobox",
      "title": "Combobox",
      "description": "Searchable select component with autocomplete. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "combobox"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "command",
      "title": "Command",
      "description": "Command palette component (cmdk). Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "command"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "context-menu",
      "title": "Context Menu",
      "description": "Right-click context menu. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "context-menu"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "dialog",
      "title": "Dialog",
      "description": "Modal dialog component. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "dialog"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "direction",
      "title": "Direction",
      "description": "Text direction provider for RTL support. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "direction"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "drawer",
      "title": "Drawer",
      "description": "Mobile-friendly drawer component using Vaul. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "drawer"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "dropdown-menu",
      "title": "Dropdown Menu",
      "description": "Dropdown menu component. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "dropdown-menu"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "empty",
      "title": "Empty",
      "description": "Empty state component for no data scenarios. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "empty"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "field",
      "title": "Field",
      "description": "Field component for form inputs with labels and error messages. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "field"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "form",
      "title": "Form",
      "description": "Form primitives for react-hook-form. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "form"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "hover-card",
      "title": "Hover Card",
      "description": "Card that appears on hover. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "hover-card"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "input",
      "title": "Input",
      "description": "Text input component. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "input"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "input-group",
      "title": "Input Group",
      "description": "Input component with prefix and suffix addons. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "input-group"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "input-otp",
      "title": "Input OTP",
      "description": "One-time password input component. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "input-otp"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "item",
      "title": "Item",
      "description": "Generic item component for lists and menus. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "item"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "kbd",
      "title": "Kbd",
      "description": "Keyboard shortcut display component. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "kbd"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "label",
      "title": "Label",
      "description": "Form label component. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "label"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "marker",
      "title": "Marker",
      "description": "Inline conversation markers: status updates, system notes, labeled separators. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "marker"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "menubar",
      "title": "Menubar",
      "description": "Horizontal menubar component. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "menubar"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "message",
      "title": "Message",
      "description": "Chat message row with avatar, header, content and footer slots. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "message"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "message-scroller",
      "title": "Message Scroller",
      "description": "Chat transcript container that follows streaming output only while the reader is at the live edge. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "message-scroller"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "native-select",
      "title": "Native Select",
      "description": "Styled native HTML select element. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "native-select"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "navigation-menu",
      "title": "Navigation Menu",
      "description": "Accessible navigation menu with dropdowns. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "navigation-menu"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "pagination",
      "title": "Pagination",
      "description": "Pagination component for lists and tables. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "pagination"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "popover",
      "title": "Popover",
      "description": "Floating popover component. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "popover"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "progress",
      "title": "Progress",
      "description": "Progress bar component. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "progress"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "questionnaire",
      "title": "Questionnaire",
      "description": "Multi-step question flow: single choice, multiple choice, freeform, skippable. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "questionnaire"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "radio-group",
      "title": "Radio Group",
      "description": "Radio button group component. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "radio-group"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "resizable",
      "title": "Resizable",
      "description": "Resizable panel layout component. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "resizable"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "scroll-area",
      "title": "Scroll Area",
      "description": "Custom scrollable area with styled scrollbars. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "scroll-area"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "select",
      "title": "Select",
      "description": "Select dropdown component. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "select"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "separator",
      "title": "Separator",
      "description": "Visual divider between content sections. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "separator"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "sheet",
      "title": "Sheet",
      "description": "Slide-out panel component (drawer). Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "sheet"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "sidebar",
      "title": "Sidebar",
      "description": "Collapsible sidebar component for app layouts. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "sidebar"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "skeleton",
      "title": "Skeleton",
      "description": "Skeleton loading placeholder. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "skeleton"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "slider",
      "title": "Slider",
      "description": "Slider input component. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "slider"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "sonner",
      "title": "Sonner",
      "description": "Opinionated toast component for React. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "sonner"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "spinner",
      "title": "Spinner",
      "description": "Loading spinner component. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "spinner"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "switch",
      "title": "Switch",
      "description": "Toggle switch component. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "switch"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "table",
      "title": "Table",
      "description": "Table component for displaying data. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "table"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "tabs",
      "title": "Tabs",
      "description": "Tabbed interface component. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "tabs"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "textarea",
      "title": "Textarea",
      "description": "Multi-line text input component. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "textarea"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "toast",
      "title": "Toast",
      "description": "Toast notification component using Sonner. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "toast"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "toggle",
      "title": "Toggle",
      "description": "Toggle button component. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "toggle"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "toggle-group",
      "title": "Toggle Group",
      "description": "Group of toggle buttons. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "toggle-group"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "tooltip",
      "title": "Tooltip",
      "description": "Tooltip component for additional context. Upstream shadcn/ui component, resolved against your style.",
      "registryDependencies": [
        "tooltip"
      ],
      "files": [],
      "type": "registry:ui"
    },
    {
      "name": "branch",
      "title": "Message Branch",
      "author": "Jamie Thompson <jamie@jamiethompson.design> (https://jamiethompson.design)",
      "description": "Branch switcher for an AI chat message that has several alternative responses (regenerations, forks). MessageBranch holds the current index, MessageBranchContent shows one child branch at a time, and MessageBranchSelector composes MessageBranchPrevious, MessageBranchPage (\"2 of 3\") and MessageBranchNext in a button-group with wrap-around navigation. Use it inside a message to let users flip between regenerated answers; pairs with @uifiles/response for markdown branches.",
      "dependencies": [
        "cn",
        "lucide-react"
      ],
      "registryDependencies": [
        "button",
        "button-group"
      ],
      "files": [
        {
          "path": "registry/ai/branch.tsx",
          "type": "registry:component",
          "target": "components/ai/branch.tsx"
        }
      ],
      "docs": "Derived from Vercel AI Elements (Apache-2.0, Copyright 2023 Vercel, Inc.); ported to Base UI and maintained by uifiles. Built for the Base UI styles (base-nova): it composes the Base UI wrappers from components/ui and relies on their render prop and event signatures, so it does not work on Radix or React Aria styles. Extracted from AI Elements message.tsx (the MessageBranch* family only; shadcn's own message replaces the rest). Same export names and props as upstream, plus an optional controlled `branch` prop (pair it with `onBranchChange`; `defaultBranch` stays uncontrolled). MessageBranch counts the branches of its MessageBranchContent while rendering: it finds the content among its own children and inside fragments and plain elements, also when the content is wrapped in memo or forwardRef or is a client reference a Server Component hands over in the App Router (a reference whose module is still loading suspends MessageBranch until it is in), so the selector and MessageBranchPage (\"2 of 3\") are in server-rendered HTML, including a page that is a Server Component, and hydrate without a warning; content rendered by a custom component cannot be counted that way and registers after mount, so its selector appears on the client. The current branch is clamped into range, so an out-of-range `defaultBranch`/`branch` or a shrinking branch list never shows an empty page, and MessageBranchPage reads \"0 of 0\" while no content is mounted. When clamping moves a controlled `branch` (out of range, or the list shrank below it), `onBranchChange` is called once with the clamped index (once under StrictMode too) so the parent's state follows; an uncontrolled index is remembered, so a branch that is removed and restored shows again. MessageBranchContent ignores null/boolean children (conditional branches) and accepts children rendered by a Server Component (RSC lazy nodes resolve without key warnings). MessageBranchPage renders ButtonGroupText (a div; upstream typed it as a span) and accepts its `render` prop. The buttons are the Base UI Button primitive (nativeButton), so onClick/disabled/type behave as before; no asChild anywhere.",
      "categories": [
        "ai",
        "chat"
      ],
      "type": "registry:component"
    },
    {
      "name": "chain-of-thought",
      "title": "Chain of Thought",
      "author": "Jamie Thompson <jamie@jamiethompson.design> (https://jamiethompson.design)",
      "description": "Collapsible step-by-step trace of an AI agent's reasoning: a header toggle (ChainOfThoughtHeader), then ChainOfThoughtStep rows with icon, label, description and complete/active/pending status, optional ChainOfThoughtSearchResults badges and a ChainOfThoughtImage with caption. Use it to show tool calls, searches and intermediate findings while an agent works; controlled or uncontrolled via open/defaultOpen/onOpenChange.",
      "dependencies": [
        "cn",
        "lucide-react"
      ],
      "registryDependencies": [
        "badge",
        "collapsible"
      ],
      "files": [
        {
          "path": "registry/ai/chain-of-thought.tsx",
          "type": "registry:component",
          "target": "components/ai/chain-of-thought.tsx"
        }
      ],
      "docs": "Derived from Vercel AI Elements (Apache-2.0, Copyright 2023 Vercel, Inc.); ported to Base UI and maintained by uifiles. Built for the Base UI styles (base-nova): it composes the Base UI wrappers from components/ui and relies on their render prop and event signatures, so it does not work on Radix or React Aria styles. Ported to Base UI. @radix-ui/react-use-controllable-state replaced with a useState controlled/uncontrolled pattern (open/defaultOpen/onOpenChange unchanged). Structure change: ChainOfThought now renders a single Base UI Collapsible root (still a div, same className/props) and ChainOfThoughtHeader/ChainOfThoughtContent render the Trigger/Panel directly instead of each wrapping their own Collapsible; upstream's split roots left the trigger's aria-controls pointing at a panel that never exists (axe aria-valid-attr-value). Panel animation selectors use data-open/data-closed instead of data-[state=...]. Pending steps keep text at text-muted-foreground and dim only the icon (upstream's text-muted-foreground/50 fails WCAG AA at 1.96:1). A status value ChainOfThoughtStep does not know falls back to the neutral (complete) styling. ChainOfThoughtSearchResults renders nothing when it has no children, so an empty results list adds no spacing. ChainOfThoughtImage is a frame around the image you pass as children: that element must carry its own alt text (alt=\"\" if decorative); the optional caption renders as a paragraph below it.",
      "categories": [
        "ai",
        "chat",
        "agent"
      ],
      "type": "registry:component"
    },
    {
      "name": "checkpoint",
      "title": "Checkpoint",
      "author": "Jamie Thompson <jamie@jamiethompson.design> (https://jamiethompson.design)",
      "description": "Inline checkpoint marker between chat messages: a bookmark icon, a small ghost button with an optional tooltip, and a separator rule that fills the rest of the row. Use it to let people restore a conversation or agent workspace to an earlier point. Composes shadcn button, tooltip and separator.",
      "dependencies": [
        "cn",
        "lucide-react"
      ],
      "registryDependencies": [
        "button",
        "separator",
        "tooltip"
      ],
      "files": [
        {
          "path": "registry/ai/checkpoint.tsx",
          "type": "registry:component",
          "target": "components/ai/checkpoint.tsx"
        }
      ],
      "docs": "Derived from Vercel AI Elements (Apache-2.0, Copyright 2023 Vercel, Inc.); ported to Base UI and maintained by uifiles. Built for the Base UI styles (base-nova): it composes the Base UI wrappers from components/ui and relies on their render prop and event signatures, so it does not work on Radix or React Aria styles. Base UI port of AI Elements checkpoint. CheckpointTrigger composes TooltipTrigger with render={<Button />} instead of asChild; it works without a TooltipProvider (wrap one to share delay). Base UI tooltips are visual only, so CheckpointTrigger mirrors the tooltip text into a visually hidden description referenced by aria-describedby, unless it repeats the button's accessible name, compared as text with whitespace collapsed: a non-empty aria-label, or else the text its children render (strings and numbers, read through fragments and plain elements, so `Checkpoint {index}` counts). The description stays when there is no aria-label and a child is a component (an icon included), since a component's output cannot be read while rendering, and whenever aria-labelledby names the button; your own aria-describedby wins. Separator is Base UI's: always role=separator, no `decorative` prop. Export names and props are unchanged.",
      "categories": [
        "ai",
        "chat"
      ],
      "type": "registry:component"
    },
    {
      "name": "code-block",
      "title": "Code Block",
      "author": "Jamie Thompson <jamie@jamiethompson.design> (https://jamiethompson.design)",
      "description": "Syntax-highlighted code block for AI chat and agent UIs, rendered with shiki (github-light-high-contrast/github-dark-high-contrast, lazy-loaded per language, token cache, one highlight per snippet). Composes a header with filename/title, a copy-to-clipboard button with a checkmark timeout, a language selector built on Select, optional line numbers, and a body that shows raw text instantly and swaps in highlighted tokens when the highlighter resolves; a block that overflows becomes a keyboard-focusable, labeled scroll container. Use it for model-generated code, tool inputs/outputs, and snippets inside Response or Tool; use CodeBlockContent/CodeBlockContainer to build custom layouts.",
      "dependencies": [
        "cn",
        "lucide-react",
        "shiki@^4.4"
      ],
      "registryDependencies": [
        "button",
        "select"
      ],
      "files": [
        {
          "path": "registry/ai/code-block.tsx",
          "type": "registry:component",
          "target": "components/ai/code-block.tsx"
        }
      ],
      "docs": "Derived from Vercel AI Elements (Apache-2.0, Copyright 2023 Vercel, Inc.); ported to Base UI and maintained by uifiles. Built for the Base UI styles (base-nova): it composes the Base UI wrappers from components/ui and relies on their render prop and event signatures, so it does not work on Radix or React Aria styles. Ported to Base UI. CodeBlockLanguageSelector is Base UI Select.Root and is generic over the value: onValueChange is (value: Value | null, eventDetails) instead of (value: string), so a useState<string | null> setter or a literal-union state type-checks without casts (guard null before narrowing); pass `items` (Record<value, label>) to Select so CodeBlockLanguageSelectorValue renders the label instead of the raw value. CodeBlockLanguageSelectorContent forwards side/sideOffset/align/alignOffset to the positioner; Radix `position=\"popper\"` is `alignItemWithTrigger={false}` (the wrapper defaults to true). Copy button is the Base UI Button (use `render` instead of `asChild`). Header background is bg-muted/50 (upstream bg-muted/80) so 12px muted-foreground text meets 4.5:1 in the light theme. uifiles changes: a block whose code overflows becomes a focusable scroll container (tabIndex 0, role=\"group\", not a landmark, so two overflowing blocks on one page do not trip axe landmark-unique) named by `aria-label` (default \"<Language> code\" from shiki's display name, e.g. \"TypeScript code\" for `typescript` or `ts`, and \"Code\" for text/plaintext or an unknown language; `aria-label` on CodeBlock is routed to that container, not the wrapper); an unknown or empty `language` renders as plain text instead of rejecting, checked as an own key of shiki's bundled languages so names that exist on Object.prototype (`constructor`, `toString`, `hasOwnProperty`) count as unknown too (one console.warn per unknown language, none for an empty one); `code` that is undefined/null renders as empty; CRLF input renders as the same lines before and after highlighting; the token cache is keyed on the full code and the language (NUL-separated, so a colon in a fence info string cannot collide), one highlight runs per snippet, and highlightCode invokes its callback synchronously on a cache hit; every block on the page shares one Shiki highlighter, created with both themes on first use, and each grammar loads on demand the first time its language appears (a failed start or grammar load is retried by the next block that needs it), so a transcript in many languages loads the themes once and never trips Shiki's console warning about ten or more highlighter instances (upstream creates a highlighter per language string, so `ts` and `typescript` count twice); line numbers use the full muted-foreground color and the gutter width follows the digit count.",
      "categories": [
        "ai",
        "chat",
        "code"
      ],
      "type": "registry:component"
    },
    {
      "name": "confirmation",
      "title": "Confirmation",
      "author": "Jamie Thompson <jamie@jamiethompson.design> (https://jamiethompson.design)",
      "description": "Human-in-the-loop approval prompt for AI tool calls, rendered as an Alert. Reads the tool part's state and approval object and shows exactly one of: the pending request with Approve/Reject action buttons (approval-requested), the accepted outcome (approval-responded, output-available or output-error with approved: true), or the rejected outcome (approval-responded or output-denied with approved: false); renders nothing while input is streaming, before an approval exists, or when a responded part carries no decision. Use it with useChat addToolApprovalResponse for tools that require user consent.",
      "dependencies": [
        "ai@^7",
        "cn"
      ],
      "registryDependencies": [
        "alert",
        "button"
      ],
      "files": [
        {
          "path": "registry/ai/confirmation.tsx",
          "type": "registry:component",
          "target": "components/ai/confirmation.tsx"
        }
      ],
      "docs": "Derived from Vercel AI Elements (Apache-2.0, Copyright 2023 Vercel, Inc.); ported to Base UI and maintained by uifiles. Built for the Base UI styles (base-nova): it composes the Base UI wrappers from components/ui and relies on their render prop and event signatures, so it does not work on Radix or React Aria styles. No Radix primitives upstream; only imports changed. ConfirmationAction is the Base UI Button: use `render` instead of `asChild` (add nativeButton={false} when the rendered element is not a <button>), and its className is merged with the default h-8 px-3 text-sm through cn instead of replacing it. ConfirmationRejected wraps its children in a <span className=\"text-destructive\"> (upstream returned them bare in the foreground color) so the rejected outcome reads differently from the accepted one; pass className to adjust, and keep the children phrasing content. ConfirmationAccepted also shows for output-error, because ai@7 keeps approval.approved === true on an approved tool whose execution then failed (the error itself is ToolOutput's to show). Confirmation renders nothing, rather than an empty role=\"alert\" box, when a responded state carries no approved boolean.",
      "categories": [
        "ai",
        "chat",
        "agent"
      ],
      "type": "registry:component"
    },
    {
      "name": "context",
      "title": "Context",
      "author": "Jamie Thompson <jamie@jamiethompson.design> (https://jamiethompson.design)",
      "description": "Context-window usage indicator for AI chat: a ghost button showing percent used with a ring icon that opens a hover card with a progress bar, token counts (input, output, reasoning, cached) and a USD cost estimated by tokenlens from the model id, with reasoning billed as output and cached reads at the cache rate so the rows add up to the total. Use in a chat toolbar or prompt-input footer to show how much of the model's context a conversation has consumed.",
      "dependencies": [
        "ai@^7",
        "cn",
        "tokenlens@^1"
      ],
      "registryDependencies": [
        "button",
        "hover-card",
        "progress"
      ],
      "files": [
        {
          "path": "registry/ai/context.tsx",
          "type": "registry:component",
          "target": "components/ai/context.tsx"
        }
      ],
      "docs": "Derived from Vercel AI Elements (Apache-2.0, Copyright 2023 Vercel, Inc.); ported to Base UI and maintained by uifiles. Built for the Base UI styles (base-nova): it composes the Base UI wrappers from components/ui and relies on their render prop and event signatures, so it does not work on Radix or React Aria styles. Base UI Preview Card puts hover delays on the trigger, not the root: Context no longer accepts openDelay/closeDelay; pass delay/closeDelay to ContextTrigger instead (both default 0). ContextTrigger composes HoverCardTrigger via render (a custom child element still replaces the default button, as upstream). Context's onOpenChange receives (open, eventDetails). ai@7 LanguageModelUsage moved fields: reasoning tokens are read from usage.outputTokenDetails.reasoningTokens and cached tokens from usage.inputTokenDetails.cacheReadTokens (upstream read usage.reasoningTokens / usage.cachedInputTokens). The Progress bar carries aria-label=\"Context window usage\" (upstream had no accessible name), and ContextContentFooter's label uses text-secondary-foreground for AA contrast on bg-secondary. Costs: the rows partition the usage so they add up to the footer. Input = inputTokens - cacheReadTokens at the input rate, Cache = cacheReadTokens at the cache-read rate, Output = outputTokens - reasoningTokens at the output rate, Reasoning = reasoningTokens at the output rate (tokenlens prices reasoning separately only for models with a reasoning rate; providers bill it as output). Each row is rounded to cents and Total cost is the sum of the rounded rows; a model tokenlens cannot price, or no modelId, shows $0.00. A row with zero tokens is omitted. usedTokens or maxTokens of 0, undefined, NaN, Infinity or a negative number (model metadata not loaded yet, or a JS consumer) renders as 0 in the header counts and as 0% in the trigger, progress bar and ring, never NaN or ∞; a usage field that is undefined, NaN or negative counts as 0 and its row is omitted. Numbers and currency use the fixed en-US locale (USD).",
      "categories": [
        "ai",
        "chat"
      ],
      "type": "registry:component"
    },
    {
      "name": "image",
      "title": "Image",
      "author": "Jamie Thompson <jamie@jamiethompson.design> (https://jamiethompson.design)",
      "description": "Renders an AI-generated image (base64 plus media type from the AI SDK GeneratedFile / experimental_generateImage result) as a responsive, rounded <img> with a data: URL. Use it to display model-generated images in chat messages; alt is required and className passes through.",
      "dependencies": [
        "ai@^7",
        "cn"
      ],
      "registryDependencies": [],
      "files": [
        {
          "path": "registry/ai/image.tsx",
          "type": "registry:component",
          "target": "components/ai/image.tsx"
        }
      ],
      "docs": "Derived from Vercel AI Elements (Apache-2.0, Copyright 2023 Vercel, Inc.); ported to Base UI and maintained by uifiles. Built for the Base UI styles (base-nova); the file imports no style wrappers, so nothing in it is style-specific. ImageProps extends `GeneratedFile` from ai@7 (Experimental_GeneratedImage is a deprecated alias slated for removal in v8; the shape is identical) and, unlike upstream, requires `alt` (pass \"\" only for a decorative image). base64 and mediaType become the data: src; uint8Array and providerMetadata are stripped, so only alt, className and src reach the <img>. A missing mediaType falls back to image/png (browsers sniff the real format); missing base64 renders nothing. Spread plain objects only: a DefaultGeneratedFile instance exposes base64 through a prototype getter that object spread does not copy, so pass base64/mediaType/uint8Array explicitly. Uses a plain <img> (biome noImgElement suppressed) because the source is an inline data: URL.",
      "categories": [
        "ai",
        "chat",
        "media"
      ],
      "type": "registry:component"
    },
    {
      "name": "inline-citation",
      "title": "Inline Citation",
      "author": "Jamie Thompson <jamie@jamiethompson.design> (https://jamiethompson.design)",
      "description": "Inline citation badge for AI-generated text: a hostname pill such as \"ai-sdk.dev +2\" placed after a passage that opens a source card on hover, keyboard focus or a click/tap, containing a carousel of sources (title, URL, description, quote) with prev/next paging and an index. Use inside streamed responses to attribute claims to web or document sources.",
      "dependencies": [
        "cn",
        "lucide-react"
      ],
      "registryDependencies": [
        "badge",
        "carousel",
        "hover-card"
      ],
      "files": [
        {
          "path": "registry/ai/inline-citation.tsx",
          "type": "registry:component",
          "target": "components/ai/inline-citation.tsx"
        }
      ],
      "docs": "Derived from Vercel AI Elements (Apache-2.0, Copyright 2023 Vercel, Inc.); ported to Base UI and maintained by uifiles. Built for the Base UI styles (base-nova): it composes the Base UI wrappers from components/ui and relies on their render prop and event signatures, so it does not work on Radix or React Aria styles. Base UI Preview Card puts hover delays on the trigger, not the root: InlineCitationCard no longer accepts openDelay/closeDelay; pass delay/closeDelay to InlineCitationCardTrigger instead (both default 0). InlineCitationCardTrigger composes HoverCardTrigger via render={<Badge render={<button type=\"button\" />} />} (no asChild): the badge is a real button, so it is in the Tab order and focus opens the card; remaining props (including onClick, which runs first and can preventDefault the toggle) still go to the Badge. InlineCitationCard keeps the open state itself (open/defaultOpen/onOpenChange still work and a press reports reason \"trigger-press\"): hover and keyboard focus peek at the card as in Base UI Preview Card, and a press on the badge (click, tap, Enter, Space) opens or pins it and moves focus to InlineCitationCardBody, where Tab and Shift+Tab cycle through the card's controls; a pinned card stays open when the pointer leaves and closes on Escape (focus returns to the badge without reopening it, also when the control that had focus inside the card went away), on an outside press or on a second press. With a controlled `open`, a press pins the card only if your `onOpenChange` opens it in the same update; a press you refuse leaves nothing behind, and a card you close through `open` drops its pin, so the next time you open it, it is the non-modal peek. Base UI Preview Card adds no ARIA to its trigger, so the badge carries aria-expanded and, while the card is open, aria-controls pointing at InlineCitationCardBody (an id you pass to the body is followed). A peek on hover or focus stays non-modal; a pinned card is role=\"dialog\", named by the badge through aria-labelledby (an aria-label or aria-labelledby you pass to the body wins), and shows a focus ring when keyboard focus lands on it. Upstream's Radix HoverCard has no press or touch path. A source that is not an absolute URL (relative path, bare host) is shown as given instead of throwing (try/catch, no URL.canParse, so Safari before 17 renders). InlineCitationCardBody is typed as the hover-card content props, so side/align/sideOffset/alignOffset type-check. InlineCitationCarouselPrev/Next are aria-disabled (not disabled) and dimmed at the first/last slide unless opts={{ loop: true }}, so a keyboard user who pages to an end keeps focus on the arrow and both arrows stay in the Tab order; a click there does nothing, and a disabled or aria-disabled prop you pass wins. They have a 24px hit area and a focus ring; InlineCitationCarouselIndex follows slides added while streaming and reads 0/0 with none. A setApi passed to InlineCitationCarousel is called alongside the internal one. InlineCitationSource and InlineCitationQuote render nothing when they have no content (undefined, an empty or whitespace-only string, an empty array, or only null/boolean children). InlineCitationSource renders its title as <p> instead of <h4> so a hover card never breaks the page's heading order. InlineCitationCarouselIndex uses text-secondary-foreground on the bg-secondary header for AA contrast.",
      "categories": [
        "ai",
        "chat"
      ],
      "type": "registry:component"
    },
    {
      "name": "model-selector",
      "title": "Model Selector",
      "author": "Jamie Thompson <jamie@jamiethompson.design> (https://jamiethompson.design)",
      "description": "Searchable model picker for AI chat: a dialog opened from any trigger that hosts a cmdk command palette with a whole-term search input, grouped items, separators, keyboard-shortcut hints, an announced empty state, and decorative provider logos from models.dev or a custom src (ModelSelectorLogo, ModelSelectorLogoGroup, ModelSelectorName). Use to switch the model behind a conversation or prompt input.",
      "dependencies": [
        "cn"
      ],
      "registryDependencies": [
        "command",
        "dialog"
      ],
      "files": [
        {
          "path": "registry/ai/model-selector.tsx",
          "type": "registry:component",
          "target": "components/ai/model-selector.tsx"
        }
      ],
      "docs": "Derived from Vercel AI Elements (Apache-2.0, Copyright 2023 Vercel, Inc.); ported to Base UI and maintained by uifiles. Built for the Base UI styles (base-nova): it composes the Base UI wrappers from components/ui and relies on their render prop and event signatures, so it does not work on Radix or React Aria styles. Base UI Dialog: ModelSelectorTrigger takes render={<Button />} instead of asChild, and ModelSelector's onOpenChange receives (open, eventDetails). ModelSelectorContent is the Base UI Dialog Popup, so initialFocus/finalFocus replace onOpenAutoFocus/onCloseAutoFocus. ModelSelectorContent filters by whole terms (every whitespace-separated term of the query must appear in the item value or its keywords, case-insensitive) instead of cmdk's fuzzy match; pass filter (for example defaultFilter from \"cmdk\") or shouldFilter={false} to change that, and give ModelSelectorItem keywords for names that differ from the value. ModelSelectorEmpty placed inside ModelSelectorList renders after the listbox in a polite live region, and ModelSelectorSeparator is decorative (aria-hidden), because a listbox may not own a message or a separator. ModelSelectorLogo is decorative (alt=\"\", aria-hidden) unless you pass alt; by default it loads https://models.dev/logos/<provider>.svg at runtime (a third-party request from the user's browser, blocked by an img-src 'self' CSP or offline) and hides itself when the image fails to load (onError still fires); pass src to serve a self-hosted file or a data URI instead, as the docs preview does with inline placeholder marks so it makes no external request. ModelSelectorDialog wraps the base-nova CommandDialog (title, description, className, showCloseButton). The base-nova command input is an InputGroup with a forced h-8, so ModelSelectorContent also sets **:data-[slot=input-group]:h-auto! to keep upstream's taller py-3.5 input. The file is a client module (\"use client\").",
      "categories": [
        "ai",
        "chat"
      ],
      "type": "registry:component"
    },
    {
      "name": "plan",
      "title": "Plan",
      "author": "Jamie Thompson <jamie@jamiethompson.design> (https://jamiethompson.design)",
      "description": "Card-shaped, collapsible plan an agent proposes before acting: a header with title, description and a toggle trigger, collapsible content for the steps, and a footer for approve or edit actions. isStreaming renders the title and description with shadcn's shimmer utility while the plan is still being generated. Composes shadcn card, collapsible and button.",
      "dependencies": [
        "cn",
        "lucide-react"
      ],
      "registryDependencies": [
        "button",
        "card",
        "collapsible"
      ],
      "files": [
        {
          "path": "registry/ai/plan.tsx",
          "type": "registry:component",
          "target": "components/ai/plan.tsx"
        }
      ],
      "docs": "Derived from Vercel AI Elements (Apache-2.0, Copyright 2023 Vercel, Inc.); ported to Base UI and maintained by uifiles. Built for the Base UI styles (base-nova): it composes the Base UI wrappers from components/ui and relies on their render prop and event signatures, so it does not work on Radix or React Aria styles. Base UI port of AI Elements plan. Plan renders the Collapsible as the Card via render={<Card />}; PlanContent and PlanTrigger use render the same way (all three were asChild). PlanContent takes CardContent props as upstream, so Collapsible.Panel props such as keepMounted are not forwarded. Upstream's <Shimmer> component is replaced by shadcn's `shimmer` class on a <span>, so there is no shimmer item or motion dependency. PlanTrigger spreads its props onto the collapsible trigger; className is merged into the inner Button and re-run through cn, so overrides still win, and a `render` prop replaces the Button. PlanTitle and PlanDescription are <div>s (CardTitle/CardDescription), so a plan never affects the page's heading order. Plan is closed by default (as upstream); pass defaultOpen. onOpenChange receives (open, eventDetails).",
      "categories": [
        "ai",
        "chat",
        "agent"
      ],
      "type": "registry:component"
    },
    {
      "name": "prompt-input",
      "title": "Prompt Input",
      "author": "Jamie Thompson <jamie@jamiethompson.design> (https://jamiethompson.design)",
      "description": "Chat composer for AI apps: an auto-growing textarea that submits on Enter (Shift+Enter for newlines), a footer/header tools row, an attachment action menu (add files, take screenshot), model select, hover-card and command-palette slots, and a submit button that turns into a stop button while submitted or streaming when onStop is provided. Handles file attachments via drag-drop, paste, and file dialog with accept/maxFiles/maxFileSize validation, converts blob URLs to data URLs on submit, and can lift its state into an optional PromptInputProvider. Use it as the input at the bottom of any AI SDK useChat conversation. Composes input-group, dropdown-menu, select, tooltip, hover-card, command, spinner.",
      "dependencies": [
        "ai@^7",
        "cn",
        "lucide-react",
        "nanoid@^6"
      ],
      "registryDependencies": [
        "command",
        "dropdown-menu",
        "hover-card",
        "input-group",
        "select",
        "spinner",
        "tooltip"
      ],
      "files": [
        {
          "path": "registry/ai/prompt-input.tsx",
          "type": "registry:component",
          "target": "components/ai/prompt-input.tsx"
        }
      ],
      "docs": "Derived from Vercel AI Elements (Apache-2.0, Copyright 2023 Vercel, Inc.); ported to Base UI and maintained by uifiles. Built for the Base UI styles (base-nova): it composes the Base UI wrappers from components/ui and relies on their render prop and event signatures, so it does not work on Radix or React Aria styles. Ported from Radix to Base UI. API changes vs AI Elements: (1) PromptInputActionAddScreenshot takes `onClick` instead of Radix `onSelect`; calling `event.preventDefault()` in it skips the capture, but the menu still closes (pass `closeOnClick={false}` to keep it open). Capture failures other than a denied or canceled picker go to PromptInput's `onError` as `{ code: \"screenshot\", message }`, or to `console.error` when there is no handler. (2) PromptInputActionAddAttachments and PromptInputActionMenuItem are Base UI Menu items: use `onClick` (not `onSelect`) and `closeOnClick={false}` to keep the menu open. PromptInputActionAddAttachments calls your `onClick` first, skips the file dialog if it called `preventDefault()`, and sets `closeOnClick={false}` by default to mirror upstream's preventDefault; override it with `closeOnClick`. (3) PromptInputHoverCard still accepts `openDelay`/`closeDelay` (default 0) but they are applied to PromptInputHoverCardTrigger via context because Base UI PreviewCard puts delays on the trigger; passing `delay`/`closeDelay` directly on the trigger overrides them. (4) PromptInputSelect is Base UI Select: `onValueChange` receives `(value, eventDetails)` and the value may be `null`; PromptInputSelectValue renders the raw value unless you pass `items={[{ label, value }]}` on PromptInputSelect (or a children function). (5) `PromptInputButton`/`PromptInputSubmit` `onClick` receive Base UI's event type (a superset of React.MouseEvent). Behavior fixed on top of upstream: (6) PromptInputSubmit is a Stop button (named \"Stop\", `type=\"button\"`, a press calls `onStop`, the square glyph while streaming) only while `status` is submitted/streaming and `onStop` is passed; without `onStop` a press still submits, so it keeps the Submit name, type and glyph, and only the spinner shows while submitted (upstream names it Stop and shows the square either way). Enter submits only through an enabled `button[type=\"submit\"]` inside the form, so while PromptInputSubmit shows Stop Enter does nothing and the draft is kept; a disabled or missing submit button blocks Enter the same way. (7) `accept` understands `.ext` patterns (matched case-insensitively against the file name, so files the OS reports with an empty type still match), `type/*` and `*/*`; when only some files of a batch pass `accept` or `maxFileSize`, the rest are dropped and reported through `onError`, like `max_files`; `maxFiles` is checked against the attachment list as `add()`, `remove()` and `clear()` leave it, before React re-renders, in local and in provider mode: several `add()` calls in one handler cannot exceed it, and a `remove()` or `clear()` followed by `add()` in the same handler (swapping a file) gets the freed slot, also when the removal goes through `useProviderAttachments()`. (8) `onError` codes are `\"max_files\" | \"max_file_size\" | \"accept\" | \"screenshot\"`; the `PromptInputError` type is exported. (9) `onSubmit` may return `false` (directly or from its promise) to reject a submit. A submit clears the text as it starts, also text held by PromptInputProvider (upstream clears the provider's text only once `onSubmit` succeeds, so a second submit in the meantime sent it again and text typed meanwhile was wiped). When it throws, rejects or returns `false`, an uncontrolled PromptInputTextarea or the provider gets its text back unless you typed since, and attachments and referenced sources are kept (as upstream); a textarea you control through `value` is left to you. Once a submit is accepted, only the attachments and referenced sources it carried are cleared, so a file attached while an async `onSubmit` is still pending stays. A submit leaves out attachments an earlier submit is still carrying (converting blob URLs, or waiting on an async `onSubmit`), so a double press or a second submit never sends the same file twice; if the earlier submit is rejected the files are offered to the next one. `onSubmit` is called in the order the submits were made, even when an earlier one is still converting its attachments; a chat page should still not return `sendMessage`'s promise from `onSubmit` (see @uifiles/chat). (10) `syncHiddenInput` is accepted for parity, but browsers cannot set a file input's value: it only clears the hidden input when the attachment list empties, and native form posts never carry the attachments. (11) PromptInputTextarea has `aria-label=\"Message\"` by default; override it via props, or pass `aria-label={undefined}` to defer to a visible `<label htmlFor>` or `aria-labelledby`, since `aria-label` outranks a native label. (12) PromptInputButton tooltips open immediately and, because Base UI tooltips carry no ARIA, their text is mirrored into a visually hidden `aria-describedby` description unless it equals the button's `aria-label` or its visible text; an empty tooltip (`\"\"` or `{ content: \"\" }`) renders no tooltip and no description. A tooltip `shortcut` is also set as `aria-keyshortcuts` on the button, so it is not lost when the description is skipped; symbols and words are translated to key names (`\"⌘K\"` → `\"Meta+K\"`, `\"Ctrl+Shift+R\"` → `\"Control+Shift+R\"`, `\"⌘⇧↵\"` → `\"Meta+Shift+Enter\"`, `\"Esc\"` → `\"Escape\"`), and an `aria-keyshortcuts` prop you pass wins. (13) PromptInputTools wraps and PromptInputSubmit never shrinks, so a long toolbar does not overlap the submit button at phone widths; clicking the empty surface of PromptInputHeader/PromptInputFooter focuses the textarea (not the model select), while clicks on anything that takes focus or handles clicks itself (buttons, links, form controls, elements with a `tabindex` other than -1, `contenteditable`, labels and ARIA widget roles) leave focus where they put it.",
      "categories": [
        "ai",
        "chat"
      ],
      "type": "registry:component"
    },
    {
      "name": "queue",
      "title": "Queue",
      "author": "Jamie Thompson <jamie@jamiethompson.design> (https://jamiethompson.design)",
      "description": "Queued work panel for agent chat: collapsible sections with a count label, a scrollable list of items with a status indicator, strike-through completed state, a description line, image and file attachments, and action buttons revealed on hover, keyboard focus, or always on coarse pointers. Use it for pending todos and queued messages beside a prompt input. Composes shadcn collapsible, scroll-area and button; exports QueueTodo, QueueMessage and QueueMessagePart types.",
      "dependencies": [
        "cn",
        "lucide-react"
      ],
      "registryDependencies": [
        "button",
        "collapsible",
        "scroll-area"
      ],
      "files": [
        {
          "path": "registry/ai/queue.tsx",
          "type": "registry:component",
          "target": "components/ai/queue.tsx"
        }
      ],
      "docs": "Derived from Vercel AI Elements (Apache-2.0, Copyright 2023 Vercel, Inc.); ported to Base UI and maintained by uifiles. Built for the Base UI styles (base-nova): it composes the Base UI wrappers from components/ui and relies on their render prop and event signatures, so it does not work on Radix or React Aria styles. Base UI port of AI Elements queue. QueueSectionTrigger is the Collapsible trigger's own native <button> (upstream wrapped a <button> in asChild); its chevron rotates on the trigger's data-panel-open attribute. QueueSectionContent unmounts when closed (Base UI Panel default; pass keepMounted to retain). QueueList is a Base UI ScrollArea, so Radix `type` and `scrollHideDelay` props do not exist. onOpenChange receives (open, eventDetails). Contrast changes: completed items use full-alpha text-muted-foreground with line-through instead of upstream's 50% and 40% alpha, which fail AA; QueueItemIndicator uses a full-alpha border-muted-foreground (pending hollow, completed filled with bg-muted-foreground) instead of upstream's 50% and 20% borders, which fall below 3:1. QueueItemAction is revealed on row hover, on keyboard focus (focus-visible and group-focus-within), and always on coarse pointers (pointer-coarse:opacity-100), because Tailwind gates the hover variant on (hover: hover) and a phone has neither hover nor a Tab key. QueueItemContent clamps titles to two lines (upstream one) so a phone-width title is not cut off after a few words; the clamp cuts whole lines. QueueItemFile truncates the file name at 100px. Both the chip and the item title mirror the whole text in a title attribute when their children are a string (your own title wins), so a truncated file name, or a title that WCAG 1.4.12 text spacing pushes onto a third line, stays readable on hover.",
      "categories": [
        "ai",
        "chat",
        "agent"
      ],
      "type": "registry:component"
    },
    {
      "name": "reasoning",
      "title": "Reasoning",
      "author": "Jamie Thompson <jamie@jamiethompson.design> (https://jamiethompson.design)",
      "description": "Collapsible \"thinking\" block for AI reasoning/thought tokens. ReasoningTrigger shows a shimmering \"Thinking...\" while isStreaming, then \"Thought for N seconds\" (duration measured automatically or passed in); ReasoningContent renders the reasoning text as markdown with Streamdown. Auto-opens when streaming starts and auto-closes one second after it ends; defaultOpen={false} disables the auto-open, a reader's manual toggle is respected, and a controlled parent receives both requests through onOpenChange. Use it above a message response when the model exposes reasoning.",
      "dependencies": [
        "cn",
        "lucide-react"
      ],
      "registryDependencies": [
        "collapsible",
        "@uifiles/response"
      ],
      "files": [
        {
          "path": "registry/ai/reasoning.tsx",
          "type": "registry:component",
          "target": "components/ai/reasoning.tsx"
        }
      ],
      "docs": "Derived from Vercel AI Elements (Apache-2.0, Copyright 2023 Vercel, Inc.); ported to Base UI and maintained by uifiles. Built for the Base UI styles (base-nova): it composes the Base UI wrappers from components/ui and relies on their render prop and event signatures, so it does not work on Radix or React Aria styles. Ported to Base UI. @radix-ui/react-use-controllable-state is replaced with a useState controlled/uncontrolled pattern for both open and duration (props unchanged); the latest onOpenChange is kept in a ref so a parent that re-renders with an inline handler does not restart the auto-close timer. The AI Elements <Shimmer> dependency is gone: the thinking label is a <span className=\"shimmer [--shimmer-duration:1s]\"> using the shimmer utility from shadcn/tailwind.css, and the finished label is a <span> (upstream used <p>, which is invalid inside a button). Collapsible content selectors use data-open/data-closed instead of data-[state=...]. onOpenChange receives only (open) as upstream; Base UI's eventDetails argument is dropped. Auto-open fires only when isStreaming turns true (upstream re-opened the panel on every render while streaming, so it could not be closed mid-stream); a manual toggle takes the panel out of the auto-open/auto-close cycle until the next stream starts, and each new stream auto-opens and auto-closes once (upstream never auto-closed a second stream). The measured duration is at least 1 second, so a stream that starts and ends within the same millisecond shows \"Thought for 1 seconds\" rather than upstream's permanent \"Thinking...\" (duration 0 is the trigger's streaming sentinel; pass duration={0} only to mean still thinking). A controlled parent receives the auto-open and auto-close requests through onOpenChange. ReasoningContent renders the reasoning text with MessageResponse from @uifiles/response (installed with this item) instead of upstream's bare Streamdown, so reasoning content is rendered by Message Response and shares its behavior: the cjk, code, math and mermaid plugins; the github-light-high-contrast/github-dark-high-contrast shiki pair (Streamdown's default GitHub light theme fails AA on orange tokens); its named scroll regions (a code block, table or display formula that overflows becomes a keyboard tab stop, role=\"group\" named \"Code\", \"Table\" or \"Math\"); its KaTeX requirement (without KaTeX's stylesheet every formula renders twice); and its link-safety dialog (a native modal <dialog> named \"Open external link?\" that takes focus and gives it back to the link). The trigger row is min-h-6 (24px) to meet the WCAG 2.2 target size. CSS: the CLI adds @uifiles/response's stylesheet lines to your globals.css with this item: `@import \"streamdown/styles.css\"`, `@import \"katex/dist/katex.min.css\"` and the `.katex-display` overflow rule. Add `@source \"../node_modules/streamdown/dist/*.js\"` yourself, next to your `@import \"tailwindcss\"` and with the path relative to your CSS file, so Streamdown's Tailwind classes compile. See @uifiles/response for what each line does.",
      "categories": [
        "ai",
        "chat"
      ],
      "type": "registry:component"
    },
    {
      "name": "response",
      "title": "Message Response",
      "author": "Jamie Thompson <jamie@jamiethompson.design> (https://jamiethompson.design)",
      "description": "Markdown renderer for streamed AI assistant output. MessageResponse wraps Streamdown with the cjk, code (Shiki), math (KaTeX) and mermaid plugins, tolerates incomplete markdown while tokens arrive, and is memoized on children/isAnimating so a chat list does not re-render every block on each token. Use it as the body of an assistant message; accepts every Streamdown prop.",
      "dependencies": [
        "cn",
        "katex@^0.16",
        "streamdown@^2.6",
        "@streamdown/cjk@^1",
        "@streamdown/code@^1",
        "@streamdown/math@^1",
        "@streamdown/mermaid@^1"
      ],
      "registryDependencies": [],
      "files": [
        {
          "path": "registry/ai/response.tsx",
          "type": "registry:component",
          "target": "components/ai/response.tsx"
        }
      ],
      "css": {
        "@import \"streamdown/styles.css\"": {},
        "@import \"katex/dist/katex.min.css\"": {},
        "@layer base": {
          ".katex-display": {
            "overflow": "auto hidden",
            "padding-block": "0.25em"
          }
        }
      },
      "docs": "Derived from Vercel AI Elements (Apache-2.0, Copyright 2023 Vercel, Inc.); ported to Base UI and maintained by uifiles. Built for the Base UI styles (base-nova); the file imports no style wrappers, so nothing in it is style-specific. Extracted from AI Elements message.tsx (MessageResponse only; shadcn's own message replaces the rest). Export name and props identical (ComponentProps<typeof Streamdown>). No Radix code in this component. MessageResponse passes shikiTheme={[\"github-light-high-contrast\", \"github-dark-high-contrast\"]} to Streamdown because Streamdown's default GitHub light theme fails AA on orange tokens; your own shikiTheme prop overrides it. Code-block bodies, tables (inline, and in the fullscreen table view Streamdown portals to document.body) and display formulas that overflow (horizontally, or vertically past codeBlockMaxHeight / tableMaxHeight; formulas sideways only, and only once the `.katex-display` rule below lets them scroll) become keyboard-focusable scroll regions on the client: tabindex=0 plus role=\"group\" and an aria-label (\"Code\", \"Table\" or \"Math\"; a group, not a region landmark, so several blocks on one page do not collide), re-checked as tokens stream in and on resize, removed again when the content fits, and applied only after React has hydrated them, so server-rendered blocks never cause a hydration mismatch. Links: Streamdown's link safety stays on by default, but its confirmation modal (a role=\"button\" backdrop around the dialog's buttons, which fails axe nested-interactive and never takes focus) is replaced through linkSafety.renderModal by a native modal <dialog>: named by its \"Open external link?\" heading, it makes the page inert, takes focus, shows the whole URL, copies it, closes on Escape, its close button or a backdrop click, returns focus to the link, and uses Streamdown's `translations`. `linkSafety={{ enabled: false }}` renders plain anchors, and your own linkSafety.renderModal or onLinkCheck wins (Streamdown renders renderModal's output inside the link's paragraph, so return phrasing content or a portal). CSS: installing this item adds three things to your globals.css through its `css` field. `@import \"streamdown/styles.css\"` provides the `[data-sd-animate]` fade/blur/slide keyframes and the list-marker fade; without it streamed blocks appear instantly instead of animating in. `@import \"katex/dist/katex.min.css\"` is KaTeX's stylesheet (this item installs katex, which @streamdown/math uses); without it every formula renders twice, KaTeX's HTML plus the MathML fallback the stylesheet hides. The CLI places each import after your last `@import` (or at the top of the file) and skips it when an identical `@import` is already there. `@layer base { .katex-display { overflow: auto hidden; padding-block: 0.25em; } }`: KaTeX gives a display formula no overflow container, so one wider than the column would widen the whole page on a phone (WCAG 1.4.10); with the rule the formula scrolls sideways in its own box and the padding keeps its tall parts (limits, over- and underbraces) from being clipped. Add one line yourself: `@source \"../node_modules/streamdown/dist/*.js\"` next to your `@import \"tailwindcss\"`, with the path relative to your CSS file, because Streamdown's own Tailwind classes only compile if your stylesheet can see them (the `@streamdown/*` plugins ship no classes). If you copy the file by hand, add all four lines. Inline `$...$` math is off by default in @streamdown/math (singleDollarTextMath: false); `$$` blocks work.",
      "categories": [
        "ai",
        "chat"
      ],
      "type": "registry:component"
    },
    {
      "name": "sources",
      "title": "Sources",
      "author": "Jamie Thompson <jamie@jamiethompson.design> (https://jamiethompson.design)",
      "description": "Collapsible list of the sources an AI answer cited. Sources wraps a collapsible; SourcesTrigger shows \"Used N sources\" (singular for one) with a chevron; SourcesContent reveals Source links (book icon + title; absolute URLs open in a new tab). A Source without href renders as plain text. Use under an assistant message to surface citations from web-search or retrieval tool results.",
      "dependencies": [
        "cn",
        "lucide-react"
      ],
      "registryDependencies": [
        "collapsible"
      ],
      "files": [
        {
          "path": "registry/ai/sources.tsx",
          "type": "registry:component",
          "target": "components/ai/sources.tsx"
        }
      ],
      "docs": "Derived from Vercel AI Elements (Apache-2.0, Copyright 2023 Vercel, Inc.); ported to Base UI and maintained by uifiles. Built for the Base UI styles (base-nova): it composes the Base UI wrappers from components/ui and relies on their render prop and event signatures, so it does not work on Radix or React Aria styles. Ported to Base UI Collapsible: SourcesTrigger and SourcesContent accept Base UI Trigger/Panel props (render instead of asChild; keepMounted instead of forceMount). Open/closed animation classes use data-open/data-closed instead of data-[state=open|closed]. Sources types `open`, `defaultOpen`, `onOpenChange` and `disabled` (Base UI root props) on top of upstream's div props. Divergences from upstream: the trigger pluralizes (\"Used 1 source\") and is a 24px-tall target; Source without `href` renders a <span> rather than an anchor; absolute URLs get target=\"_blank\" rel=\"noreferrer noopener\" while relative links open in the same tab; a missing or empty `title` falls back to the URL's hostname (or the href itself when it has none), and empty `children` fall back to the icon and title, so a provider's empty title never yields an unlabeled link.",
      "categories": [
        "ai",
        "chat"
      ],
      "type": "registry:component"
    },
    {
      "name": "suggestion",
      "title": "Suggestion",
      "author": "Jamie Thompson <jamie@jamiethompson.design> (https://jamiethompson.design)",
      "description": "Horizontally scrolling row of pill-shaped suggestion buttons (\"Summarize this repo\", \"Explain the auth flow\") shown under an empty conversation or above the prompt input to give users one-click starting prompts. Suggestions wraps children in a hidden-scrollbar scroll area; Suggestion is an outline button that calls onClick with its suggestion string. Pair with prompt-input to prefill the textarea. Composes button and scroll-area.",
      "dependencies": [
        "cn"
      ],
      "registryDependencies": [
        "button",
        "scroll-area"
      ],
      "files": [
        {
          "path": "registry/ai/suggestion.tsx",
          "type": "registry:component",
          "target": "components/ai/suggestion.tsx"
        }
      ],
      "docs": "Derived from Vercel AI Elements (Apache-2.0, Copyright 2023 Vercel, Inc.); ported to Base UI and maintained by uifiles. Built for the Base UI styles (base-nova): it composes the Base UI wrappers from components/ui and relies on their render prop and event signatures, so it does not work on Radix or React Aria styles. Same API as AI Elements. Built on the Base UI ScrollArea and Button wrappers; Suggestions accepts ScrollArea.Root props (Radix-only props such as `type` and `scrollHideDelay` no longer exist). Suggestion scrolls itself fully into view when it receives focus (browsers only do that for a chip that is entirely hidden, not one cut off at the row's edge) and still calls the `onFocus` you pass.",
      "categories": [
        "ai",
        "chat"
      ],
      "type": "registry:component"
    },
    {
      "name": "task",
      "title": "Task",
      "author": "Jamie Thompson <jamie@jamiethompson.design> (https://jamiethompson.design)",
      "description": "Collapsible task log for agent activity: a search-icon trigger with a title and rotating chevron, and an indented list of TaskItem rows with inline TaskItemFile chips for filenames. Use it inside a chat message to show what an agent did in a step, such as files read or searches run. Open by default; composes shadcn collapsible.",
      "dependencies": [
        "cn",
        "lucide-react"
      ],
      "registryDependencies": [
        "collapsible"
      ],
      "files": [
        {
          "path": "registry/ai/task.tsx",
          "type": "registry:component",
          "target": "components/ai/task.tsx"
        }
      ],
      "docs": "Derived from Vercel AI Elements (Apache-2.0, Copyright 2023 Vercel, Inc.); ported to Base UI and maintained by uifiles. Built for the Base UI styles (base-nova): it composes the Base UI wrappers from components/ui and relies on their render prop and event signatures, so it does not work on Radix or React Aria styles. Base UI port of AI Elements task. TaskTrigger renders the Collapsible trigger's native <button> (upstream rendered a <div> through asChild); custom children render inside that button instead of replacing it, so they must be phrasing content without interactive descendants (no nested buttons or links); `render` substitutes the element (pass nativeButton={false} for a non-button). The default label is a <span> (was <p>, which is invalid inside a button). The trigger row is min-h-6 (24px) to meet the WCAG 2.2 target size. The chevron rotates on the trigger's data-panel-open attribute (was group-data-[state=open]); TaskContent animates on data-open / data-closed and takes Collapsible.Panel props such as keepMounted. onOpenChange receives (open, eventDetails).",
      "categories": [
        "ai",
        "chat",
        "agent"
      ],
      "type": "registry:component"
    },
    {
      "name": "tool",
      "title": "Tool",
      "author": "Jamie Thompson <jamie@jamiethompson.design> (https://jamiethompson.design)",
      "description": "Collapsible tool-call card for AI chat: header with wrench icon, tool name derived from the part type (or toolName for dynamic tools), and a status badge for every AI SDK tool state (input-streaming, input-available, approval-requested, approval-responded, output-available, output-denied, output-error); expandable content showing Parameters (input as JSON) and Result or Error (JSON and strings in Code Blocks, React nodes and primitives rendered as given). Use it to render ToolUIPart and DynamicToolUIPart parts from useChat messages.",
      "dependencies": [
        "ai@^7",
        "cn",
        "lucide-react"
      ],
      "registryDependencies": [
        "badge",
        "collapsible",
        "@uifiles/code-block"
      ],
      "files": [
        {
          "path": "registry/ai/tool.tsx",
          "type": "registry:component",
          "target": "components/ai/tool.tsx"
        }
      ],
      "docs": "Derived from Vercel AI Elements (Apache-2.0, Copyright 2023 Vercel, Inc.); ported to Base UI and maintained by uifiles. Built for the Base UI styles (base-nova): it composes the Base UI wrappers from components/ui and relies on their render prop and event signatures, so it does not work on Radix or React Aria styles. Ported to Base UI. Tool is Base UI Collapsible.Root: onOpenChange is (open, eventDetails); ToolContent is Collapsible.Panel (forceMount → keepMounted). Open-state styling uses data-open/data-closed and group-data-open (root carries data-open) instead of data-[state=open]. Status icon colors use semantic tokens (text-primary for completed/responded, text-destructive for error/denied, text-muted-foreground for awaiting approval) because the theme has no success/warning tokens; the error result container is bg-card text-destructive (upstream bg-destructive/10) to meet WCAG AA contrast with the light-mode destructive token. The \"Parameters\" and \"Result\"/\"Error\" labels are <div>s (upstream <h4>) so a tool card never breaks the page's heading order. ToolInput renders a muted \"No input yet\" placeholder while input is undefined (the shape the AI SDK emits on tool-input-start) instead of crashing, and stringifies BigInt values; anything else JSON cannot serialize falls back to String(). ToolOutput shows falsy results (0, false, \"\") and renders booleans and numbers as text. An unknown tool state renders its raw state text with a neutral icon.",
      "categories": [
        "ai",
        "chat",
        "agent"
      ],
      "type": "registry:component"
    },
    {
      "name": "chat",
      "title": "Chat",
      "author": "Jamie Thompson <jamie@jamiethompson.design> (https://jamiethompson.design)",
      "description": "AI chat block for AI SDK useChat: a MessageScroller transcript that follows streamed output, an Empty state with one-click Suggestions, user text in Bubbles, assistant parts as Message Response (Streamdown), Reasoning, Tool (tool-* and dynamic-tool) and Attachments, a shimmering Marker while a request is pending, an error row with Retry on failure, and a Prompt Input composer with attachments whose submit button becomes Stop while a response is in flight (given onStop). Ships as a composable Chat component (plus ChatMessages, ChatMessage, ChatMessagePart, ChatToolPart, ChatAttachments, ChatEmpty, ChatThinkingMarker, ChatErrorMarker and ChatComposer exports), a scripted demo conversation built with @shadcn/helpers createChat that streams with no API key, and an app/chat page wiring it up. Use it to start a chatbot, assistant or agent UI, then swap the demo transport for your own route.",
      "dependencies": [
        "ai@^7",
        "@ai-sdk/react@^4",
        "@shadcn/helpers@^0.2",
        "cn",
        "lucide-react"
      ],
      "registryDependencies": [
        "attachment",
        "bubble",
        "button",
        "empty",
        "marker",
        "message",
        "message-scroller",
        "@uifiles/prompt-input",
        "@uifiles/reasoning",
        "@uifiles/response",
        "@uifiles/suggestion",
        "@uifiles/tool"
      ],
      "files": [
        {
          "path": "registry/blocks/chat/components/blocks/chat.tsx",
          "type": "registry:component",
          "target": "components/blocks/chat.tsx"
        },
        {
          "path": "registry/blocks/chat/lib/demo-conversation.ts",
          "type": "registry:lib",
          "target": "lib/demo-conversation.ts"
        },
        {
          "path": "registry/blocks/chat/page.tsx",
          "type": "registry:page",
          "target": "app/chat/page.tsx"
        }
      ],
      "docs": "Built for the Base UI styles (base-nova): it composes the base-nova wrappers from components/ui and the @uifiles AI items, which rely on Base UI's render prop and event signatures, so it does not work on Radix or React Aria styles. The page installs at app/chat/page.tsx and streams a scripted conversation through @shadcn/helpers, so it works without an API key. To connect a model, replace `transport` in the page with `new DefaultChatTransport({ api: \"/api/chat\" })` from `ai` and add a route that returns `result.toUIMessageStreamResponse()`. Pass `error` and `onRetry={regenerate}` from `useChat` so a failed request renders a row with its message and a Retry button; when `status` is \"error\" and no `error` is given the row still appears with a generic \"Something went wrong.\" (Retry only when `onRetry` is passed), and a rejected `onRetry` (`regenerate` rejects when there is nothing to regenerate) is reported through `console.error`, never as an unhandled rejection; after an error the composer shows a plain Submit again. While a response is in flight, Enter, the submit button and a programmatic `form.requestSubmit()` submit nothing and the draft and attachments are kept (ChatComposer returns `false` from `onSubmit`); if you pass `onStop` to Chat or ChatComposer (the page passes `stop` from `useChat`) the submit button becomes a Stop button that calls it, and without `onStop` it keeps the Submit name and a press does nothing. The page does not return `sendMessage`'s promise from `onSubmit` (it settles only once the answer has streamed) and ChatComposer does not wait for it either, so sent attachments leave the composer as soon as the message is sent and a file attached while an answer streams stays. If your `onSubmit` returns a promise that rejects, from the composer or a suggestion click, the rejection is reported through `console.error`, never as an unhandled rejection; if it throws synchronously the error is reported the same way and the composer keeps the draft and attachments. Once the chat is no longer generating (after Stop, or a stream that ended early) parts still marked streaming or running are shown settled, and a tool call left in input-available reads Pending; a tool call whose input has not started shows the Tool item's \"No input yet\" placeholder. Approval-requested tool calls are shown but not wired: compose @uifiles/confirmation with `addToolApprovalResponse` from `useChat` to add human-in-the-loop approvals. Chat fills its parent: render it inside a height-constrained container (the page uses h-svh). Suggestions are submitted as-is when clicked. CSS: the CLI adds @uifiles/response's stylesheet lines to your globals.css with this block: `@import \"streamdown/styles.css\"`, `@import \"katex/dist/katex.min.css\"` (without it every formula renders twice) and the `.katex-display` overflow rule. Add `@source \"../node_modules/streamdown/dist/*.js\"` yourself, next to your `@import \"tailwindcss\"` and with the path relative to your CSS file, so Streamdown's Tailwind classes compile. See @uifiles/response for what each line does.",
      "categories": [
        "ai",
        "chat",
        "block"
      ],
      "type": "registry:block"
    }
  ]
}