Data display
Chat messages
The conversation log for copilots: attributed messages, streaming, tool calls.
import { ChatMessages, ChatMessage, ChatToolCall } from '@jsonresume/ui'Source(opens in a new tab)
Examples
YouAdd Rust to my skills.
Copilot
update_resume· Done· skills +1
{ "skills.backend": ["Rust"] }Done. I added Rust under Backend. Want me to mention it in your summary too?
Show code
chat/copilot.tsx
'use client';
import {
ChatComposer,
ChatMessage,
ChatMessages,
ChatToolCall,
Markdown,
Stack,
} from '@jsonresume/ui';
import { useState } from 'react';
import * as styles from './copilot.css';
interface Turn {
role: 'user' | 'assistant';
text: string;
}
export default function ChatCopilot() {
const [turns, setTurns] = useState<Turn[]>([
{ role: 'user', text: 'Add Rust to my skills.' },
{
role: 'assistant',
text: 'Done. I added **Rust** under *Backend*. Want me to mention it in your summary too?',
},
]);
const [pending, setPending] = useState(false);
return (
<Stack gap={3} className={styles.root}>
<ChatMessages
label="Copilot conversation"
scrollKey={turns.length + Number(pending)}
className={styles.log}
>
{turns.map((t, i) =>
t.role === 'user' ? (
<ChatMessage key={i} role="user">
{t.text}
</ChatMessage>
) : (
<ChatMessage key={i} role="assistant" author="Copilot">
{i === 1 ? (
<ChatToolCall
name="update_resume"
status="done"
summary="skills +1"
details={'{ "skills.backend": ["Rust"] }'}
/>
) : null}
<Markdown>{t.text}</Markdown>
</ChatMessage>
)
)}
{pending ? (
<ChatMessage role="assistant" author="Copilot" pending />
) : null}
</ChatMessages>
<ChatComposer
busy={pending}
onSubmit={(text) => {
setTurns((all) => [...all, { role: 'user', text }]);
setPending(true);
setTimeout(() => {
setPending(false);
setTurns((all) => [
...all,
{
role: 'assistant',
text: 'Good idea. I can draft that for you.',
},
]);
}, 1200);
}}
/>
</Stack>
);
}chat/copilot.css.ts
import { style } from '@vanilla-extract/css';
import { vars } from '@jsonresume/ui/tokens';
export const root = style({ width: 'min(100%, 40rem)' });
export const log = style({
height: '20rem',
border: `1px solid ${vars.color.border.subtle}`,
borderRadius: vars.radius.sm,
});Accessibility
- A labelled log region announcing new messages politely.
- Each message is an article named by its author; the typing state is spoken.
- The view follows new messages only while the reader is at the bottom.
Props
<ChatMessages>
| Prop | Type | Default | Description |
|---|---|---|---|
| label | string | Conversation | |
| scrollKey | unknown | – | Change this (e.g. message count or streamed length) to keep the view pinned to the newest message. |
<ChatMessage>
| Prop | Type | Default | Description |
|---|---|---|---|
| role required | userassistantsystem | – | |
| author | string | – | Shown above the message and to screen readers ("You", "Copilot"). |
| avatarUrl | string | null | – | |
| pending | boolean | false | Show a typing indicator (no content yet). |
<ChatToolCall>
| Prop | Type | Default | Description |
|---|---|---|---|
| name required | string | – | The tool's name, e.g. update_resume. |
| status required | donerunningerror | – | |
| details | string | – | Input or output, shown when expanded. |
| summary | ReactNode | – | A one-line summary of what it did. |