Components
Thread panel
Switch between agent threads and reply in place.
A thread panel holds several conversations with an agent, one tab each, with a reply field
under the open one. The tabs switch which thread is shown and an underline slides to the open
one; switching staggers that thread's messages in, and a later message glides into view. Each
agent message can carry sources — the files or pages it used — and a thinking message
shimmers while the agent works on a reply.
Preview
Why is the workspace step losing people?
It asks for a team name before anyone has made anything. Moving it after the first project, with the name filled from the email domain, should help.
Usage
<!-- include core.css + thread-panel.css -->
<div class="dm-thp dm-panel">
<div class="dm-thp-tabs" role="tablist">
<button class="dm-thp-tab" role="tab" aria-selected="true">
<span class="dm-dot" style="color:var(--dm-ok-mark)"></span>Onboarding
</button>
<!-- more tabs -->
<span class="dm-thp-ink"></span>
</div>
<div class="dm-thp-body" aria-live="polite">
<div class="dm-bub">Why is the workspace step losing people?</div>
<div class="dm-thp-ag">Moving the team-name step after the first project should help.</div>
</div>
<form class="dm-thp-in">
<input class="dm-thp-field" aria-label="Reply" placeholder="Reply to the agent" />
<button class="dm-thp-send" aria-label="Send" disabled><!-- up arrow --></button>
</form>
</div>Properties
| Name | Type | Description |
|---|---|---|
threads | Thread[] | The conversations, one tab each. |
value | string | The id of the open thread. Leave it out to let the tabs switch it. |
defaultValue | string | The thread open at first when uncontrolled. Defaults to the first thread. |
onValueChange | (id) => void | Called when a tab opens another thread. |
onSend | (text, threadId) => void | Called with the reply and the open thread's id. Add the message to that thread to show it. |
draft | string | The reply being written. Leave it out to let the field keep its own. |
defaultDraft | string | The reply written at first when uncontrolled. |
onDraftChange | (text) => void | Called as the reply field changes. |
placeholder | string | The reply field's placeholder. Defaults to Reply to the agent. |
bare | boolean | Sits flat in a chat column (the default). Set false to give it a card of its own. |
Thread.color | string | The dot in front of the title, any CSS colour. Defaults to a quiet grey. |
ThreadMessage.sources | Source[] | Files or pages the agent used, shown as chips above its reply. |
ThreadMessage.thinking | boolean | Shows a shimmering line while the agent works on a reply. |
Theming
Works in light and dark (data-theme="dark") and in the Soft style (data-style="soft"). The
open tab's underline uses the accent mark token and the reply field sits on a surface with an
inset field line, so it reads against the dark stage; in Soft the field is rounder (squircle).