{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "chat",
  "type": "registry:component",
  "title": "Chat",
  "description": "A subtle 1px outline with 4px corners, a named header, scrollable conversation, and a composer anchored below the history.",
  "registryDependencies": [
    "https://vlak.dev/r/vlak-base.json",
    "https://vlak.dev/r/inter.json",
    "https://vlak.dev/r/conversation.json",
    "https://vlak.dev/r/vlak-lib.json"
  ],
  "dependencies": [
    "@stylexjs/stylex"
  ],
  "devDependencies": [
    "@stylexjs/babel-plugin"
  ],
  "docs": "Vlak leaves are StyleX. Compile them with @stylexjs/babel-plugin (Vite: @stylexjs/unplugin, Next: @stylexjs/nextjs-plugin). If you would rather not run a compiler, import @noorddev/vlak-react instead: it ships precompiled with one stylesheet.",
  "files": [
    {
      "path": "vlak/chat.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport * as stylex from \"@stylexjs/stylex\";\nimport { vlak, mq } from \"./tokens.stylex\";\nimport { rs } from \"./rs\";\nimport { Conversation } from \"./conversation\";\n\nexport interface ChatProps extends Omit<React.HTMLAttributes<HTMLElement>, \"title\"> {\n  /** Visible name of this chat and its region. */\n  title: React.ReactNode;\n  /** Context or status below the title. */\n  description?: React.ReactNode;\n  /** Application-owned controls in the header. */\n  actions?: React.ReactNode;\n  /** Messages rendered inside the conversation history. */\n  children?: React.ReactNode;\n  /** An application-owned message composer, outside the scrolling history. */\n  composer: React.ReactNode;\n  /** Supporting text below the composer. */\n  footer?: React.ReactNode;\n  /** Accessible name of the scrollable message history. */\n  conversationLabel?: string;\n  /** Height reserved for the message history, including its jump control. */\n  historyHeight?: React.CSSProperties[\"height\"];\n  /** Follow new content while the reader is at the end. */\n  autoScroll?: boolean;\n}\n\nconst styles = stylex.create({\n  root: {\n    boxSizing: \"border-box\", display: \"flex\", flexDirection: \"column\", width: \"100%\", minWidth: 0, minHeight: 0,\n    borderWidth: vlak.hairline, borderStyle: \"solid\", borderColor: { default: vlak.divider, [mq.forcedColors]: \"CanvasText\" },\n    borderRadius: vlak.radiusSm, color: vlak.ink, backgroundColor: vlak.paper,\n  },\n  header: {\n    display: \"flex\", flexWrap: \"wrap\", alignItems: \"center\", justifyContent: \"space-between\", gap: \"0.75rem\", flexShrink: 0,\n    paddingBlock: \"1rem\", paddingInline: { default: \"1.5rem\", [mq.phone]: \"1rem\" },\n    borderBottomWidth: vlak.hairline, borderBottomStyle: \"solid\", borderBottomColor: vlak.divider,\n  },\n  heading: { display: \"flex\", flexDirection: \"column\", gap: \"0.25rem\", minWidth: 0, flex: \"1 1 12rem\", overflowWrap: \"anywhere\" },\n  title: { fontSize: vlak.controlFs, fontWeight: 600, lineHeight: 1.45 },\n  description: { color: vlak.gray, fontSize: vlak.controlLabel, lineHeight: 1.45 },\n  actions: { display: \"flex\", flexWrap: \"wrap\", alignItems: \"center\", gap: \"0.5rem\", minWidth: 0 },\n  history: {\n    boxSizing: \"border-box\", minHeight: 0, flex: \"0 1 auto\",\n    paddingBlock: \"1.5rem\", paddingInline: { default: \"1.5rem\", [mq.phone]: \"1rem\" },\n  },\n  composer: {\n    display: \"flex\", flexDirection: \"column\", gap: \"0.75rem\", minWidth: 0, flexShrink: 0,\n    paddingBlock: \"1rem\", paddingInline: { default: \"1.5rem\", [mq.phone]: \"1rem\" },\n    borderTopWidth: vlak.hairline, borderTopStyle: \"solid\", borderTopColor: vlak.divider,\n  },\n  footer: { color: vlak.gray, fontSize: vlak.controlLabel, lineHeight: 1.45, overflowWrap: \"anywhere\" },\n});\n\n/** A chat frame with a named history and a composer that stays outside its scroll area. */\nexport const Chat = React.forwardRef<HTMLElement, ChatProps>(function Chat({\n  title, description, actions, children, composer, footer, conversationLabel = \"Conversation\", historyHeight = \"28rem\", autoScroll = true,\n  className, style, \"aria-label\": ariaLabel, \"aria-labelledby\": ariaLabelledBy, ...props\n}, ref) {\n  const titleId = React.useId();\n  const root = rs([\"rs-chat\", className], styles.root);\n  const header = rs([\"rs-chat-header\"], styles.header);\n  const heading = rs([\"rs-chat-heading\"], styles.heading);\n  const titleStyle = rs([\"rs-chat-title\"], styles.title);\n  const descriptionStyle = rs([\"rs-chat-description\"], styles.description);\n  const actionsStyle = rs([\"rs-chat-actions\"], styles.actions);\n  const history = rs([\"rs-chat-history\"], styles.history);\n  const composerStyle = rs([\"rs-chat-composer\"], styles.composer);\n  const footerStyle = rs([\"rs-chat-footer\"], styles.footer);\n\n  return <section {...props} ref={ref} aria-label={ariaLabel} aria-labelledby={ariaLabelledBy ?? (ariaLabel == null ? titleId : undefined)}\n    className={root.className} style={{ ...root.style, ...style }}>\n    <header {...header}>\n      <div {...heading}>\n        <span {...titleStyle} id={titleId}>{title}</span>\n        {description != null && <div {...descriptionStyle}>{description}</div>}\n      </div>\n      {actions != null && <div {...actionsStyle}>{actions}</div>}\n    </header>\n    <Conversation {...history} label={conversationLabel} maxHeight=\"100%\" autoScroll={autoScroll} style={{ ...history.style, height: historyHeight }}>\n      {children}\n    </Conversation>\n    <div {...composerStyle}>\n      {composer}\n      {footer != null && <div {...footerStyle}>{footer}</div>}\n    </div>\n  </section>;\n});\n",
      "type": "registry:component",
      "target": "components/vlak/chat.tsx"
    },
    {
      "path": "vlak/styles/chat.css",
      "content": "/* ── chat: generated from packages/react/src/components/chat.tsx ── */\n.rs-chat{box-sizing:border-box;display:flex;flex-direction:column;width:100%;min-width:0;min-height:0;border-width:1px;border-style:solid;border-color:var(--divider);border-radius:var(--radius-sm);color:var(--text);background-color:var(--bg)}\n@media (forced-colors: active){.rs-chat{border-color:CanvasText}}\n.rs-chat-header{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:0.75rem;flex-shrink:0;padding-block:1rem;padding-inline:1.5rem;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:var(--divider)}\n@media (max-width: 640px){.rs-chat-header{padding-inline:1rem}}\n.rs-chat-heading{display:flex;flex-direction:column;gap:0.25rem;min-width:0;flex:1 1 12rem;overflow-wrap:anywhere}\n.rs-chat-title{font-size:var(--control-fs);font-weight:600;line-height:1.45}\n.rs-chat-description{color:var(--text-secondary);font-size:var(--control-label);line-height:1.45}\n.rs-chat-actions{display:flex;flex-wrap:wrap;align-items:center;gap:0.5rem;min-width:0}\n.rs-chat-history{box-sizing:border-box;min-height:0;flex:0 1 auto;padding-block:1.5rem;padding-inline:1.5rem}\n@media (max-width: 640px){.rs-chat-history{padding-inline:1rem}}\n.rs-chat-composer{display:flex;flex-direction:column;gap:0.75rem;min-width:0;flex-shrink:0;padding-block:1rem;padding-inline:1.5rem;border-top-width:1px;border-top-style:solid;border-top-color:var(--divider)}\n@media (max-width: 640px){.rs-chat-composer{padding-inline:1rem}}\n.rs-chat-footer{color:var(--text-secondary);font-size:var(--control-label);line-height:1.45;overflow-wrap:anywhere}\n",
      "type": "registry:file",
      "target": "styles/vlak/chat.css"
    }
  ],
  "meta": {
    "vlak": {
      "category": "ai",
      "classes": [
        "rs-chat",
        "rs-chat-header",
        "rs-chat-heading",
        "rs-chat-title",
        "rs-chat-description",
        "rs-chat-actions",
        "rs-chat-history",
        "rs-chat-composer",
        "rs-chat-footer"
      ],
      "snippet": "<section class=\"rs-chat\" aria-labelledby=\"project-chat-title\"><header class=\"rs-chat-header\"><div class=\"rs-chat-heading\"><span class=\"rs-chat-title\" id=\"project-chat-title\">Project brief</span><div class=\"rs-chat-description\">Ask about the current draft</div></div></header><div class=\"rs-chat-history rs-conversation\" style=\"height:28rem\"><div class=\"rs-conversation-viewport\" role=\"log\" aria-label=\"Project brief messages\" aria-live=\"off\" tabindex=\"0\" style=\"max-height:100%\"><div class=\"rs-conversation-content\"><p>You: What needs to change?</p><p>Assistant: Name the owner and next step.</p></div></div></div><div class=\"rs-chat-composer\"><form aria-label=\"Message composer\" style=\"display:flex;align-items:flex-end;gap:4px\"><textarea aria-label=\"Message\" name=\"message\" rows=\"1\" style=\"flex:1;min-width:0;min-height:44px;max-height:10rem\"></textarea><button type=\"submit\" aria-label=\"Send message\" style=\"width:44px;height:44px;flex:none\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\"><path d=\"M8 12.5v-9M4.5 7 8 3.5 11.5 7\" /></svg></button></form></div></section>",
      "cssOnly": false,
      "registryDependencies": [
        "conversation",
        "vlak-lib"
      ],
      "aliases": [
        "AI chat",
        "Chat interface",
        "Chat panel",
        "Assistant chat",
        "Chat shell"
      ],
      "example": "import { Chat, MessageComposer, Reasoning, Response, ResponseActions, type ComposedMessage } from \"@noorddev/vlak-react\";\n\nexport function ProjectChat({ onSend, generating, onStop }: {\n  onSend: (message: ComposedMessage) => void | Promise<void>;\n  generating: boolean;\n  onStop: () => void;\n}) {\n  return <Chat title=\"Project brief\" description=\"Ask about the current draft\" conversationLabel=\"Project brief messages\" historyHeight=\"28rem\"\n    composer={<MessageComposer compact maxRows={6} sendOnEnter onSend={onSend} generating={generating} onStop={onStop} />}>\n    <Response from=\"user\">What needs to change?</Response>\n    <Response actions={<ResponseActions text=\"Name the owner and next step.\" />}>\n      <Reasoning variant=\"inline\" title=\"Reviewed the brief\" status=\"complete\">\n        <p>Checked the supplied scope and responsibilities.</p>\n      </Reasoning>\n      <p>Name the owner and next step.</p>\n    </Response>\n  </Chat>;\n}",
      "usage": {
        "use": [
          "A complete chat surface with a stable header and composer around scrolling messages.",
          "Supply Response elements as children and MessageComposer through the composer slot. Compact mode starts on one line, uses icon send and stop controls, and grows up to maxRows.",
          "Set historyHeight to reserve message space and let the frame fill its container width.",
          "Use actions for header controls and footer for supporting text below the composer.",
          "CSS-only markup provides the frame and native scrolling; following, draft growth, and send handling require React or application code."
        ],
        "avoid": [
          "Nesting another Conversation inside the supplied children.",
          "Using this component to connect a model, store messages, or execute tools.",
          "Using a fixed page overlay when the surrounding application needs to control placement."
        ]
      },
      "keyboard": [
        {
          "keys": "Tab",
          "does": "Moves through supplied header actions, the message history, and composer controls"
        },
        {
          "keys": "Arrow keys, Page Up, Page Down",
          "does": "Scrolls the focused history using native browser behavior"
        },
        {
          "keys": "Enter, Space",
          "does": "Activates Jump to latest and returns focus to the history"
        }
      ],
      "a11y": [
        "The section is named by its visible title unless an accessible name override is supplied.",
        "conversationLabel names the scrollable message log independently of the frame.",
        "The built-in Conversation keeps its live region off and preserves the reader's position when they scroll back.",
        "The composer and its supporting footer remain outside the scrollable history.",
        "Native attributes and the forwarded ref reach the outer section. The application supplies accessible controls in the actions and composer slots."
      ]
    }
  }
}
