Skip to content

Timeline / activity feed

In review
Live · web

Session run: events on a rail · status nodes · tab/click the top event

  1. Error: read_file timed out

    ETIMEDOUT after 30s · retried once, then skipped

  2. tool_call: edit_file

    src/components/Timeline.tsx · +42 −6

  3. tool_call: read_filedone

    docs/design-laws.md

  4. 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

  1. Deploy succeeded

    Zero-downtime swap · design-system

    highlighted, live
  2. CI passed

    typecheck · lint · build · test

  3. Approval required

    credential-store · allowlist scope change

  4. Build failed

    dueydo · exited 1

  5. Branch pushed

    main · 3 commits

Grouped by day: audit log with “load earlier”

  1. Tool call: run_testsrunning…

    in progress, live
  2. Memory written

    “No em dashes in emails” → decisions

  1. PR merged

    · timeline component

  2. Error: 403 from the credential store

    URL not in allowlist

Compact, dense activity stream

  1. tool_call: greprunning…

    in progress, live
  2. tool_call: read_file

  3. tool_call: edit_file

  4. thinking…

  5. Turn complete

    4 tools · 3.1k tok

Two-sided: events alternate either side of a center rail (roadmap / release)

  1. Tokens published

    @dizyx/nockerl-tokens v1

  2. Docs site live

    Astro Starlight catalog

  3. Component review

    one-by-one sign-off

    highlighted, live
  4. Compose 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)}
/>
);
}
PropTypeDefaultDescription
events *TimelineEvent[]The chronological events, newest first. Each: { id, title, time, status?, description?, datetime?, icon?, mono?, meta?, hollow?, live? }.
densebooleanfalseCompact density: tighter rows + smaller nodes for a dense activity stream.
onEventActivate(id: string) => voidWhen set, each event becomes a real <button> (opens detail). Keyboard-operable with a focus-visible ring.
surfacestringcard.surface1The 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.

Cross-platform drift (tracked for reconciliation)

Timeline is the chronological event feed (each node is a moment in time: timestamp + what happened), distinct from the Stepper (a forward process with done/current/upcoming flow state) and the List (plain rows). These are the real divergences:

  • Designed, not extracted. The look + API are drawn from the event-sequence surfaces the apps already have: Android’s AgentTranscriptPanel + AgentTranscriptSegments (a time-ordered run of session_init / tool_use / done / error events) and NotificationRow (formatTimeAgo); Swift’s HistorySection (a reverse-chronological timestamped list). The NockerlTimeline symbols above are the intended components to extract.
  • Relative time: the demo uses Android’s formatTimeAgo ramp (just now · 2m · 1h · 3d · 1w · 1mo); Swift’s HistorySection renders an absolute month().day().hour().minute() instead. One format (relative gutter + absolute <time> tooltip) should win.
  • Two-sided + load-earlier: web-original here; the apps’ feeds are single-sided and paginate via scroll. Track before adopting on Android/Swift.