Installation
Theme
Mode

Blocks

AI chat

A complete assistant surface in the shape people already know — a conversation sidebar, a threaded chat with attachments and a rich composer, and a side panel that opens generated documents beside the conversation. Assembled from the Orbit library: Button, Tooltip, Input, Menu, Avatar, Badge, the markdown-editor's renderer, and the file-dropzone helpers.

$npx shadcn@latest add @orbit/ai-chat

Overview

Live — send a message (Enter), click the Sprint 24 Retro card to open the artifact panel, attach or drag in a file, switch chats, or start a new one. It ships with a seeded conversation and canned replies so it demos on its own.

Sprint 24 retro summary
retro-notes.txtTXT · 8 KB
velocity.csvCSV · 2 KB

Can you summarize our Sprint 24 retro from the notes I attached? Pull out themes and turn them into action items with owners.

Done — I read through the notes and the velocity export. Here’s the shape of it:

  • 38 of 40 points shipped, two carried into Sprint 25.
  • Sentiment is up, mostly from the daily design/eng pairing.
  • The clear miss was rate limiting, which was under-scoped.

I’ve written it up as a retro doc with themes and owner-assigned action items:

Nice. Mark the rate-limiting split as done — Maya already broke it out.

Updated. The rate-limiting action item is checked off and the doc is now at v2 — the other two items are still open with their owners.

Orbit AI can make mistakes — verify important details before acting on them.

Without the sidebar

showSidebar={false} starts with the history rail collapsed and startWithArtifact={false} keeps the panel closed — the focused center pane for embedding in tighter layouts. Both rails stay one click away.

showSidebar={false} · startWithArtifact={false}
Sprint 24 retro summary
retro-notes.txtTXT · 8 KB
velocity.csvCSV · 2 KB

Can you summarize our Sprint 24 retro from the notes I attached? Pull out themes and turn them into action items with owners.

Done — I read through the notes and the velocity export. Here’s the shape of it:

  • 38 of 40 points shipped, two carried into Sprint 25.
  • Sentiment is up, mostly from the daily design/eng pairing.
  • The clear miss was rate limiting, which was under-scoped.

I’ve written it up as a retro doc with themes and owner-assigned action items:

Nice. Mark the rate-limiting split as done — Maya already broke it out.

Updated. The rate-limiting action item is checked off and the doc is now at v2 — the other two items are still open with their owners.

Orbit AI can make mistakes — verify important details before acting on them.

Empty state & greeting

A conversation with no messages renders the greeting headline (override it with the greeting prop) and suggestion chips that pre-fill the composer. Try one that sounds like drafting — “Draft a project brief” — and the demo reply mints an artifact.

greeting="What are we shipping today?"
New chat
What are we shipping today?
Ask about your projects, draft docs, or crunch the numbers.
Orbit AI can make mistakes — verify important details before acting on them.

Capabilities

Every affordance people expect from an assistant, on your tokens and ready to drop into a product.

Threaded chat

User bubbles and assistant replies with lightweight markdown — bold, code, and bullet lists — plus copy, rate and retry actions and a typing indicator.

Attachments

Attach files from the composer or drag them in — images preview as thumbnails, everything else as type-colored cards, removable before you send.

Artifact panel

Replies can produce documents that open in a side panel with a version pill, copy and download — the conversation and its output live side by side.

History & models

A date-grouped, searchable chat sidebar with new-chat and switching, plus a composer model picker — collapse either rail to focus.

Built from the library

The block is a composition — these Orbit components do the heavy lifting, so it inherits their theming, focus rings, and accessibility.

ButtonNew chat, every icon action, and the send control.
TooltipNames each icon-only action, with an Esc hint on the panel close.
InputThe sidebar's conversation search, with a clearable ×.
MenuThe composer's model picker — a radio group anchored above the pill.
AvatarThe account row in the sidebar footer.
BadgeThe artifact panel's version pill.
Markdown editorIts renderMarkdown() renders every message body and the artifact document — no second parser.
File dropzoneformatFileSize() and fileExt() helpers drive the attachment chips.

How it's assembled

Three columns — sidebar, chat pane, artifact panel — inside one bordered frame.

