# Chat primitives

> Frame chat primitives for conversational and agent interfaces: MessageCard, ChatMessage, MessageActions, ChatWelcomeState, ChatInput, AttachmentChip and ChatPanel.

Web page: https://system.beibeidesign.com/components/chat/

### MessageCard

Master + extended Used in: Wingman 1 · WorkPal 0

Inline event notice with information, success or warning tone and an optional action slot.

Install component Copy code [View source](https://github.com/BeibeiZhang/frame-design-system/blob/main/components/message-card.tsx)

Requires React 19, ReactDOM 19, TypeScript and lucide-react 1.14.0. Import frame.css once and use a frame-root wrapper; set its background to var(--color-bg-page) and color to var(--color-text-primary), or supply a matching host surface; add dark to an ancestor for dark mode.

Tested with shadcn 4.21.0, React 19.2.5, Vite 8.0.10 and Tailwind 3.4.19 / 4.3.3. Start with a configured React/Vite shadcn app. Files install into src/components/frame; import its frame.css. For manual copy, save the complete component and stylesheet below. Examples leave optional voice features off; retain the MIT license and third-party notices.

[Required stylesheet: frame.css](https://github.com/BeibeiZhang/frame-design-system/blob/main/styles/frame.css)

```
npx shadcn@4.21.0 add https://raw.githubusercontent.com/BeibeiZhang/frame-design-system/main/registry/message-card.json
```

Copy install command Close

Inline card-style message for meta-events in a chat stream — distinct from a regular ChatMessage bubble. Three tones: info / success / warning.

Heads up

A new agent version is available.

Branch merged

Pushed claude/loving-khorana to origin/main.

View commit

Conflict detected

Two of your tasks reference the same file. Resolve before continuing.

Extension details (1 fork)

- WorkPal fork: `apps/workpal/src/components/MessageCard.tsx`

### ChatMessage

Master + extended Used in: Wingman 1 · WorkPal 1

User and assistant message presentation with a visual streaming state.

Install component Copy code [View source](https://github.com/BeibeiZhang/frame-design-system/blob/main/components/chat-message.tsx)

Requires React 19, ReactDOM 19, TypeScript and lucide-react 1.14.0. Import frame.css once and use a frame-root wrapper; set its background to var(--color-bg-page) and color to var(--color-text-primary), or supply a matching host surface; add dark to an ancestor for dark mode.

Tested with shadcn 4.21.0, React 19.2.5, Vite 8.0.10 and Tailwind 3.4.19 / 4.3.3. Start with a configured React/Vite shadcn app. Files install into src/components/frame; import its frame.css. For manual copy, save the complete component and stylesheet below. Examples leave optional voice features off; retain the MIT license and third-party notices.

[Required stylesheet: frame.css](https://github.com/BeibeiZhang/frame-design-system/blob/main/styles/frame.css)

```
npx shadcn@4.21.0 add https://raw.githubusercontent.com/BeibeiZhang/frame-design-system/main/registry/chat-message.json
```

Copy install command Close

Single chat bubble. role='user' (right, no avatar) or role='assistant' (left, default Sparkles avatar; showAvatar={false} renders none — WorkPal's configuration). Optional streaming dots.

Help me plan dinner for two on Saturday.

On it — I'll cross-reference your past picks and the weather forecast.

Drafting suggestions

2 sources

No avatar — WorkPal's configuration.

Product extensions

Wingman Attachment record + conversational GIF reaction

[image: profile-photo.jpg]

profile-photo.jpg

842.0 KB

Does this photo feel like me?

Warm reaction GIF

Wingman may append a replaceable GIF after an assistant turn.

WorkPal No assistant avatar + primary artifact delivery

I finished the research pass and created the requested report.

market-brief.html

WorkPal composes the shared bubble with `showAvatar={false}` — assistant messages carry no avatar. Its wrapper keeps rich content (the one primary artifact it selects, web-source chips, media grids, feedback bar) below the bubble, outside the primitive.

Extension details (2 forks)

- Wingman fork: `apps/wingman/src/components/ChatMessage.tsx`
- WorkPal fork: `apps/workpal/src/components/ChatMessage.tsx`

### MessageActions

Master Used in: Wingman 0 · WorkPal 3

THE shared answer toolbar under an AI reply. Two abilities are built in — read-aloud (plays the whole answer, click again to stop) and copy (glyph flips to a check on success). Everything else — thumbs-up/down, retry, share — is optional and injected by the host through `actions`, laid out in the locked order: read-aloud · copy · injected. Used by the viewer's Ask-AI panel and WorkPal's chat. Read-aloud runs on the browser Web-Speech engine by default; a host can pass a `readAloud` transport (the viewer routes it to a server-side `/api/tts` so no key reaches the frontend).

On it — I'll cross-reference your past picks and the weather forecast.

Read aloud Copy Share Good response Bad response Retry

Read-aloud + copy are built in; share / thumbs-up / thumbs-down / retry are optional actions the host wires up — shown here as no-op placeholders. Read-aloud uses the browser Web-Speech engine, so the demo makes no server call.

With a timestamp

Help me plan dinner for two on Saturday.

3 minutes ago, Oct 13, 2026, 12:33 AM Copy

On it — I'll cross-reference your past picks and the weather forecast.

Read aloud Copy 2 minutes ago, Oct 13, 2026, 12:34 AM

Pass `timestamp` and the row shows when the message was sent — a relative time refreshed once a minute, full local time on hover. An AI answer puts it after the buttons; a user message puts it first (`timestampPosition="start"`) in a right-aligned row with Copy. Omit it and the row is unchanged.

### ChatWelcomeState

Master Used in: Wingman 2 · WorkPal 1

Shared agent carousel, greeting, commit/cancel behavior, and collapsed return state. Products configure identity copy and optional capabilities; they do not fork the carousel.

The product-owned production strings “What should we work on?” and “Customize” stay in English. Viewer annotations and explicitly localized demo labels follow the selected language.

WorkPal Role tags · fixed work prompt · video eligibility filter

[image: Matt]

[image: Maya]

Lily-The Research Partner

#### Hi, Beibei

What should we work on?

Product owns the greeting, role-title tags, selected agent, and which videos are eligible.

Wingman Persona tags · dating prompt · custom-avatar action

Happy-Sassy spark

[image: Matt]

[image: Maya]

[image: Lily]

#### Hi, friend

What’s on your mind? Let’s figure it out together.

Product owns custom-avatar persistence, the Customize flow, dating copy, and returning-user collapse state.

Long-name marquee Narrow phone (~320px)

Bartholomew Montgomery Fitzgerald III

[image: 司马长风明月清泉朗照山河万里长歌远行志高]

[image: Maya]

[image: Lily]

#### Hi, Beibei

What should we work on?

The long Latin name is focused on load; tap the avatar to its right for the 20-character CJK name. Names that fit stay static; only a name that overflows its track scrolls — a continuous one-direction ticker loop, no hover needed. Under prefers-reduced-motion the name wraps instead of scrolling (WCAG 2.3.3). Toggle the theme to check dark mode.

### ChatInput

Master + extended Used in: Wingman 5 · WorkPal 6

Prompt composer with callback-based sending, optional attachments and mentions. No model provider is included.

Install component Copy code [View source](https://github.com/BeibeiZhang/frame-design-system/blob/main/components/chat-input.tsx)

Requires React 19, ReactDOM 19, TypeScript and lucide-react 1.14.0. Import frame.css once and use a frame-root wrapper; set its background to var(--color-bg-page) and color to var(--color-text-primary), or supply a matching host surface; add dark to an ancestor for dark mode.

Tested with shadcn 4.21.0, React 19.2.5, Vite 8.0.10 and Tailwind 3.4.19 / 4.3.3. Start with a configured React/Vite shadcn app. Files install into src/components/frame; import its frame.css. For manual copy, save the complete component and stylesheet below. Examples leave optional voice features off; retain the MIT license and third-party notices.

[Required stylesheet: frame.css](https://github.com/BeibeiZhang/frame-design-system/blob/main/styles/frame.css)

```
npx shadcn@4.21.0 add https://raw.githubusercontent.com/BeibeiZhang/frame-design-system/main/registry/chat-input.json
```

Copy install command Close

Bottom composer — the brand-gradient pill **both Wingman and WorkPal render**, via a thin wrapper (`apps/{wingman,workpal}/src/components/ChatInput.tsx`) that opts into the features each call site needs through `features={{…}}`: attach, voice mode, voice input, action chips, @mentions, send overrides, and draft per-key. (A `features`-less baseline mode — controlled `value`/`onChange`, plain textarea — also exists for callers that just need a bare composer; no product uses it in V0.1.)

All 7 feature groups opted in — the same rich composer Wingman + WorkPal both render. Try: type a message, type @ to mention, click +, click the mic, hover chips.

[field: Attach photos] [field: Take a photo] [field: Attach files]

Create performance goals

Analyze doc(s)

Visualize data

Attach

[field: Message WorkPal]

Start voice input

Start voice mode

#### @ references and / commands

Default off. Pass an async mentions.source for @ (e.g. files) and features.commands for / at the start of the input. The master debounces, aborts stale requests and shows loading / empty states; the product owns the data and what a command does. Sample data below is a viewer demo, not a real product source. Use the sun/moon toggle to check dark mode.

[field: Type @ for a file or / for a command]

Events appear here — picked commands and sent messages.

#### Suggested reply

Optional suggestion prop. While the input is empty the predicted reply shows in grey as the placeholder; Tab (or → in the empty input) fills it, and a touch device gets a Use chip instead of the Tab hint. Tab keeps its normal focus behaviour with no suggestion or with text in the box. Wingman does not pass it.

#### useSuggestedReply

useSuggestedReply (@beibei/design-system/suggest) produces it: one request per finished turn you watched; the host supplies its messages and the request. Demo: a static suggestion.

[field: Yes, use this version and replace it on the About page] Use Suggested reply: Yes, use this version and replace it on the About page. Press Tab to use it.

#### Actual product configurations

Same master primitive, visibly different product choices. The profiles below are imported by the real wrappers and welcome call sites, so this comparison changes with the products instead of duplicating a Wingman or WorkPal specification in the viewer.

#### WorkPal

Real welcome configuration

workpal

[field: Attach photos] [field: Take a photo] [field: Attach files]

Create performance goals

Analyze doc(s)

Visualize data

Attach

[field: Message WorkPal]

Start voice input

Start voice mode

- **Voice entry**: Emphasized
- **Chip model**: Quick + action
- **@ mentions**: Not configured
- **Busy gate**: Product-controlled

Source: `apps/workpal/src/components/ChatInput.tsx · apps/workpal/src/components/ChatPanel.tsx`

#### Wingman

Real welcome configuration

wingman

[field: Attach photos] [field: Take a photo] [field: Attach files]

Dating personality

Connection style

Values compass

Get to know me more What I'm looking for Recap my last date

Attach

[field: What's on your mind?]

Start voice input

Start voice mode

- **Voice entry**: Emphasized
- **Chip model**: Quick only
- **@ mentions**: Match picker
- **Busy gate**: Not exposed

Source: `apps/wingman/src/components/ChatInput.tsx · apps/wingman/src/pages/ChatsPage.tsx`

Both still share attachments (10 files, up to 2 MB each), voice transcription, per-chat drafts, AI-response gating, and the same responsive shell. Try `@` in Wingman to open its product-only mention picker; WorkPal intentionally has no mention configuration.

Extension details (2 forks, 9 flag uses)

- Wingman fork: `apps/wingman/src/components/ChatInput.tsx`
- WorkPal fork: `apps/workpal/src/components/ChatInput.tsx`
- Wingman feature flag: `apps/wingman/src/components/ChatInput.tsx` (actionChips + attach + draft + forceSendActive + isAiResponding + mentions + voice + voiceInput)
- Wingman feature flag: `apps/wingman/src/components/chatInputMentionsCommands.test.tsx` (commands + mentions)
- Wingman feature flag: `apps/wingman/src/components/chatInputTooltipAria.test.tsx` (attach + voice + voiceInput)
- Wingman feature flag: `apps/wingman/src/pages/QuizChatPage.tsx` (actionChips + draft)
- Wingman feature flag: `apps/wingman/src/pages/SelfTestChatPage.tsx` (actionChips + draft)
- WorkPal feature flag: `apps/workpal/src/components/liveDictation.test.tsx` (attach + voiceInput)
- WorkPal feature flag: `apps/workpal/src/components/nestedOverlayPopovers.test.tsx` (attach)
- WorkPal feature flag: `apps/workpal/src/components/voiceComposerFuse.test.tsx` (attach + voice + voiceInput)
- WorkPal feature flag: `apps/workpal/src/components/voiceErrorFused.test.tsx` (attach + voice + voiceInput)

### AttachmentChip

Master Used in: Wingman 0 · WorkPal 1

One attached-file pill — 48×48 thumbnail or icon area + filename (truncates at `maxNameWidth`, default 160px) + size, on a `bg-bg-message` surface. Two optional affordances: `onRemove` (hover-revealed ×, used in the input pending strip) or `showDownload` (Download icon at the right edge, used in sent message bubbles paired with an `<a download>` wrapper). PR #54 unified the visual chrome — same chip across input + bubble.

Input strip — narrow, × on hover:

[image: 已生成图像 3.png]

已生成图像 3.png

1.7 MB

Remove 已生成图像 3.png

Beibei Zhang Resume.pdf

224.8 KB

Remove Beibei Zhang Resume.pdf

a-very-long-attachment-filename-that-should-truncate.docx

48.0 KB

Sent in bubble — wider, Download icon:

Beibei Zhang Resume (2).pdf

224.8 KB

### ChatPanel

Master + extended Used in: Wingman 3 · WorkPal 0

Conversation layout with message and composer slots and near-bottom scrolling.

Install component Copy code [View source](https://github.com/BeibeiZhang/frame-design-system/blob/main/components/chat-panel.tsx)

Requires React 19, ReactDOM 19, TypeScript and lucide-react 1.14.0. Import frame.css once and use a frame-root wrapper; set its background to var(--color-bg-page) and color to var(--color-text-primary), or supply a matching host surface; add dark to an ancestor for dark mode.

Tested with shadcn 4.21.0, React 19.2.5, Vite 8.0.10 and Tailwind 3.4.19 / 4.3.3. Start with a configured React/Vite shadcn app. Files install into src/components/frame; import its frame.css. For manual copy, save the complete component and stylesheet below. Examples leave optional voice features off; retain the MIT license and third-party notices.

[Required stylesheet: frame.css](https://github.com/BeibeiZhang/frame-design-system/blob/main/styles/frame.css)

```
npx shadcn@4.21.0 add https://raw.githubusercontent.com/BeibeiZhang/frame-design-system/main/registry/chat-panel.json
```

Copy install command Close

Container for a chat surface — optional toolbar + header (one frosted sticky layer the messages scroll beneath) + scrollable message list + pinned input. Auto-scrolls to bottom when content grows (unless user scrolled up).

Toggle sidebar

#### Saturday plans

Hi Beibei — what would you like to work on today?

Help me plan a date for Saturday evening.

Got it. I'll suggest a few options based on weather + your past picks.

Option 1: rooftop dinner at 7, then a walk by the water.

Something indoors in case it rains?

Sure — a small jazz bar nearby has seats at 9.

[field: Reply…]

Extension details (1 fork)

- WorkPal fork: `apps/workpal/src/components/ChatPanel.tsx`
