StateTimeline
Lane-per-entity discrete state bands over a shared time window.
Source
"use client"
import { StateTimeline } from "@/registry/warren/state-timeline/state-timeline"
const LANES = [
{ id: "ingest", label: "INGEST" },
{ id: "index", label: "INDEX" },
{ id: "notify", label: "NOTIFY" },
]
/** Two lanes, one shared time window, no interaction; `now` marker mid-window. */
const SEGMENTS = [
{ lane: "ingest", start: 0, end: 9, state: "running", label: "batch-41" },
{ lane: "ingest", start: 9, end: 11, state: "queued" },
{ lane: "ingest", start: 11, end: 15, state: "done" },
{ lane: "index", start: 2, end: 7, state: "running", label: "shard-rebuild" },
{ lane: "index", start: 7, end: 8.5, state: "failed" },
{ lane: "index", start: 8.5, end: 13, state: "running" },
{ lane: "index", start: 13, end: 15, state: "done" },
{ lane: "notify", start: 12, end: 14.5, state: "queued" },
]
const STATES = {
queued: "warning",
running: "primary",
failed: "danger",
done: "success",
} as const
export default function StateTimelineDemo() {
return (
<div className="flex flex-col gap-3 border-2 border-border bg-panel p-2">
<span className="font-mono text-[10px] uppercase tracking-wide text-muted-foreground">
PIPELINE LANES · WINDOW 0–15S · TOOLTIPS ON
</span>
<StateTimeline lanes={LANES} segments={SEGMENTS} states={STATES} now={10} showTooltips />
</div>
)
}
Installation
npx shadcn@latest add https://warren.eduard3v.com/r/state-timeline.jsonUsage
import { StateTimeline } from "@/components/ui/state-timeline"<StateTimeline
lanes={[
{ id: "api", label: "API" },
{ id: "worker", label: "WORKER" },
]}
segments={[{ lane: "api", start: 0, end: 10, state: "running" }]}
states={{ queued: "warning", running: "primary", failed: "danger", done: "success" }}
/>Encoding
Each lane renders as one horizontal band row; segments get duration-proportional widths
inside the shared time domain (default: the full extent of all segments). Segment color is
looked up from states — signal color = state, never decoration; unknown states fall back to
muted. Touching or overlapping same-state segments in a lane are merged before layout
(mergeSegments in layout.ts), so tick-driven appends don't fragment runs.
The optional now marker draws a 1px ink line through every track at that timestamp,
clipped at the domain edges.
Interactive
Source
"use client"
import { useState } from "react"
import { StateTimeline } from "@/registry/warren/state-timeline/state-timeline"
import type { TimelineSegment } from "@/registry/warren/state-timeline/state-timeline"
const LANES = [
{ id: "ingest", label: "INGEST" },
{ id: "index", label: "INDEX" },
]
const SEGMENTS = [
{ lane: "ingest", start: 0, end: 9, state: "running", label: "batch-41" },
{ lane: "ingest", start: 9, end: 11, state: "queued" },
{ lane: "ingest", start: 11, end: 15, state: "done" },
{ lane: "index", start: 2, end: 7, state: "running", label: "shard-rebuild" },
{ lane: "index", start: 7, end: 8.5, state: "failed" },
{ lane: "index", start: 8.5, end: 13, state: "running" },
]
const STATES = {
queued: "warning",
running: "primary",
failed: "danger",
done: "success",
} as const
export default function StateTimelineInteractive() {
const [last, setLast] = useState<TimelineSegment | null>(null)
return (
<div className="flex flex-col gap-3 border-2 border-border bg-panel p-2">
<span className="font-mono text-[10px] uppercase tracking-wide text-muted-foreground">
CLICK A SEGMENT
</span>
<StateTimeline
lanes={LANES}
segments={SEGMENTS}
states={STATES}
onSegmentClick={setLast}
/>
<span className="font-mono text-[10px] tabular-nums text-muted-foreground">
{last ? `${last.lane} · ${last.state} · ${last.start}–${last.end}` : "NO SELECTION"}
</span>
</div>
)
}
With onSegmentClick, segments render as real <button>s and the container flips from
role="img" to role="group". Segments always carry an aria-label
("<label|state> <start>–<end>"); set showTooltips for native title tooltips.
API Reference
Prop
Type
Prop
Type
Prop
Type