<AIChat>
├─ Sidebar                        · aside, collapsible
│  ├─ Button                      · primary lg — "New chat"
│  ├─ Input                       · search chats, clearable
│  ├─ Conversation list           · date-grouped, filtered rows
│  └─ Avatar + Button(ghost)      · account row, settings
├─ Chat pane
│  ├─ Top bar                     · Button(ghost) × 3 + Tooltip
│  ├─ Thread                      · role="log", autoscrolls
│  │  ├─ renderMarkdown()         · assistant + user bodies
│  │  ├─ Attachment chips         · file-dropzone helpers
│  │  ├─ Artifact card            · opens the panel
│  │  ├─ Copy / rate / retry      · Button(ghost sm) + Tooltip
│  │  └─ Typing dots              · while a reply is pending
│  └─ Composer
│     ├─ Attach + drag-and-drop   · Button(secondary) + drop overlay
│     ├─ Menu                     · model picker (radio group)
│     └─ Button                   · send, disabled when empty
└─ Artifact panel                 · aside, Esc closes
   ├─ Badge                       · version pill
   ├─ Copy / download / close     · Button(ghost) + Tooltip
   └─ renderMarkdown()            · document body

Props

PropTypeDefaultDescription
showSidebarbooleantrueShow the conversation history sidebar on load. The top-bar toggle flips it either way.
startWithArtifactbooleantrueOpen the artifact panel on load (the first artifact in the map).
greetingstring“Good to see you, {first name}”Empty-state greeting headline.
userNamestring"Sam Chen"Account in the sidebar footer — drives the Avatar and the default greeting.
conversationsAIChatConversation[]seeded demoInitial conversations. The first one is active.
artifactsRecord<string, AIChatArtifact>seeded demoArtifact map keyed by AIChatMessage.artifactId. Bodies are Markdown.
modelsAIChatModel[]3 Orbit modelsOptions in the composer's model picker.
defaultModelstringsecond modelInitially selected model id.
suggestionsAIChatSuggestion[]4 chipsEmpty-state suggestion chips — { label, icon? }.
demoRepliesbooleantrueBuilt-in canned-reply mode: typing dots, then a reply (an artifact for drafting-shaped prompts). Turn off with a real backend.
onSend(text, attachments, conversationId) => voidA message was submitted — wire your model call here.
onModelChange(modelId: string) => voidThe picker's model changed.
onConversationChange(conversationId: string) => voidA different conversation was selected in the sidebar.
onNewChat(conversationId: string) => void“New chat” was clicked, with the fresh conversation's id.
PropTypeDefaultDescription
AIChatConversation{ id, title, group, messages }group is the sidebar section label — “Today”, “Yesterday”, “Previous 7 days”.
AIChatMessage{ id, role, text, attachments?, artifactId? }role is "user" | "assistant"; text is Markdown; artifactId renders an open-artifact card.
AIChatAttachment{ id, name, size, kind, url? }kind "image" with a url renders an 88px thumbnail; anything else a type-colored chip.
AIChatArtifact{ title, kind, version, body }body is Markdown, rendered by the markdown-editor's renderMarkdown().
AIChatModel{ id, name, desc }A picker option — name plus a one-line positioning blurb.

Usage

The block demos on its own — wire the composer's submit to a real model to make it live.

Do

Stream responses

Swap the canned reply for a real token stream and keep the typing indicator until the first token — the perceived speed is most of the experience.

Don’t

Trap output in the chat

Anything long or reusable — a doc, a table, code — belongs in the artifact panel, not buried in the scrollback where it's lost on the next turn.

Do

Keep the composer reachable

Enter sends, Shift+Enter adds a line, and the send button disables when empty. The field stays pinned so it's always one keystroke away.

Don’t

Hide provenance

Show which model answered and let people switch. A silent model swap erodes trust in the answers people are acting on.

Keyboard & accessibility

Every action is a labeled button with the library's focus ring. The thread is a polite live region (role=log) so replies are announced; the model picker is a Base UI menu with radio semantics and full keyboard navigation; Esc closes the artifact panel, and the drag-and-drop overlay is presentation-only — the attach button reaches the same picker.

Send the message
⇧ ↵Add a line without sending
EscClose the artifact panel (or the model menu)
↑ ↓ ↵Navigate and pick in the model menu
TabReach every action — all are real buttons