# Plugin Chatbot





Chat interface component with message history, typing indicators, and customizable avatars.

> **v5.4+** — the chat surface is now composed from vendored
> [Vercel AI Elements](https://elements.ai-sdk.dev) (MIT). The public
> `chatbot` schema and `ChatbotEnhanced` / `FloatingChatbot` props are
> unchanged. A new optional `suggestions: string[]` field renders chip
> prompts in the empty state.

For advanced compositions, the underlying elements are re-exported:

```tsx
import { AIElements } from '@object-ui/plugin-chatbot';
// AIElements.Conversation, AIElements.Message, AIElements.PromptInput, ...
```

## Installation [#installation]

```bash
npm install @object-ui/plugin-chatbot
```

<PluginLoader plugins="['chatbot']">
  ## Interactive Examples [#interactive-examples]

  ### Basic Chatbot [#basic-chatbot]

  <SchemaExample id="plugin-chatbot/basic-chatbot" />

  ### Chatbot with Timestamps [#chatbot-with-timestamps]

  <SchemaExample id="plugin-chatbot/chatbot-with-timestamps" />

  ### Customer Support Chat [#customer-support-chat]

  <SchemaExample id="plugin-chatbot/customer-support-chat" />

  ## Usage [#usage]

  ### Basic Usage [#basic-usage]

  ```tsx
  // Import once in your app entry point
  import '@object-ui/plugin-chatbot'
  import type { ChatbotSchema } from '@object-ui/types'

  // Use in schemas
  const schema: ChatbotSchema = {
    type: 'chatbot',
    messages: [
      {
        id: '1',
        role: 'assistant',
        content: 'Hello! How can I help you?'
      }
    ],
    placeholder: 'Type your message...',
    autoResponse: true
  }
  ```

  ## Features [#features]

  * **Message History**: Display chat messages with user and assistant roles
  * **System Messages**: Show system notifications in the chat
  * **Timestamps**: Optional timestamp display for each message
  * **Custom Avatars**: Configurable avatar images and fallback text
  * **Auto-scroll**: Automatically scroll to newest messages
  * **Typing Indicator**: Built-in typing indicator component
  * **Auto-response**: Demo mode with automatic responses
  * **Responsive Floating Panel**: Console assistants keep the panel inside safe
    browser gutters and hide the FAB while the chat is open
  * **Conversation States**: Empty streaming messages render as an assistant
    responding indicator, the submit control becomes Stop while streaming, and
    backend errors collapse into a retryable notice with optional details
  * **Lightweight**: Pure React components with minimal dependencies

  ## Schema API [#schema-api]

  ```plaintext
  {
    type: 'chatbot',
    messages?: ChatMessage[],
    placeholder?: string,
    showTimestamp?: boolean,
    disabled?: boolean,
    userAvatarUrl?: string,
    userAvatarFallback?: string,
    assistantAvatarUrl?: string,
    assistantAvatarFallback?: string,
    maxHeight?: string,
    autoResponse?: boolean,
    autoResponseText?: string,
    autoResponseDelay?: number,
    onSend?: (content: string, messages: ObjectChatMessage[]) => void,
    className?: string,
    // AI / service-ai integration fields
    api?: string,
    conversationId?: string,
    systemPrompt?: string,
    model?: string,
    streamingEnabled?: boolean,
    headers?: Record<string, string>,
    requestBody?: Record<string, unknown>,
    maxToolRoundtrips?: number, // deprecated - inert, see below
    onError?: (error: Error) => void,
  }
  ```

  ### ChatMessage [#chatmessage]

  ```plaintext
  {
    id: string,
    role: 'user' | 'assistant' | 'system' | 'tool',
    content: string,
    timestamp?: string | Date,
    metadata?: any,
    streaming?: boolean,
    toolInvocations?: ChatToolInvocation[],
  }
  ```

  ## Properties [#properties]

  `showTimestamp` through `onSend` below are declared on [`ChatbotSchema`](https://github.com/objectstack-ai/objectui/blob/main/packages/types/src/complex.ts)
  (`@object-ui/types`) — previously they existed only in an anonymous type local
  to the renderer, referenceable, validatable and documentable by nothing
  outside that one file (objectui#6169).

  This page documents **three** registrations - `chatbot`, `chatbot-enhanced` and
  `chatbot-floating` - and they do not all read the same keys. &#x2A;*A row whose
  description carries no bolded scope note is read by all three.** The rows only
  some of them read say so in bold at the start of the description, and name what
  to author instead on the registrations that ignore the key.

  Each registration has its own importable authoring-face type in
  `@object-ui/types` (objectui#7655): `ChatbotSchema` for `chatbot`,
  `ChatbotEnhancedSchema` for `chatbot-enhanced` and `ChatbotFloatingSchema` for
  `chatbot-floating`. Each declares exactly the keys its registration reads - the
  twenty shared rows below are one declaration the two newer faces pick off
  `ChatbotSchema` by name, and a scoped row is declared only on the face(s) whose
  registration reads it. A key a registration ignores is therefore not a declared
  member of its type. It still type-checks (`BaseSchema` ends in an index
  signature, so an unlisted key is `any` rather than an error) and still parses
  (the Zod twins are `.passthrough()`). On `chatbot` and `chatbot-enhanced` it is
  then dropped silently at render time. `chatbot-floating` is different today: its
  registration forwards the whole authored node to the panel through an
  unfiltered props spread, so some keys its type does not declare
  (`processVisibility`, `surface`, `showAvatars`) do reach the panel - an
  accidental channel, measured and tracked as objectui#7708, not a contract to
  author against. That is why the scope is spelled out here as well as in the
  types.

  The table below is that shared chat surface. `chatbot-floating` declares
  six more keys of its own - the six `floatingConfig&#x60; entries - which no row
  below carries and which the other two registrations have no trigger or panel
  to apply. They are documented in their own table after this one, under
  **`chatbot-floating` panel and trigger keys**.

  | Property                  | Type                               | Default                  | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
  | ------------------------- | ---------------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
  | `messages`                | array                              | `[]`                     | Initial chat messages                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
  | `placeholder`             | string                             | `'Type your message...'` | Input field placeholder text                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
  | `showTimestamp`           | boolean                            | `false`                  | Display timestamps for messages                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
  | `disabled`                | boolean                            | `false`                  | Disable chat input                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
  | `userAvatarUrl`           | string                             | -                        | URL for user avatar image                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
  | `userAvatarFallback`      | string                             | `'You'`                  | Fallback text for user avatar                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
  | `assistantAvatarUrl`      | string                             | -                        | URL for assistant avatar image                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
  | `assistantAvatarFallback` | string                             | `'AI'`                   | Fallback text for assistant avatar                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
  | `maxHeight`               | string                             | `'500px'`                | **`chatbot` and `chatbot-enhanced` only** (declared on `ChatbotSchema` and `ChatbotEnhancedSchema`; `ChatbotFloatingSchema` does not declare it). Maximum height of the chat message container, as a CSS length. `chatbot-floating` does not read it: its panel is sized by `floatingConfig.panelHeight` (a **number** of pixels, default `520`), and the panel pins its inner chat to `maxHeight: '100%'` so it fills that panel - a `maxHeight` authored on a floating node would be overridden even if it were forwarded. Size a floating chatbot with `floatingConfig.panelHeight` instead                                                                                       |
  | `autoResponse`            | boolean                            | `false`                  | Enable auto-response (demo mode, ignored when `api` is set)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
  | `autoResponseText`        | string                             | -                        | Text for auto-response                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
  | `autoResponseDelay`       | number                             | `1000`                   | Delay before auto-response (ms)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
  | `onSend`                  | function                           | -                        | Callback when message is sent                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
  | `className`               | string                             | `''`                     | Additional Tailwind CSS classes                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
  | `api`                     | string                             | -                        | Backend SSE endpoint (e.g., `/api/v1/ai/chat`). Enables AI streaming mode                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
  | `conversationId`          | string                             | -                        | Multi-turn conversation identifier                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
  | `systemPrompt`            | string                             | -                        | System prompt to configure assistant behavior                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
  | `model`                   | string                             | -                        | AI model identifier (e.g., `gpt-4o`)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
  | `streamingEnabled`        | boolean                            | `true`                   | Enable SSE streaming for AI responses                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
  | `headers`                 | object                             | -                        | Additional headers for API requests                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
  | `requestBody`             | object                             | -                        | Additional body parameters sent with each API request. Authored on the node as `requestBody`; the renderer forwards it to the chat runtime under its own `body` option. Writing `body` on the node instead sets the base schema's children container and never reaches the API                                                                                                                                                                                                                                                                                                                                                                                                       |
  | `maxToolRoundtrips`       | number                             | -                        | **Deprecated - has no effect.** Nothing reads this value, so it never capped anything. Cap tool-calling loops on the agent instead (`planning.maxIterations`). Still accepted so existing documents keep parsing; slated for removal in a future major                                                                                                                                                                                                                                                                                                                                                                                                                               |
  | `surface`                 | `'card' \| 'plain'`                | `'card'`                 | **`chatbot-enhanced` only** (declared on `ChatbotEnhancedSchema`). Controls whether the chat renders as a bordered panel (`'card'`) or a frameless full-page workspace (`'plain'`). `chatbot` renders the plain chat component, which has no such chrome to switch, and does not read this key. `chatbot-floating` has no named read for it and `ChatbotFloatingSchema` does not declare it; its panel is a `ChatbotEnhanced`, and an authored value currently reaches that panel only through the registration's unfiltered props spread (objectui#7708) - not a contract to author against                                                                                         |
  | `processVisibility`       | `'hidden' \| 'summary' \| 'debug'` | `'summary'`              | **`chatbot-enhanced` only** (declared on `ChatbotEnhancedSchema`; `ChatbotSchema` still declares it too, though the `chatbot` registration has no read for it). Controls how much agent reasoning and tool detail is shown. `chatbot` renders the plain chat component, which has no agent-process display to configure at all - switch the node to `chatbot-enhanced` if you need one. `chatbot-floating` has no named read for it and `ChatbotFloatingSchema` does not declare it; an authored value currently reaches its panel only through the registration's unfiltered props spread (objectui#7708), which is not a contract - there is no floating-side substitute to author |
  | `onError`                 | function                           | -                        | Error callback for streaming/API errors                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |

  ### `chatbot-floating` panel and trigger keys [#chatbot-floating-panel-and-trigger-keys]

  The six keys below are declared in the `chatbot-floating` registration's own
  `inputs` (`packages/plugin-chatbot/src/renderer.tsx`). They configure the
  floating action button and the panel it opens; the `chatbot` and
  `chatbot-enhanced` registrations render neither and ignore them. `floatingConfig`
  is declared on `ChatbotSchema` and on `ChatbotFloatingSchema` alike
  (objectui#7655 declared the floating face with the same member; `ChatbotSchema`
  kept its own), so authoring it on an inline node type-checks and parses - and
  is dropped at render time, because the `chatbot` node never read it.

  **There is no `displayMode` key.** The presentation is selected by the node's
  own `type`: author a `chatbot-floating` node for the trigger-and-panel
  presentation, and a `chatbot` or `chatbot-enhanced` node for an inline one.
  `displayMode` (`'inline' | 'floating'`) used to be declared on both faces,
  offered as a **Display Mode** control in the designer and seeded as
  `'floating'` into every node the designer created - and read by nothing: it was
  a second spelling of the choice `type` already makes, so `'inline'` on a
  `chatbot-floating` node changed nothing and `'floating'` on a `chatbot` node
  produced no trigger. objectui#7654 retired it (maintainer ruling, 2026-09-05):
  the declaration is a `never` tombstone on `ChatbotSchema` and
  `ChatbotFloatingSchema`, so writing the key against either face is now a
  compile error, and the designer control and default are gone. Stored documents
  that still carry the key parse exactly as they did - it never had a Zod arm and
  the twins are passthrough - and the value is ignored at render time, as it
  always was.

  | Property                     | Type                              | Default          | Description                                                                                                                                                                                                                            |
  | ---------------------------- | --------------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | `floatingConfig.position`    | `'bottom-right' \| 'bottom-left'` | `'bottom-right'` | Corner the trigger sits in; the panel is anchored to the same side                                                                                                                                                                     |
  | `floatingConfig.defaultOpen` | boolean                           | `false`          | Whether the panel is already open when the node mounts                                                                                                                                                                                 |
  | `floatingConfig.panelWidth`  | number                            | `400`            | Panel width in pixels, applied from the `sm` breakpoint up - below it the panel is full-bleed. Snapped to a step, see below                                                                                                            |
  | `floatingConfig.panelHeight` | number                            | `520`            | Panel height in **pixels, as a number** - not a CSS length string. This is the key that sizes a floating chatbot: `maxHeight` is a string, belongs to the two inline registrations, and is not read here. Snapped to a step, see below |
  | `floatingConfig.title`       | string                            | `'Chat'`         | Text in the panel header, and the panel's `aria-label`                                                                                                                                                                                 |
  | `floatingConfig.triggerSize` | number                            | `56`             | Diameter of the floating action button in pixels. Snapped to a step, see below                                                                                                                                                         |

  **The three size keys snap to the nearest declared step.** `FloatingChatbotPanel`
  and `FloatingChatbotTrigger` resolve each number through a fixed table of
  Tailwind classes and fall back to the closest entry, so a number outside the
  table does not render at that size - a `panelHeight` of `530` renders at `520`:

  * `panelWidth` - 300, 320, 340, 360, 380, 400, 420, 440, 450, 460, 480, 500, 520, 560, 600, 640, 720, 800
  * `panelHeight` - 360, 400, 420, 440, 480, 500, 520, 560, 600, 640, 720, 800
  * `triggerSize` - 32, 36, 40, 44, 48, 52, 56, 60, 64, 72, 80

  On small screens `panelHeight` is additionally capped to the viewport
  (`min(step, 100svh - 6rem - safe-area-inset-bottom)`), and while the panel is
  fullscreen it ignores both size keys and fills the screen.

  Authored on the node, with the node's own type - `ChatbotFloatingSchema` pins
  `type` to `'chatbot-floating'` and declares `floatingConfig`, so this fence
  compiles against the published types. (Until objectui#7655 no type could
  annotate a floating node - `ChatbotSchema` pins `type` to `'chatbot'` - and
  this example had to be untyped JSON.)

  ```tsx
  import type { ChatbotFloatingSchema } from '@object-ui/types';

  const supportChat: ChatbotFloatingSchema = {
    type: 'chatbot-floating',
    messages: [], // seed with your own ChatMessage values
    floatingConfig: {
      position: 'bottom-left',
      defaultOpen: false,
      panelWidth: 400,
      panelHeight: 520,
      title: 'Support',
      triggerSize: 56,
    },
    placeholder: 'Ask us anything...',
  };
  ```

  The config object also has its own exported type:

  ```tsx
  import type { FloatingChatbotConfig } from '@object-ui/types';

  const floatingConfig: FloatingChatbotConfig = {
    position: 'bottom-left',
    panelWidth: 400,
    panelHeight: 520, // pixels, as a number - '520px' does not type-check
    title: 'Support',
    triggerSize: 56,
  };
  ```

  ## Operating Modes [#operating-modes]

  The chatbot supports two modes, automatically selected based on the `api` field:

  ### Local/Demo Mode (default) [#localdemo-mode-default]

  When `api` is not set, the chatbot operates in local mode with optional auto-response:

  ```tsx
  import type { ChatbotSchema } from '@object-ui/types';

  const schema: ChatbotSchema = {
    type: 'chatbot',
    messages: [], // seed with your own ChatMessage values
    autoResponse: true,
    autoResponseText: 'Thanks!',
    autoResponseDelay: 1000,
  };
  ```

  ### AI Streaming Mode (service-ai) [#ai-streaming-mode-service-ai]

  When `api` is set, the chatbot uses `@ai-sdk/react` for real SSE streaming:

  ```tsx
  import type { ChatbotSchema } from '@object-ui/types';

  const schema: ChatbotSchema = {
    type: 'chatbot',
    api: '/api/v1/ai/chat',
    model: 'gpt-4o',
    systemPrompt: 'You are a helpful assistant.',
    streamingEnabled: true,
    messages: [],
  };
  ```

  ## Message Roles [#message-roles]

  ### User Messages [#user-messages]

  Messages from the user appear on the right side with primary styling:

  ```tsx
  import type { ChatMessage } from '@object-ui/types';

  const userMessage: ChatMessage = {
    id: '1',
    role: 'user',
    content: 'Hello!',
    timestamp: '10:30 AM',
  };
  ```

  ### Assistant Messages [#assistant-messages]

  Messages from the assistant appear on the left side:

  ```tsx
  import type { ChatMessage } from '@object-ui/types';

  const assistantMessage: ChatMessage = {
    id: '2',
    role: 'assistant',
    content: 'Hi! How can I help?',
    timestamp: '10:30 AM',
  };
  ```

  ### System Messages [#system-messages]

  System messages appear centered with muted styling:

  ```tsx
  import type { ChatMessage } from '@object-ui/types';

  const systemMessage: ChatMessage = {
    id: '3',
    role: 'system',
    content: 'Chat session started',
  };
  ```

  ### Tool Messages (AI Mode) [#tool-messages-ai-mode]

  Tool messages represent results from tool invocations during AI streaming. They are generated automatically by the vercel/ai SDK when the backend performs tool calls (e.g., fetching weather, querying a database). The SDK may emit `role: 'tool'` messages as well as populate the assistant message's `toolInvocations` array.

  By default, `ChatbotEnhanced` renders tool invocations as a compact agent activity summary. Repeated calls are grouped, raw tool names are hidden, and reasoning text is not shown. Use `processVisibility="debug"` for developer/admin views that need the full reasoning panel, raw tool names, parameters, and results. Use `processVisibility="hidden"` to suppress non-interactive activity entirely; approval and draft-review actions remain visible.

  Use `surface="plain"` for full-page chat workspaces where the surrounding app
  already provides navigation chrome. The default `surface="card"` remains a
  better fit for embedded dashboards, side panels, and floating chat windows.

  Both keys are authorable as metadata on a `chatbot-enhanced` node, typed with
  that node's own face (objectui#7655):

  ```tsx
  import type { ChatbotEnhancedSchema } from '@object-ui/types';

  const workspace: ChatbotEnhancedSchema = {
    type: 'chatbot-enhanced',
    messages: [], // seed with your own ChatMessage values
    api: '/api/v1/ai/chat',
    surface: 'plain',
    processVisibility: 'debug',
    enableFileUpload: true,
  };
  ```

  Console chat surfaces also keep a sanitized browser-side display cache for the
  current conversation. When a conversation can be reopened but the server returns
  no message rows, the UI restores user/assistant text and grouped tool names plus
  states. Reasoning, tool parameters, and raw tool results are not stored in this
  cache.

  ```tsx
  import type { ChatMessage } from '@object-ui/types';

  const toolMessage: ChatMessage = {
    id: '4',
    role: 'assistant',
    content: 'The weather in SF is 68°F.',
    toolInvocations: [
      {
        toolCallId: 'tc-1',
        toolName: 'getWeather',
        args: { city: 'San Francisco' },
        result: { temp: 68, condition: 'Sunny' },
        state: 'result',
      },
    ],
  };
  ```

  ## Examples [#examples]

  ### Simple AI Chat [#simple-ai-chat]

  ```tsx
  import type { ChatbotSchema } from '@object-ui/types';

  const aiChat: ChatbotSchema = {
    type: 'chatbot',
    messages: [
      {
        id: 'welcome',
        role: 'assistant',
        content: 'Hello! I\'m your AI assistant. Ask me anything!'
      }
    ],
    placeholder: 'Ask me a question...',
    assistantAvatarFallback: 'AI',
    userAvatarFallback: 'You',
    autoResponse: true,
    autoResponseText: 'That\'s a great question! Let me think about that...',
    maxHeight: '600px'
  }
  ```

  ### Support Ticket Chat [#support-ticket-chat]

  ```tsx
  import type { ChatbotSchema } from '@object-ui/types';

  const supportChat: ChatbotSchema = {
    type: 'chatbot',
    messages: [
      {
        id: 'sys-1',
        role: 'system',
        content: 'Ticket #12345 - Account Access Issue'
      },
      {
        id: '1',
        role: 'assistant',
        content: 'Hi! I\'m here to help with your account access issue.',
        avatarFallback: 'SP',
        timestamp: '2:15 PM'
      }
    ],
    placeholder: 'Describe your issue...',
    showTimestamp: true,
    userAvatarFallback: 'JD',
    assistantAvatarFallback: 'SP',
    className: 'w-full max-w-3xl mx-auto'
  };
  ```

  ### Sales Bot [#sales-bot]

  ```tsx
  import type { ChatbotSchema } from '@object-ui/types';

  const salesBot: ChatbotSchema = {
    type: 'chatbot',
    messages: [
      {
        id: '1',
        role: 'assistant',
        content: 'Welcome! I\'m here to help you find the perfect product. What are you looking for today?',
        avatarFallback: 'SB'
      }
    ],
    placeholder: 'Tell us what you need...',
    assistantAvatarFallback: 'SB',
    userAvatarFallback: 'You',
    autoResponse: true,
    autoResponseText: 'Great choice! Let me show you some options...',
    autoResponseDelay: 1200,
    maxHeight: '500px',
    className: 'border-2 border-primary rounded-xl'
  };
  ```

  ### Multi-agent Chat [#multi-agent-chat]

  ```tsx
  import type { ChatbotSchema } from '@object-ui/types';

  const multiAgentChat: ChatbotSchema = {
    type: 'chatbot',
    messages: [
      {
        id: '1',
        role: 'assistant',
        content: 'Hello! Sarah from Sales here.',
        avatarFallback: 'SA'
      },
      {
        id: '2',
        role: 'user',
        content: 'I need help with pricing',
        avatarFallback: 'CU'
      },
      {
        id: '3',
        role: 'system',
        content: 'Transferring to Finance team...'
      },
      {
        id: '4',
        role: 'assistant',
        content: 'Hi! Mike from Finance. I can help with that.',
        avatarFallback: 'MI'
      }
    ],
    showTimestamp: true,
    userAvatarFallback: 'CU'
  };
  ```

  ## Custom Avatars [#custom-avatars]

  ### Avatar Images [#avatar-images]

  ```tsx
  import type { ChatbotSchema } from '@object-ui/types';

  const schema: ChatbotSchema = {
    type: 'chatbot',
    userAvatarUrl: 'https://example.com/user-avatar.jpg',
    assistantAvatarUrl: 'https://example.com/bot-avatar.jpg',
    messages: [], // seed with your own ChatMessage values
  };
  ```

  ### Avatar Fallbacks [#avatar-fallbacks]

  When images aren't available, fallback text is displayed:

  ```tsx
  import type { ChatbotSchema } from '@object-ui/types';

  const schema: ChatbotSchema = {
    type: 'chatbot',
    userAvatarFallback: 'JD', // User initials
    assistantAvatarFallback: 'AI', // Bot identifier
    messages: [], // seed with your own ChatMessage values
  };
  ```

  ### Per-message Avatars [#per-message-avatars]

  Override avatars for individual messages:

  ```tsx
  import type { ChatMessage } from '@object-ui/plugin-chatbot';

  const message: ChatMessage = {
    id: '1',
    role: 'assistant',
    content: 'Message content',
    avatar: 'https://example.com/special-avatar.jpg',
    avatarFallback: 'SP',
  };
  ```

  ## Event Handling [#event-handling]

  ### onSend Callback [#onsend-callback]

  Handle message sending in your application:

  ```tsx
  import type { ChatbotSchema } from '@object-ui/types';

  const schema: ChatbotSchema = {
    type: 'chatbot',
    messages: [], // seed with your own ChatMessage values
    onSend: (content, allMessages) => {
      console.log('User sent:', content);
      console.log('All messages:', allMessages);

      // Send to your backend
      fetch('/api/chat', {
        method: 'POST',
        body: JSON.stringify({ message: content }),
      });
    },
  };
  ```

  ## Customization [#customization]

  ### Container Styling [#container-styling]

  ```tsx
  import type { ChatbotSchema } from '@object-ui/types';

  const schema: ChatbotSchema = {
    type: 'chatbot',
    className: 'w-full max-w-2xl mx-auto border-2 rounded-xl shadow-lg',
    maxHeight: '600px',
    messages: [], // seed with your own ChatMessage values
  };
  ```

  ### Responsive Heights [#responsive-heights]

  ```tsx
  import type { ChatbotSchema } from '@object-ui/types';

  const schema: ChatbotSchema = {
    type: 'chatbot',
    maxHeight: '400px', // or use Tailwind: 'h-96'
    className: 'sm:max-h-[500px] lg:max-h-[600px]',
    messages: [], // seed with your own ChatMessage values
  };
  ```

  ## TypeScript Support [#typescript-support]

  Each of the three registrations has its own authoring-face type:
  `ChatbotSchema` (`chatbot`), `ChatbotEnhancedSchema` (`chatbot-enhanced`) and
  `ChatbotFloatingSchema` (`chatbot-floating`) - see the typed examples under
  **Tool Messages*&#x2A; and **`chatbot-floating` panel and trigger keys** above.

  ```plaintext
  import type { ChatbotSchema, ChatbotEnhancedSchema, ChatbotFloatingSchema, ChatMessage, ChatToolInvocation } from '@object-ui/types'
  import { useObjectChat } from '@object-ui/plugin-chatbot'

  // Basic messages
  const messages: ChatMessage[] = [
    {
      id: '1',
      role: 'assistant',
      content: 'Hello!'
    }
  ]

  // Local/demo mode
  const demoSchema: ChatbotSchema = {
    type: 'chatbot',
    messages,
    placeholder: 'Type here...',
    showTimestamp: true
  }

  // AI streaming mode (service-ai)
  const aiSchema: ChatbotSchema = {
    type: 'chatbot',
    messages: [],
    api: '/api/v1/ai/chat',
    model: 'gpt-4o',
    systemPrompt: 'You are a helpful assistant.',
    streamingEnabled: true,
    conversationId: 'conv-123',
  }
  ```

  ### What comes back out: `ObjectChatMessage` [#what-comes-back-out-objectchatmessage]

  You AUTHOR with `@object-ui/types`' `ChatMessage`. What `useObjectChat` HANDS
  BACK — from `messages` and from `onSend(content, messages)` — is
  `ObjectChatMessage`, exported from `@object-ui/plugin-chatbot`:

  ```plaintext
  import type { ObjectChatMessage } from '@object-ui/plugin-chatbot'
  ```

  It is the authoring shape plus the render-only keys API mode really carries
  (`buildProgress`, `blueprintProgress`, `charts`, and `pendingActionId` /
  `draftReview` / `proposedPlan` / `proposedChanges` / `builderHandoff` on each
  tool invocation — the approval card, the "Review N changes" affordance, the plan
  card, the build panel, the inline charts), with `timestamp` narrowed to `string`
  because both modes absorb an authored `Date` before emitting.

  It is a subtype of the authoring `ChatMessage`, so an `onSend` callback that
  already declares `ChatMessage[]` keeps type-checking; naming `ObjectChatMessage`
  is what lets it read those keys. Rebuilding a message field-by-field from the
  authoring type drops every one of them — silently, and with the compiler's
  agreement (objectui#4424).

  ## Related Documentation [#related-documentation]

  * [Plugin System Overview](/docs/guide/plugins)
  * [Package README](https://github.com/objectstack-ai/objectui/tree/main/packages/plugin-chatbot)
</PluginLoader>
