# Chat Demo (Moonarc)

A conversation with an AI agent, replayed in the page: the reader's message, the agent's reply with its tool calls folded into <details> (the call and its result in the monospace face), a request for approval that is approved, and the final answer, typed, with a button that copies it; a prompt box at the foot as a demo form. The server HTML is the end state. The replay is CSS on one clock, started when the chat comes into view, held by a native checkbox drawn as a Pause button and by a pointer, and done in 8 to 12 s. Several conversations can be keyed by slug, and a small internal script picks one from the address (?task=refactor); the default renders without it.

- Pro block. Install: `npx shadcn@latest add @moonarc-pro/chat-demo` (license key required)
- Tier B · category section · trigger scroll, hover, click
- Readout: `<ChatDemo chats={[{ slug, prompt, reply, calls, ask, answer }]}>`
- Browser support: limited (Chrome 116 · not Firefox · Safari 18); elsewhere: without :has() the chat plays once and cannot be held; where display does not animate (Firefox) the answer still types in turn through the font-size in the same keyframes; it ends on the whole conversation either way
- Measured cost: 0 B JS of its own · uses Reveal + runtime + CopyButton + PromptInputSubmitOnEnter (with dependencies 4.1 kB raw; CSS 32.6 kB raw)
- Page: https://moonarc.dev/components/chat-demo/

## Props

| Prop | Type | Default | Description |
|---|---|---|---|
| `chats` | `{ slug, prompt, reply, calls, ask, answer }[]` | none | The conversations, up to six. prompt: the reader's message. reply: the agent's first words. calls: one to four { name, args?, took?, result }, each folded into a <details> with its result. ask: the request for approval. answer: the final answer, typed, with a copy button. slug keys it in the address. |
| `active` | `string` | none | The slug of the conversation shown by default; the first unless set. |
| `param` | `string` | `'task'` | The query parameter that picks a conversation (/demo/?task=refactor). With more than one conversation this adds a small internal script, measured on its own; an unknown slug keeps the default. |
| `names` | `{ user?, agent? }` | `You · Agent` | Who is speaking: every message is a list item that starts with its speaker's name for screen readers, and the agent's name is shown in the chat's bar. |
| `mark` | `ImageMetadata | string` | none | The agent's mark beside its name. Unset, its initial on the accent. |
| `headline` | `string` | none | The headline above the chat. The headline slot wins over it. |
| `eyebrow` | `string` | none | Small label above the headline. The eyebrow slot wins over it. |
| `copy` | `string` | none | The paragraph under the headline. The copy slot wins over it. |
| `labels` | `{ log?, working?, running?, done?, approve?, approved?, copy?, copied?, placeholder?, prompt?, send? }` | `Conversation · Working · Running · Done · Approve · Approved · Copy the answer · Copied · Ask a follow-up` | The words the chat prints around the conversation: the list's name, the working line, the status, the approve before and after it is pressed, the copy button's name (it says what it copies) and its done face, and the prompt box's placeholder, name and send button. |
| `pauseLabel` | `string` | `'Pause'` | The pause control's label: a checkbox drawn as a small button, checked while the chat is held. |
| `input` | `boolean` | `true` | The prompt box at the foot. |
| `action` | `string` | `'/demo/'` | Where the foot's form goes: a demo page by default. |
| `method` | `'get' | 'post'` | `'get'` | The foot's form method. |
| `name` | `string` | none | The foot's textarea name. Unset, it has none and the form sends no text. |
| `level` | `1 | 2` | `2` | Heading level of the headline: 2 under the page's h1, 1 when the section opens the page. |
| `tone` | `'surface' | 'muted' | 'ink' | 'brand'` | `unset (paints nothing)` | The ground: the page surface, a muted band, an always-dark ink panel, or a deep brand panel. With theme.css the chat's panel takes the tone's card; on brand what is live is marked in the text colour. |
| `density` | `'compact' | 'cozy' | 'roomy'` | `'cozy'` | Block padding and the gap above the chat ×0.8 / ×1 / ×1.25 through --ma-density. |

## Usage

```astro
---
import ChatDemo from '@/components/moonarc/pro/ChatDemo.astro';
import chats from '../content/chats.json';
---
<!-- /demo/?task=update-deps shows that conversation; the first is the default, and what renders without JavaScript -->
<ChatDemo
  level={1}
  headline="Watch the agent work"
  names={{ user: 'You', agent: 'Agent' }}
  chats={chats}
  action="/demo/"
/>
```

## Reduced motion

The whole conversation at once: every message there, the calls folded, the approval given, the answer whole and its copy button shown. Nothing travels and the Pause control is not drawn.

## With ClientRouter

CSS only; the copy button, the prompt box's Enter and the conversation picker rebind through the shared runtime. A new page replays the chat when it comes into view.

## Craft

- The end state is the markup, as in AgentRun: each message animates from a start state back to its own style with fill both, so the replay lands on what the server sent and no script has to reveal a message.
- Typewriter types the answer on the chat's clock: its characters take the chat's play state, so a held chat holds the typing, and the answer types over a hidden copy of itself so nothing under it moves.
- Spinner turns on its own clock, so while the chat is held it is replaced by a still arc: held means every animation reports paused.
- The tool calls are native <details>: a click, Enter or Space opens one, and the call and what it returned are machine text in the monospace face. They start folded, as a reader skimming the chat would want them.
- Every message is a list item that starts with its speaker's name for screen readers, so the conversation reads as a conversation, not as a column of paragraphs.
- The copy button says what it copies (Copy the answer), set through CopyButton's tokens; the prompt box at the foot sends no text unless it is given a name.

## Replaces

- Chat demo (Vercel AI Elements)
- AI chat mockup (Magic UI)

## Source

Pro block. The source is served by the license-gated registry: `npx shadcn@latest add @moonarc-pro/chat-demo` with a key in components.json (https://moonarc.dev/account/). Everything it composes is free: reveal, spinner, typewriter, copy-button, prompt-input.
