{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "task-rows",
  "title": "Task rows",
  "description": "A list of steps an agent works through, each queued, running, done or failed, with details that fold out.",
  "dependencies": [
    "motion",
    "lucide-react"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/manniche/task-rows/task-rows.tsx",
      "content": "import { Check, ChevronDown, X } from 'lucide-react'\r\nimport { AnimatePresence, MotionConfig, motion } from 'motion/react'\r\nimport { useId, useState, type ReactNode } from 'react'\r\nimport { cn } from '@/lib/utils'\r\n\r\nexport type TaskStatus = 'queued' | 'running' | 'done' | 'failed'\r\n\r\nexport type Task = {\r\n  id: string\r\n  title: string\r\n  status: TaskStatus\r\n  /** Short count or result shown on the right, e.g. “12 suppliers”. */\r\n  meta?: string\r\n  /** Shown when the row is opened: steps, a log, a result. */\r\n  detail?: ReactNode\r\n}\r\n\r\nexport type TaskRowsProps = {\r\n  tasks: Task[]\r\n  labels?: Partial<Record<TaskStatus, string>>\r\n  className?: string\r\n}\r\n\r\nconst EN: Record<TaskStatus, string> = { queued: 'Waiting', running: 'Working', done: 'Done', failed: 'Failed' }\r\n\r\nfunction Marker({ status, n }: { status: TaskStatus; n: number }) {\r\n  if (status === 'done')\r\n    return (\r\n      <span className=\"grid size-6 shrink-0 place-items-center rounded-full bg-success text-background\">\r\n        <Check className=\"size-3.5\" strokeWidth={3} aria-hidden />\r\n      </span>\r\n    )\r\n  if (status === 'failed')\r\n    return (\r\n      <span className=\"grid size-6 shrink-0 place-items-center rounded-full bg-destructive text-background\">\r\n        <X className=\"size-3.5\" strokeWidth={3} aria-hidden />\r\n      </span>\r\n    )\r\n  return (\r\n    <span className=\"relative grid size-6 shrink-0 place-items-center text-xs font-semibold tabular-nums\">\r\n      {status === 'running' ? (\r\n        // A ring with a gap that turns while the agent works; reduced motion keeps it still.\r\n        <svg viewBox=\"0 0 24 24\" className=\"absolute inset-0 animate-spin text-foreground [animation-duration:1.4s] motion-reduce:animate-none\" aria-hidden>\r\n          <circle cx=\"12\" cy=\"12\" r=\"10.5\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeDasharray=\"48 18\" />\r\n        </svg>\r\n      ) : (\r\n        <span className=\"absolute inset-0 rounded-full border\" aria-hidden />\r\n      )}\r\n      {n}\r\n    </span>\r\n  )\r\n}\r\n\r\n/** Rows that show what an agent is doing, one task each, with the details tucked away until opened. */\r\nexport function TaskRows({ tasks, labels = {}, className }: TaskRowsProps) {\r\n  const t = { ...EN, ...labels }\r\n  const [open, setOpen] = useState<string | null>(null)\r\n  const base = useId()\r\n\r\n  return (\r\n    <MotionConfig reducedMotion=\"user\" transition={{ type: 'spring', bounce: 0, duration: 0.4 }}>\r\n      <ol className={cn('w-full space-y-2', className)}>\r\n        {tasks.map((task, i) => {\r\n          const isOpen = open === task.id\r\n          const panel = `${base}-${task.id}`\r\n          return (\r\n            <motion.li key={task.id} layout style={{ borderRadius: 16 }} className=\"relative overflow-hidden rounded-2xl border bg-card text-card-foreground\">\r\n              <motion.button\r\n                layout=\"position\"\r\n                type=\"button\"\r\n                aria-expanded={isOpen}\r\n                aria-controls={panel}\r\n                disabled={!task.detail}\r\n                onClick={() => setOpen(isOpen ? null : task.id)}\r\n                className=\"flex min-h-12 w-full items-center gap-3 px-3 py-2.5 text-left transition-colors duration-150 hover:bg-muted/60 disabled:cursor-default disabled:hover:bg-transparent\"\r\n              >\r\n                <Marker status={task.status} n={i + 1} />\r\n                <span className={cn('min-w-0 flex-1 truncate text-sm font-medium', task.status === 'queued' && 'text-muted-foreground')}>{task.title}</span>\r\n                {task.meta && <span className=\"hidden text-sm text-muted-foreground tabular-nums sm:inline\">{task.meta}</span>}\r\n                <AnimatePresence mode=\"popLayout\" initial={false}>\r\n                  <motion.span\r\n                    key={task.status}\r\n                    initial={{ opacity: 0, y: 6 }}\r\n                    animate={{ opacity: 1, y: 0 }}\r\n                    exit={{ opacity: 0, y: -6 }}\r\n                    className={cn(\r\n                      'rounded-full px-2 py-0.5 text-xs font-medium',\r\n                      task.status === 'done' && 'bg-success/15 text-success',\r\n                      task.status === 'failed' && 'bg-destructive/15 text-destructive',\r\n                      task.status === 'running' && 'bg-muted text-foreground',\r\n                      task.status === 'queued' && 'text-muted-foreground',\r\n                    )}\r\n                  >\r\n                    {t[task.status]}\r\n                  </motion.span>\r\n                </AnimatePresence>\r\n                {task.detail && (\r\n                  <ChevronDown className={cn('size-4 shrink-0 text-muted-foreground transition-transform duration-200', isOpen && 'rotate-180')} aria-hidden />\r\n                )}\r\n              </motion.button>\r\n              {/* The row grows with a layout (transform) animation; the detail only fades. */}\r\n              <AnimatePresence initial={false} mode=\"popLayout\">\r\n                {isOpen && (\r\n                  <motion.div\r\n                    id={panel}\r\n                    key=\"detail\"\r\n                    layout=\"position\"\r\n                    initial={{ opacity: 0 }}\r\n                    animate={{ opacity: 1 }}\r\n                    exit={{ opacity: 0 }}\r\n                    className=\"w-full text-sm text-muted-foreground\"\r\n                  >\r\n                    <div className=\"border-t border-dashed px-4 py-3 pl-12\">{task.detail}</div>\r\n                  </motion.div>\r\n                )}\r\n              </AnimatePresence>\r\n            </motion.li>\r\n          )\r\n        })}\r\n      </ol>\r\n    </MotionConfig>\r\n  )\r\n}\r\n",
      "type": "registry:component"
    }
  ],
  "categories": [
    "agent"
  ],
  "type": "registry:component"
}