{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "drift-chart",
  "type": "registry:component",
  "title": "Drift Chart",
  "description": "An agent's plan drawn as dashed outlines with the real work filling in over them: when a step runs long it spills into amber, nudges everything after it to the right and drags the finish line with it.",
  "categories": [
    "component",
    "Mission Control"
  ],
  "dependencies": [
    "lucide-react",
    "motion"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/devl/drift-chart.tsx",
      "type": "registry:component",
      "content": "\"use client\";\n\nimport { CheckIcon } from \"lucide-react\";\nimport { motion, useReducedMotion } from \"motion/react\";\nimport { useEffect, useLayoutEffect, useRef, useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface DriftStep {\n  id: string;\n  label: string;\n  planned: number;\n  actual?: number;\n  done?: boolean;\n}\n\nexport interface DriftChartProps {\n  title: string;\n  agent?: string;\n  steps: DriftStep[];\n  unit?: string;\n  tickEvery?: number;\n  className?: string;\n}\n\ntype Laid = DriftStep & { start: number; ghost: number; bar: number; state: \"done\" | \"running\" | \"todo\" };\n\nfunction layout(steps: DriftStep[]) {\n  let cursor = 0;\n  const rows: Laid[] = steps.map((step) => {\n    const start = cursor;\n    const state: Laid[\"state\"] = step.done ? \"done\" : step.actual !== undefined ? \"running\" : \"todo\";\n    const bar = step.actual ?? 0;\n    const span = state === \"done\" ? bar : state === \"running\" ? Math.max(bar, step.planned) : step.planned;\n    cursor += span;\n    return { ...step, start, ghost: step.planned, bar, state };\n  });\n  return { rows, expected: cursor };\n}\n\nfunction useReducedMotionAfterMount() {\n  const prefers = useReducedMotion();\n  const [mounted, setMounted] = useState(false);\n  useEffect(() => setMounted(true), []);\n  return mounted && !!prefers;\n}\n\nconst BAR_Y = 4;\nconst BAR_H = 20;\nconst RADIUS = 6;\n\nfunction barPath(x: number, w: number, left: number, right: number, y = BAR_Y, h = BAR_H) {\n  const width = Math.max(0, w);\n  const rl = Math.min(left, width / 2, h / 2);\n  const rr = Math.min(right, width / 2, h / 2);\n  const f = (n: number) => n.toFixed(2);\n  return [\n    `M${f(x + rl)},${f(y)}`,\n    `H${f(x + width - rr)}`,\n    `A${f(rr)},${f(rr)} 0 0 1 ${f(x + width)},${f(y + rr)}`,\n    `V${f(y + h - rr)}`,\n    `A${f(rr)},${f(rr)} 0 0 1 ${f(x + width - rr)},${f(y + h)}`,\n    `H${f(x + rl)}`,\n    `A${f(rl)},${f(rl)} 0 0 1 ${f(x)},${f(y + h - rl)}`,\n    `V${f(y + rl)}`,\n    `A${f(rl)},${f(rl)} 0 0 1 ${f(x + rl)},${f(y)}`,\n    \"Z\",\n  ].join(\" \");\n}\n\nexport function DriftChart({ title, agent, steps, unit = \"m\", tickEvery = 5, className }: DriftChartProps) {\n  const reduce = useReducedMotionAfterMount();\n  const axisRef = useRef<HTMLDivElement>(null);\n  const [trackW, setTrackW] = useState(0);\n\n  useLayoutEffect(() => {\n    const el = axisRef.current;\n    if (!el) return;\n    setTrackW(el.offsetWidth);\n    const ro = new ResizeObserver(([entry]) => setTrackW(entry.contentRect.width));\n    ro.observe(el);\n    return () => ro.disconnect();\n  }, []);\n\n  const { rows, expected } = layout(steps);\n  const planned = steps.reduce((s, x) => s + x.planned, 0);\n  const max = Math.max(planned, expected) * 1.08;\n  const px = (t: number) => (t / max) * trackW;\n  const over = expected - planned;\n  const late = over > 0.05;\n  const doneCount = rows.filter((r) => r.state === \"done\").length;\n  const running = rows.find((r) => r.state === \"running\");\n  const finished = doneCount === rows.length;\n  const spring = reduce ? { duration: 0 } : { type: \"spring\" as const, stiffness: 170, damping: 26 };\n  const ticks = Array.from({ length: Math.floor(max / tickEvery) + 1 }, (_, i) => i * tickEvery);\n  const fmt = (t: number) => `${Math.round(t)}${unit}`;\n\n  return (\n    <div className={cn(\"flex w-[520px] max-w-full flex-col gap-4 rounded-2xl border bg-card p-4 text-card-foreground shadow-xs/5\", className)}>\n      <div className=\"flex items-start justify-between gap-4\">\n        <div className=\"flex min-w-0 flex-col gap-1\">\n          <span className=\"truncate font-medium text-sm\">{title}</span>\n          <span className=\"flex items-center gap-1.5 text-muted-foreground text-xs\">\n            <span className=\"relative flex size-1.5\">\n              {!finished && !reduce && <span className=\"absolute inset-0 animate-ping rounded-full bg-info/60\" />}\n              <span className={cn(\"relative size-1.5 rounded-full\", finished ? \"bg-success\" : \"bg-info\")} />\n            </span>\n            {agent && <span className=\"font-mono\">{agent}</span>}\n            {agent && <span aria-hidden>·</span>}\n            <span aria-live=\"polite\" className=\"truncate\">\n              {finished ? \"Finished\" : running ? running.label : \"Queued\"}\n            </span>\n          </span>\n        </div>\n        <div className=\"flex shrink-0 items-center gap-4 text-xs\">\n          <div className=\"flex flex-col items-end gap-1\">\n            <span className=\"text-muted-foreground\">Planned</span>\n            <span className=\"font-medium text-sm tabular-nums\">{fmt(planned)}</span>\n          </div>\n          <div className=\"flex flex-col items-end gap-1\">\n            <span className=\"text-muted-foreground\">{finished ? \"Took\" : \"Expected\"}</span>\n            <span className={cn(\"font-medium text-sm tabular-nums transition-colors duration-300\", late ? \"text-warning-foreground\" : \"text-foreground\")}>\n              {fmt(expected)}\n            </span>\n          </div>\n        </div>\n      </div>\n\n      <div className=\"relative flex flex-col gap-1.5\">\n        {rows.map((row) => {\n          const overrun = row.bar > row.ghost;\n          const x = px(row.start);\n          return (\n            <div className=\"flex h-7 items-center gap-3\" key={row.id}>\n              <div className=\"flex w-28 shrink-0 items-center gap-1.5 text-xs sm:w-32\">\n                <span\n                  className={cn(\n                    \"flex size-3.5 shrink-0 items-center justify-center rounded-full border transition-colors\",\n                    row.state === \"done\" && (overrun ? \"border-transparent bg-warning text-white\" : \"border-transparent bg-success text-white\"),\n                    row.state === \"running\" && \"border-info\",\n                    row.state === \"todo\" && \"border-input\",\n                  )}\n                >\n                  {row.state === \"done\" && <CheckIcon className=\"size-2.5\" strokeWidth={3} />}\n                  {row.state === \"running\" && <span className=\"size-1.5 rounded-full bg-info\" />}\n                </span>\n                <span className={cn(\"truncate\", row.state === \"todo\" ? \"text-muted-foreground\" : \"text-foreground\")} title={row.label}>\n                  {row.label}\n                </span>\n              </div>\n              <div className=\"relative h-full flex-1\">\n                {trackW > 0 && (\n                  <>\n                    <svg aria-hidden className=\"absolute inset-0 size-full overflow-visible\">\n                      <motion.path\n                        animate={{ d: barPath(x + 0.5, px(row.ghost) - 1, RADIUS, RADIUS, BAR_Y + 0.5, BAR_H - 1) }}\n                        className=\"fill-none stroke-muted-foreground/40\"\n                        initial={false}\n                        strokeDasharray=\"3 3\"\n                        transition={spring}\n                      />\n                      <motion.path\n                        animate={{ d: barPath(x, px(Math.min(row.bar, row.ghost)), RADIUS, overrun ? 0 : RADIUS) }}\n                        className=\"fill-teal-500 dark:fill-teal-400\"\n                        initial={false}\n                        transition={spring}\n                      />\n                      <motion.path\n                        animate={{\n                          d: barPath(x + px(row.ghost), px(Math.max(row.bar - row.ghost, 0)), 0, RADIUS),\n                          opacity: overrun ? 1 : 0,\n                        }}\n                        className=\"fill-warning\"\n                        initial={false}\n                        transition={spring}\n                      />\n                    </svg>\n                    {row.state !== \"todo\" && row.bar > 0 && (\n                      <motion.span\n                        animate={{ x: px(row.start + (row.state === \"done\" ? Math.max(row.bar, row.ghost) : row.bar)) }}\n                        className={cn(\n                          \"absolute top-1/2 left-0 ms-1.5 -translate-y-1/2 whitespace-nowrap font-medium text-[10px] tabular-nums\",\n                          overrun ? \"text-warning-foreground\" : \"text-muted-foreground\",\n                        )}\n                        initial={false}\n                        transition={spring}\n                      >\n                        {overrun ? `+${Math.round((row.bar - row.ghost) * 10) / 10}${unit}` : fmt(row.bar)}\n                      </motion.span>\n                    )}\n                  </>\n                )}\n              </div>\n            </div>\n          );\n        })}\n\n        {trackW > 0 && (\n          <div aria-hidden className=\"pointer-events-none absolute inset-y-0 right-0 left-31 sm:left-35\">\n            <motion.span\n              animate={{ x: px(planned) }}\n              className=\"absolute -top-1 -bottom-1 left-0 w-px border-foreground/40 border-l border-dashed\"\n              initial={false}\n              transition={spring}\n            />\n            <motion.span\n              animate={{ x: px(expected), opacity: late ? 1 : 0 }}\n              className=\"absolute -top-1 -bottom-1 left-0 w-0.5 -translate-x-1/2 rounded-full bg-warning\"\n              initial={false}\n              transition={spring}\n            />\n          </div>\n        )}\n      </div>\n\n      <div className=\"flex items-center gap-3\">\n        <div className=\"w-28 shrink-0 sm:w-32\" />\n        <div className=\"relative h-4 flex-1 text-[10px] text-muted-foreground tabular-nums\" ref={axisRef}>\n          {trackW > 0 &&\n            ticks.map((t, i) => (\n              <motion.span\n                animate={{ x: px(t) }}\n                className={cn(\"absolute left-0\", i > 0 && \"-translate-x-1/2\")}\n                initial={false}\n                key={t}\n                transition={spring}\n              >\n                {t}\n                {unit}\n              </motion.span>\n            ))}\n        </div>\n      </div>\n\n      <div className=\"flex flex-wrap items-center gap-x-4 gap-y-1 border-t pt-3 text-muted-foreground text-xs\">\n        <span className=\"flex items-center gap-1.5\">\n          <span className=\"h-2.5 w-4 rounded-[calc(var(--radius)*0.3)] border border-muted-foreground/50 border-dashed\" />\n          Plan\n        </span>\n        <span className=\"flex items-center gap-1.5\">\n          <span className=\"h-2.5 w-4 rounded-[calc(var(--radius)*0.3)] bg-teal-500 dark:bg-teal-400\" />\n          Actual\n        </span>\n        <span className=\"flex items-center gap-1.5\">\n          <span className=\"h-2.5 w-4 rounded-[calc(var(--radius)*0.3)] bg-warning\" />\n          Overrun\n        </span>\n        <span className={cn(\"ms-auto tabular-nums transition-colors duration-300\", late && \"text-warning-foreground\")}>\n          {late ? (\n            <>\n              Finish pushed <span className=\"font-medium\">+{Math.round(over)}{unit}</span>\n            </>\n          ) : (\n            \"On plan\"\n          )}\n        </span>\n      </div>\n    </div>\n  );\n}\n"
    }
  ]
}