{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "trace-waterfall",
  "title": "Trace Waterfall",
  "description": "An agent run on one timeline: agents, model calls and tools.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "utils",
    "https://ui.cuelark.com/r/theme.json",
    "https://ui.cuelark.com/r/tooltip.json"
  ],
  "files": [
    {
      "path": "src/registry/cuelark/ui/trace-waterfall.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { BotIcon, ChevronRightIcon, SparklesIcon, WrenchIcon } from \"lucide-react\";\n\nimport { Tooltip, TooltipContent, TooltipTrigger } from \"@/registry/cuelark/ui/tooltip\";\nimport { cn } from \"@/lib/utils\";\n\ntype SpanKind = \"agent\" | \"llm\" | \"tool\";\ntype TraceSpan = {\n  id: string;\n  name: string;\n  kind: SpanKind;\n  /** Seconds from the start of the run. */\n  start: number;\n  /** Omit while the span is still running. */\n  end?: number;\n  tokens?: number;\n  cost?: number;\n  error?: boolean;\n  children?: TraceSpan[];\n};\n\ntype TraceWaterfallProps = React.ComponentProps<\"div\"> & {\n  spans: TraceSpan[];\n  /** Current time in seconds for running spans; ticks on its own when omitted and something is running. */\n  now?: number;\n};\n\nconst ICON: Record<SpanKind, React.ComponentType<{ className?: string }>> = { agent: BotIcon, llm: SparklesIcon, tool: WrenchIcon };\nconst BAR: Record<SpanKind, string> = {\n  agent: \"bg-foreground/15 ring-1 ring-foreground/25 ring-inset\",\n  llm: \"bg-signal\",\n  tool: \"bg-foreground/70\",\n};\n\nfunction flatten(spans: TraceSpan[], open: Set<string>, depth = 0): { span: TraceSpan; depth: number }[] {\n  return spans.flatMap((s) => [{ span: s, depth }, ...(s.children && open.has(s.id) ? flatten(s.children, open, depth + 1) : [])]);\n}\n\n/**\n * An agent run on one timeline: nested agents, model calls and tools as bars, running spans growing live with a\n * shimmer at their edge. Hover a bar for duration, tokens and cost; collapse branches to focus.\n */\nfunction TraceWaterfall({ spans, now: nowProp, className, ...props }: TraceWaterfallProps) {\n  const [open, setOpen] = React.useState(() => new Set(spans.flatMap(function ids(s: TraceSpan): string[] { return [s.id, ...(s.children ?? []).flatMap(ids)]; })));\n  const all = flatten(spans, new Set([...open, ...spans.map((s) => s.id)])).map((r) => r.span);\n  const running = all.some((s) => s.end === undefined);\n  const [tick, setTick] = React.useState(() => Math.max(...all.map((s) => s.end ?? s.start)));\n  React.useEffect(() => {\n    if (nowProp !== undefined || !running) return;\n    const t0 = performance.now() - tick * 1000;\n    let raf = 0;\n    const loop = () => {\n      setTick((performance.now() - t0) / 1000);\n      raf = requestAnimationFrame(loop);\n    };\n    raf = requestAnimationFrame(loop);\n    return () => cancelAnimationFrame(raf);\n  }, [running, nowProp]);\n  const now = nowProp ?? tick;\n  const domain = Math.max(1, ...all.map((s) => s.end ?? now)) * 1.08;\n  const step = [0.5, 1, 2, 5, 10, 15, 30, 60].find((s) => domain / s <= 6) ?? 60;\n  const ticks = Array.from({ length: Math.floor(domain / step) + 1 }, (_, i) => i * step);\n  const rows = flatten(spans, open);\n  const toggle = (id: string) => setOpen((o) => (o.has(id) ? (o.delete(id), new Set(o)) : new Set(o).add(id)));\n\n  return (\n    <div data-slot=\"trace-waterfall\" className={cn(\"overflow-hidden rounded-xl border border-border bg-card text-sm\", className)} {...props}>\n      <div className=\"grid grid-cols-[minmax(9rem,14rem)_1fr] border-b border-border bg-muted/40 text-[0.6875rem] text-muted-foreground\">\n        <div className=\"cue-label px-3 py-2\">Span</div>\n        <div className=\"relative\">\n          {ticks.map((t) => (\n            <span key={t} className=\"absolute top-2 font-mono\" style={{ left: `calc(${(t / domain) * 100}% + 4px)` }}>\n              {t}s\n            </span>\n          ))}\n        </div>\n      </div>\n      <div role=\"tree\" aria-label=\"Trace\" className=\"relative\">\n        <div aria-hidden=\"true\" className=\"pointer-events-none absolute inset-y-0 right-0 left-[min(14rem,40%)]\">\n          {ticks.map((t) => (\n            <span key={t} className=\"absolute inset-y-0 w-px bg-border/60\" style={{ left: `${(t / domain) * 100}%` }} />\n          ))}\n        </div>\n        {rows.map(({ span, depth }) => {\n          const Icon = ICON[span.kind];\n          const end = span.end ?? now;\n          const live = span.end === undefined;\n          const hasKids = !!span.children?.length;\n          return (\n            <div key={span.id} role=\"treeitem\" aria-expanded={hasKids ? open.has(span.id) : undefined} className=\"grid grid-cols-[minmax(9rem,14rem)_1fr] items-center border-b border-border/60 last:border-b-0 hover:bg-muted/40\">\n              <div className=\"flex min-w-0 items-center gap-1.5 py-1.5 pe-2\" style={{ paddingInlineStart: 8 + depth * 16 }}>\n                {hasKids ? (\n                  <button type=\"button\" onClick={() => toggle(span.id)} aria-label={open.has(span.id) ? \"Collapse\" : \"Expand\"} className=\"relative after:absolute after:-inset-2 grid size-4 place-items-center rounded text-muted-foreground hover:text-foreground\">\n                    <ChevronRightIcon className={cn(\"size-3.5 transition-transform duration-200 ease-cue\", open.has(span.id) && \"rotate-90\")} />\n                  </button>\n                ) : (\n                  <span className=\"size-4\" />\n                )}\n                <Icon className={cn(\"size-3.5 shrink-0 text-muted-foreground\", span.error && \"text-destructive\")} />\n                <span className={cn(\"truncate font-mono text-xs\", span.error && \"text-destructive\")}>{span.name}</span>\n              </div>\n              <div className=\"relative h-7\">\n                <Tooltip>\n                  <TooltipTrigger asChild>\n                    <span\n                      tabIndex={0}\n                      className={cn(\"absolute top-1.5 h-4 min-w-1 rounded-[4px] outline-none focus-visible:ring-3 focus-visible:ring-ring/50\", span.error ? \"bg-destructive\" : BAR[span.kind], live && \"overflow-hidden\")}\n                      style={{ left: `${(span.start / domain) * 100}%`, width: `${((end - span.start) / domain) * 100}%` }}\n                    >\n                      {live && <span className=\"absolute inset-y-0 right-0 w-6 animate-pulse bg-gradient-to-r from-transparent to-white/60\" />}\n                    </span>\n                  </TooltipTrigger>\n                  <TooltipContent>\n                    <span className=\"font-mono\">\n                      {span.name} · {(end - span.start).toFixed(2)}s{span.tokens ? ` · ${span.tokens.toLocaleString()} tok` : \"\"}\n                      {span.cost ? ` · $${span.cost.toFixed(3)}` : \"\"}\n                      {live ? \" · running\" : \"\"}\n                    </span>\n                  </TooltipContent>\n                </Tooltip>\n                <span className=\"pointer-events-none absolute top-1.5 font-mono text-[0.625rem] leading-4 text-muted-foreground\" style={{ left: `calc(${(end / domain) * 100}% + 6px)` }}>\n                  {(end - span.start).toFixed(1)}s\n                </span>\n              </div>\n            </div>\n          );\n        })}\n      </div>\n      <div className=\"flex items-center gap-4 border-t border-border bg-muted/30 px-3 py-2 text-[0.6875rem] text-muted-foreground\">\n        {([\"agent\", \"llm\", \"tool\"] as const).map((k) => (\n          <span key={k} className=\"flex items-center gap-1.5\">\n            <span className={cn(\"h-2 w-3 rounded-[2px]\", BAR[k])} />\n            {k === \"llm\" ? \"model\" : k}\n          </span>\n        ))}\n        <span className=\"ms-auto font-mono\">\n          {all.reduce((a, s) => a + (s.tokens ?? 0), 0).toLocaleString()} tok · ${all.reduce((a, s) => a + (s.cost ?? 0), 0).toFixed(3)}\n        </span>\n      </div>\n    </div>\n  );\n}\n\nexport { TraceWaterfall };\nexport type { TraceSpan, TraceWaterfallProps, SpanKind };\n",
      "type": "registry:ui"
    }
  ],
  "docs": "Docs and examples: https://ui.cuelark.com/components/trace-waterfall",
  "categories": [
    "agents"
  ],
  "type": "registry:ui"
}