A dot-matrix disc that shows whether an agent is idle, listening, thinking or speaking.
Comparing roast notes
'use client';
import { useState } from 'react';
import { AgentThinking, Button } from '@brand-studio/ui';
const states = [
{ state: 'idle', label: 'Ready when you are' },
{ state: 'listening', label: 'Listening' },
{ state: 'thinking', label: 'Comparing roast notes' },
{ state: 'speaking', label: 'Answering' },
] as const;
export default function AgentThinkingDemo() {
const [current, setCurrent] = useState<(typeof states)[number]>(states[2]);
return (
<div style={{ display: 'grid', justifyItems: 'center', gap: 40 }}>
<AgentThinking state={current.state} label={current.label} size={72} />
<div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'center', gap: 8 }} role="group" aria-label="Agent state">
{states.map((item) => (
<Button key={item.state} tone={item === current ? 'primary' : 'secondary'} shape="pill" aria-pressed={item === current} onClick={() => setCurrent(item)}>
{item.state}
</Button>
))}
</div>
</div>
);
}Installation
pnpm add @brand-studio/uinpm install @brand-studio/uiyarn add @brand-studio/uibun add @brand-studio/uiInstall the peer dependencies
npm install react react-domImport the stylesheet once
import '@brand-studio/ui/styles.css';Wrap your app in a brand theme
import { BrandTheme, AgentThinking } from '@brand-studio/ui'; import brand from './brand.json'; <BrandTheme palette={brand.tokens} mode="system"> {/* your app */} </BrandTheme>
Usage
import { AgentThinking } from '@brand-studio/ui';<AgentThinking state="thinking" label="Comparing roast notes" />Examples
Sizes
size sets the width in pixels. Below 40 pixels the disc uses fewer, larger dots so it stays legible next to text.
Searching 14 orders
Listening
Speaking
import { AgentThinking } from '@brand-studio/ui';
export default function AgentThinkingSizes() {
return (
<div style={{ display: 'grid', gap: 24, justifyItems: 'start' }}>
<AgentThinking size={20} label="Searching 14 orders" />
<AgentThinking size={48} state="listening" />
<AgentThinking size={96} state="speaking" />
</div>
);
}Accessibility
- The component has
role="status". Withlabelit shows and announces that text; without it, screen readers hear the state name. - Each state moves differently, so it does not rely on colour alone. Under reduced motion the disc holds still and the label stays.
API Reference
| Prop | Type | Default |
|---|---|---|
state? | "idle" | "listening" | "speaking" | "thinking" | 'thinking' |
size? | number | 64 |
label? | string | – |
className? | string | '' |