Timeline
Ordered lists with [status] prefixes are rendered as vertical timelines with step indicators.
Markdown syntax
1. [done] Collect requirements
2. [active] Implement API
3. [pending] Write tests
4. [error] Deploy to staging (rollback triggered)
5. [skipped] Performance benchmarking
Supported status keywords (case-insensitive):
| Status | Aliases |
|---|---|
done | complete |
active | current |
error | fail |
pending | wait |
skipped | skip |
Block ID (bid)
When a message contains multiple timelines, add a <!-- bid:xxx --> comment before each list to assign a unique identifier:
<!-- bid:build -->
1. [pending] Build Docker image
2. [pending] Run test suite
3. [pending] Push to registry
<!-- bid:deploy -->
1. [pending] Deploy to staging
2. [pending] Run smoke tests
3. [pending] Promote to production
The comment is stripped during rendering — it only serves as metadata.
Programmatic status updates
Use updateTimeline() on <i-chat> (same method as the inner list) to change a step’s status after the message has been rendered:
// Single timeline (targets the first timeline in the message)
chatEl.updateTimeline(messageId, step, status);
// Multiple timelines — use bid to target the right one
chatEl.updateTimeline(messageId, 0, 'done', 'build');
chatEl.updateTimeline(messageId, 1, 'active', 'deploy');
| Parameter | Type | Description |
|---|---|---|
messageId | string | The message id that contains the timeline |
step | number | Zero-based step index |
status | TimelineStatus | 'done' | 'active' | 'error' | 'pending' | 'skipped' |
bid | string? | Optional block id; when omitted, targets the first timeline |
SSE integration
Timelines that need dynamic status updates are typically generated by the backend orchestration logic (agent frameworks, pipelines), not by the AI model. The workflow has two phases:
Phase 1 — Define the timeline (markdown inside a text or reasoning part):
data: {"reasoning": "<!-- bid:agent -->\n1. [pending] Search documents\n2. [pending] Analyze results\n3. [pending] Generate response\n"}
Map that stream into a part — e.g. appendPart(messageId, reasoningPart(md, { id: 'plan' })) then grow it with updatePart. The <!-- bid:agent --> annotation and [pending] status markers live inside the part’s markdown, so the bid stays associated with its timeline regardless of how the stream is chunked or re-rendered.
Phase 2 — Update step statuses (structured event):
data: {"timeline": {"bid": "agent", "step": 0, "status": "done"}}
data: {"timeline": {"bid": "agent", "step": 1, "status": "active"}}
The frontend parses these events and calls on <i-chat>:
chatEl.updateTimeline(messageId, ev.timeline.step, ev.timeline.status, ev.timeline.bid);
For single-timeline messages, bid can be omitted in both phases.
Timeline CSS custom properties
| Property | Derives from | Description |
|---|---|---|
--chat-timeline-done | --chat-success | Done step indicator color |
--chat-timeline-active | --chat-primary | Active step indicator color |
--chat-timeline-error | --chat-error | Error step indicator color |
--chat-timeline-line | --chat-border | Connector line color |
--chat-timeline-pending-border | --chat-border | Pending step border color |
--chat-timeline-indicator-size | --chat-font-size | Indicator circle diameter |