GitHub

Step Waterfall

A run's per-step timing as a horizontal waterfall — each step a bar positioned by its start offset and sized by its duration along a shared time axis, with expandable loop containers and a slowest-step callout.

Where the Run Timeline answers "what ran and what did it return," the Step Waterfall answers "where did the time go." Each step is a horizontal bar positioned by its start offset and sized by its duration along a shared time axis, so you can see at a glance which steps ran when and which one dominated the run.

Step timing waterfallTotal: 6.0s
01.5s3.0s4.5s6.0s
Slowest step: Extract · page 2 (2.0s)

Loop blocks (for_each, while_loop) render as expandable container rows with their iterations nested beneath; hover a bar to see its type, duration, offset and status; the footer calls out the slowest step.

Usage

Pass a run with each step's startOffsetMs and durationMs (offsets relative to the start of the run). totalDurationMs is computed from the steps when omitted. Everything is presentation only.

import { StepWaterfall } from "@/registry/new-york-v4/blocks/step-waterfall-block"
 
export function Example() {
  return (
    <div className="h-[420px] overflow-hidden rounded-xl border">
      <StepWaterfall
        run={{
          steps: [
            { id: "parse", label: "Parse PDF", blockType: "parse", status: "completed", startOffsetMs: 0, durationMs: 920 },
            {
              id: "pages",
              label: "For each page",
              blockType: "for_each",
              status: "completed",
              startOffsetMs: 930,
              durationMs: 3800,
              container: true,
              children: [
                { id: "p1", label: "Extract · page 1", blockType: "extract", status: "completed", startOffsetMs: 930, durationMs: 1800 },
                { id: "p2", label: "Extract · page 2", blockType: "extract", status: "completed", startOffsetMs: 2730, durationMs: 2000 },
              ],
            },
          ],
        }}
      />
    </div>
  )
}

Deriving offsets from timestamps

If your steps carry started_at / completed_at, compute the inputs against the earliest start:

const runStart = Math.min(...steps.map((s) => +new Date(s.started_at)))
 
const waterfallSteps = steps.map((s) => ({
  id: s.id,
  label: s.label,
  blockType: s.block_type,
  status: s.status,
  startOffsetMs: +new Date(s.started_at) - runStart,
  durationMs: +new Date(s.completed_at) - +new Date(s.started_at),
}))

API

WaterfallRun

FieldTypeDescription
stepsWaterfallStep[]The steps, in display order.
totalDurationMsnumberThe time axis span. Computed from the latest step end when omitted.

WaterfallStep

FieldTypeDescription
idstringStable key.
labelstringThe step's name, shown in the label column and tooltip.
blockTypestringDrives the row icon (parse, extract, api_call, for_each, …).
status"completed" | "running" | "error" | "skipped" | "pending" | "awaiting_review"Drives the bar color.
startOffsetMsnumberOffset from the start of the run.
durationMsnumberLength of the bar. A 0.5% minimum keeps tiny steps visible.
containerbooleanRender as an expandable loop row.
childrenWaterfallStep[]Nested iterations / sub-steps, shown indented when expanded.

Components

ExportPropsDescription
StepWaterfall{ run: WaterfallRun }The full waterfall with time axis and slowest-step footer.
StepWaterfallBlock{ run?: WaterfallRun }The waterfall preloaded with a sample run; pass run to override.