Skip to main content

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):

StatusAliases
donecomplete
activecurrent
errorfail
pendingwait
skippedskip

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');
ParameterTypeDescription
messageIdstringThe message id that contains the timeline
stepnumberZero-based step index
statusTimelineStatus'done' | 'active' | 'error' | 'pending' | 'skipped'
bidstring?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​

PropertyDerives fromDescription
--chat-timeline-done--chat-successDone step indicator color
--chat-timeline-active--chat-primaryActive step indicator color
--chat-timeline-error--chat-errorError step indicator color
--chat-timeline-line--chat-borderConnector line color
--chat-timeline-pending-border--chat-borderPending step border color
--chat-timeline-indicator-size--chat-font-sizeIndicator circle diameter