{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "elements-job-progress",
  "type": "registry:component",
  "title": "Long job",
  "description": "Work measured in minutes: weighted stages, an ETA, and a way out.",
  "registryDependencies": [
    "https://r.assistant-ui.com/elements-surfaces.json",
    "https://r.assistant-ui.com/elements-range.json"
  ],
  "dependencies": [
    "lucide-react"
  ],
  "files": [
    {
      "type": "registry:component",
      "path": "components/assistant-ui/elements/job-progress.tsx",
      "sourcePath": "packages/ui/src/components/react/assistant-ui/elements/job-progress.tsx",
      "content": "\"use client\";\n\nimport type { ComponentProps } from \"react\";\nimport { CheckIcon, Loader2Icon, XIcon } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport { ghostButton, mono, paper } from \"./surfaces\";\nimport { announced, clamp, pct, progressOf, take } from \"../utils/range\";\n\nexport interface JobStage {\n  name: string;\n  weight: number;\n}\n\nexport function JobProgress({\n  title,\n  stages,\n  stageIndex,\n  stageProgress,\n  eta,\n  onCancel,\n  className,\n  ...props\n}: Omit<\n  ComponentProps<\"div\">,\n  | \"children\"\n  | \"title\"\n  | \"stages\"\n  | \"stageIndex\"\n  | \"stageProgress\"\n  | \"eta\"\n  | \"onCancel\"\n> & {\n  title: string;\n  stages: readonly JobStage[];\n  stageIndex: number;\n  stageProgress: number;\n  eta: string;\n  onCancel?: () => void;\n}) {\n  const stage = progressOf(stageIndex, stages.length);\n  const progress = clamp(stageProgress, 0, 1);\n  const totalWeight = stages.reduce((sum, item) => sum + item.weight, 0) || 1;\n  const completed = take(stages, stage).reduce(\n    (sum, item) => sum + item.weight,\n    0,\n  );\n  const current = stages[stage];\n  const overall = pct(\n    completed + (current ? current.weight * progress : 0),\n    totalWeight,\n  );\n  const finished = stage >= stages.length;\n\n  return (\n    <div\n      data-slot=\"job-progress\"\n      className={cn(\n        paper,\n        \"flex w-full max-w-sm flex-col gap-3 rounded-2xl p-4\",\n        className,\n      )}\n\n      {...props}\n    >\n      <div className=\"flex items-center gap-2.5\">\n        {finished ? (\n          <CheckIcon className=\"size-3.5 shrink-0 text-emerald-500\" />\n        ) : (\n          <Loader2Icon className=\"text-foreground/35 size-3.5 shrink-0 animate-spin motion-reduce:animate-none\" />\n        )}\n        <span className=\"min-w-0 flex-1 truncate text-[13.5px] font-medium\">\n          {title}\n        </span>\n        <span className={cn(mono, \"text-foreground/35 shrink-0 tabular-nums\")}>\n          {finished ? \"done\" : eta}\n        </span>\n        {!finished && (\n          <button\n            type=\"button\"\n            aria-label=\"Cancel the job\"\n            onClick={onCancel}\n            className={cn(ghostButton, \"size-6 shrink-0\")}\n          >\n            <XIcon className=\"size-3.5\" />\n          </button>\n        )}\n      </div>\n\n      <span\n        role=\"progressbar\"\n        aria-label={`${title} progress`}\n        aria-valuemin={0}\n        aria-valuemax={100}\n        aria-valuenow={announced(overall)}\n        className=\"bg-foreground/[0.06] h-1 w-full overflow-hidden rounded-full\"\n      >\n        <span\n          className={cn(\n            \"block h-full rounded-full transition-[width] duration-500 ease-out motion-reduce:transition-none\",\n            finished ? \"bg-emerald-500\" : \"bg-blue-500 dark:bg-blue-400\",\n          )}\n          style={{ width: `${overall}%` }}\n        />\n      </span>\n\n      <div className=\"flex flex-wrap gap-x-3 gap-y-1\">\n        {stages.map((item, i) => (\n          <span\n            key={item.name}\n            className={cn(\n              mono,\n              i < stage\n                ? \"text-foreground/35\"\n                : i === stage\n                  ? \"text-foreground/90\"\n                  : \"text-foreground/20\",\n            )}\n          >\n            {item.name}\n          </span>\n        ))}\n      </div>\n    </div>\n  );\n}\n"
    }
  ]
}