Timeline / activity feed
Session run: events on a rail · status nodes · tab/click the top event
Error: read_file timed out
ETIMEDOUT after 30s · retried once, then skipped
tool_call: edit_file
src/components/Timeline.tsx · +42 −6
tool_call: read_file
docs/design-laws.md
Session started
Large 2.0 · 14 tools
Top event is a button. Tab to it and press Enter.
Status nodes: success · warning · error · neutral, and a live “now” node
Deploy succeeded
Zero-downtime swap · design-system
highlighted, liveCI passed
typecheck · lint · build · test
Approval required
credential-store · allowlist scope change
Build failed
dueydo · exited 1
Branch pushed
main · 3 commits
Grouped by day: audit log with “load earlier”
Tool call: run_tests
in progress, liveMemory written
“No em dashes in emails” → decisions
PR merged
· timeline component
Error: 403 from the credential store
URL not in allowlist
Compact, dense activity stream
tool_call: grep
in progress, livetool_call: read_file
tool_call: edit_file
thinking…
Turn complete
4 tools · 3.1k tok
Two-sided: events alternate either side of a center rail (roadmap / release)
Tokens published
@dizyx/nockerl-tokens v1
Docs site live
Astro Starlight catalog
Component review
one-by-one sign-off
highlighted, liveCompose package
Maven publish
Live animation is running. The “now” node fades opacity; it freezes under reduced-motion.
// Web: React, consuming @dizyx/nockerl-tokens. (Web is the laggard platform. Spec + live demo,// not yet exported from @dizyx/nockerl-react; promotion is tracked.)
export function SessionActivity() { return ( <Timeline events={[ { id: 'e1', status: 'success', time: 'just now', datetime: '2026-06-27T14:32:10', title: 'Session completed', description: 'done · 1m 48s · 18.2k tok' }, { id: 'e2', status: 'error', time: '1m', title: 'Error: read_file timed out', description: 'ETIMEDOUT after 30s', mono: true }, { id: 'e3', status: 'info', time: '2m', title: 'tool_call: read_file', meta: 'running…', mono: true, hollow: true }, { id: 'e4', status: 'success', time: '2m', title: 'Session started', description: 'Large 2.0 · 14 tools' }, ]} onEventActivate={(id) => openEvent(id)} /> );}// Android: Jetpack Compose. No dedicated NockerlTimeline ships yet; the// chronological event feed IS the agent transcript. This is the intended API,// designed from AgentTranscriptPanel + AgentTranscriptSegments.groupSegments()// (a time-ordered run of events) + NotificationRow's formatTimeAgo. See drift.import com.nockerl.app.core.ui.NockerlTimelineimport com.nockerl.app.core.ui.TimelineEventimport com.nockerl.app.core.ui.EventStatus
NockerlTimeline( events = listOf( TimelineEvent("e1", title = "Session completed", description = "done · 1m 48s · 18.2k tok", time = "just now", status = EventStatus.SUCCESS), TimelineEvent("e2", title = "Error: read_file timed out", description = "ETIMEDOUT after 30s", time = "1m", status = EventStatus.ERROR, mono = true), TimelineEvent("e3", title = "tool_call: read_file", time = "2m", status = EventStatus.INFO, mono = true, hollow = true, meta = "running…"), TimelineEvent("e4", title = "Session started", description = "Large 2.0 · 14 tools", time = "2m", status = EventStatus.SUCCESS), ), onEventClick = viewModel::openEvent,)// macOS: SwiftUI. No dedicated timeline ships yet; the closest surface is// HistorySection (a reverse-chronological list of timestamped rows) in// NockerlVoice/UI/HistoryView.swift. This is the intended API. See the drift note.NockerlTimeline(events: [ .init(id: "e1", title: "Session completed", description: "done · 1m 48s · 18.2k tok", time: "just now", status: .success), .init(id: "e2", title: "Error: read_file timed out", description: "ETIMEDOUT after 30s", time: "1m", status: .error, mono: true), .init(id: "e3", title: "tool_call: read_file", time: "2m", status: .info, mono: true, hollow: true, meta: "running…"), .init(id: "e4", title: "Session started", description: "Large 2.0 · 14 tools", time: "2m", status: .success),]) { event in open(event)}Parameters
Section titled “Parameters”| Prop | Type | Default | Description |
|---|---|---|---|
events * | TimelineEvent[] | The chronological events, newest first. Each: { id, title, time, status?, description?, datetime?, icon?, mono?, meta?, hollow?, live? }. | |
dense | boolean | false | Compact density: tighter rows + smaller nodes for a dense activity stream. |
onEventActivate | (id: string) => void | When set, each event becomes a real <button> (opens detail). Keyboard-operable with a focus-visible ring. | |
surface | string | card.surface1 | The host surface token the nodes notch into, so the dot reads as a shape on that surface. |
TimelineEvent fields: status is 'success' | 'warning' | 'error' | 'info' | 'neutral' | 'accent'
(warm status + the cyan accent for the one highlight / live event). time is the relative
gutter label ("2m"); datetime is the absolute ISO for the <time> element. live pulses the
node; mono renders a tool-call title; hollow is a quieter routine node; meta is an inline chip.
| Parameter | Type | Default | Description |
|---|---|---|---|
events * | List<TimelineEvent> | The chronological events, newest first. TimelineEvent(id, title, time, status, description?, datetime?, icon?, mono?, meta?, hollow?, live?). | |
modifier | Modifier | Modifier | External modifier for the feed container (the lifted card the rail sits on). |
dense | Boolean | false | Compact density: tighter rows + smaller nodes. |
onEventClick | ((String) -> Unit)? | null | When non-null, each event becomes clickable (ripple + the event id is passed back). |
EventStatus enum: SUCCESS · WARNING · ERROR · INFO · NEUTRAL · ACCENT. Status is never color
alone: the node also carries a glyph (a check on done, a close on error) and a content description.
| Style | Type | Default | Description |
|---|---|---|---|
events * | [TimelineEvent] | The chronological events, newest first. TimelineEvent(id:title:time:status:description:datetime:mono:meta:hollow:live:). | |
dense | Bool | false | Compact density: tighter rows + smaller nodes. |
onActivate | ((TimelineEvent) -> Void)? | nil | Trailing closure. When set, each event row is a button that hands back the event. |
status | EventStatus | Per-event enum: .success · .warning · .error · .info · .neutral · .accent. Drives the node hue + glyph. |