Skip to content
Twentyfirst UIPro

Chat Message

AI · ChatMessage

View as Markdown

One turn in a conversation with an agent: user bubble on the right, agent reply as rich text on the left.

Props: role ("user" | "assistant" | "system"), name, time, model, text (Markdown subset: paragraphs, bold, code, - lists, 1. lists, ### headings, links, `` code blocks), streaming (blinking caret, no actions yet), tint (agent color), sources ({title, icon, url | onClick}), attachments ({name, size}), usage (tokens · cost text), onRetry, onFeedback("up" | "down"), actions (false hides the row), before / after` (slots for tool calls, reasoning, approvals).

  • User messages are ink pills, agent replies are plain text on the surface: the agent's words get the space.
  • Actions (copy, regenerate, thumbs) appear once streaming ends.
  • Number sources in the order they were used; keep titles short.
  • RichText and CodeBlock are exported on their own for docs, notes and logs.
ME
Mara Ellis

Which invoices are overdue? Fix the reminder job too.

reminders.ts4 KB
Ledger09:41Claude Sonnet 5.5

Three invoices are overdue, €2,520 in total:

  • #2041 Harbor & Co · €860
  • #2058 Pinegate · €1,240
  • #2063 Orbit Foods · €420

The fix for the reminder job:

ts
const due = invoices.filter(i => i.dueAt < now && !i.paid);
await sendReminders(due, { tone: "friendly" });
1,842 tokens · €0.01
Ledger switched to Claude Haiku 5.5
Ledger

Sending reminders now. Harbor & Co first because

Props

ChatMessage

PropTypeNotes
role"user" | "assistant" | "system"Required
name?string
time?string
model?string
text?string
streaming?boolean
tint?Tint
sources?Source[]
attachments?{ name: string; size?: string; kind?: string }[]
usage?string
onRetry?() => void
onFeedback?(v: "up" | "down") => void
actions?boolean
before?ReactNode
after?ReactNode
children?ReactNode
feedback?"up" | "down" | null
className?stringExtra class on the root element.
style?CSSPropertiesInline style on the root element.