{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "task-rows",
  "title": "Task rows",
  "description": "Expandable agent task rows with pending, running, failed, and completed states plus Capsule and List layouts.",
  "registryDependencies": [
    "https://beadkit.dev/r/ai-core.json"
  ],
  "files": [
    {
      "path": "components/ai/TaskRows.tsx",
      "content": "\"use client\";\n\nimport { useState } from \"react\";\nimport styles from \"./ai-state.module.css\";\n\nexport type TaskRowsVariant = \"Capsules\" | \"List\";\nexport type TaskStatus = \"pending\" | \"running\" | \"failed\" | \"completed\";\n\nexport interface TaskDetail {\n  label: string;\n  meta?: string;\n}\n\nexport interface TaskRow {\n  id: string;\n  label: string;\n  amount?: string;\n  status: TaskStatus;\n  details?: readonly TaskDetail[];\n  error?: string;\n}\n\nexport interface TaskRowsProps {\n  rows: readonly TaskRow[];\n  variant?: TaskRowsVariant;\n  defaultExpanded?: readonly string[];\n  /** Controlled expanded row ids. Omit to let the component manage disclosure state. */\n  expandedRows?: readonly string[];\n  onExpandedChange?: (rowIds: readonly string[]) => void;\n  onRetry?: (row: TaskRow) => void;\n  className?: string;\n}\n\nfunction StatusMark({ status }: { status: TaskStatus }) {\n  if (status === \"completed\") return <span className=\"bg-success text-background flex size-6 items-center justify-center rounded-full\"><svg aria-hidden=\"true\" width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"3.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><path d=\"M20 6 9 17l-5-5\" /></svg></span>;\n  if (status === \"failed\") return <span className=\"bg-destructive text-background flex size-6 items-center justify-center rounded-full\"><svg aria-hidden=\"true\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"3.5\" strokeLinecap=\"round\"><path d=\"M18 6 6 18M6 6l12 12\" /></svg></span>;\n  return <span className=\"border-muted-foreground/40 text-muted-foreground relative flex size-6 items-center justify-center rounded-full border-2 text-[10px] font-semibold tabular-nums\">{status === \"running\" ? <span className={`${styles.spinner} absolute inset-[-2px] rounded-full border-2 border-transparent border-t-foreground`} aria-hidden=\"true\" /> : null}</span>;\n}\n\nexport function TaskRows({ rows, variant = \"Capsules\", defaultExpanded = [], expandedRows, onExpandedChange, onRetry, className = \"\" }: TaskRowsProps) {\n  const [openRows, setOpenRows] = useState<Set<string>>(() => new Set(defaultExpanded));\n  const visibleOpenRows = expandedRows ? new Set(expandedRows) : openRows;\n  const list = variant === \"List\";\n  const toggle = (id: string) => {\n    const next = new Set(visibleOpenRows);\n    if (next.has(id)) next.delete(id); else next.add(id);\n    if (expandedRows === undefined) setOpenRows(next);\n    onExpandedChange?.([...next]);\n  };\n  return <div data-ai-state=\"task-rows\" data-variant={variant} className={`flex w-full max-w-xl flex-col ${list ? \"bg-card border-border overflow-hidden rounded-xl border\" : \"gap-2\"} ${className}`}>\n    {rows.map((row, index) => {\n      const open = visibleOpenRows.has(row.id);\n      return <div key={row.id} className={`${list ? \"border-border border-b last:border-0\" : \"bg-card rounded-xl border border-border shadow-sm\"} ${styles.fadeUp}`} style={{ animationDelay: `${index * 80}ms` }}>\n        <div className=\"flex min-h-12 w-full items-center gap-2.5 px-3\">\n          <button type=\"button\" aria-expanded={open} onClick={() => toggle(row.id)} className=\"hover:bg-muted -mx-3 flex min-h-12 min-w-0 flex-1 items-center gap-2.5 px-3 text-left transition-colors duration-150\">\n          <StatusMark status={row.status} />\n          <span className=\"min-w-0 flex-1 truncate text-[13px] font-medium\">{row.label}</span>\n          {row.amount ? <span className=\"text-muted-foreground shrink-0 text-xs tabular-nums\">{row.amount}</span> : null}\n          <span role=\"status\" aria-label={`Task ${row.status}`} className={`shrink-0 rounded-full px-2 py-1 text-[11px] font-medium ${row.status === \"completed\" ? \"bg-success/10 text-success\" : row.status === \"failed\" ? \"bg-destructive/10 text-destructive\" : \"bg-muted text-muted-foreground\"}`}>{row.status === \"completed\" ? \"Completed\" : row.status === \"failed\" ? \"Failed\" : row.status === \"running\" ? \"Running\" : \"Pending\"}</span>\n          <svg aria-hidden=\"true\" width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" className={`${styles.motion} text-muted-foreground shrink-0 transition-transform duration-300`} style={{ transform: open ? \"rotate(180deg)\" : \"none\" }}><path d=\"m6 9 6 6 6-6\" /></svg>\n          </button>\n          {row.status === \"failed\" && onRetry ? <button type=\"button\" aria-label={`Retry ${row.label}`} onClick={() => onRetry(row)} className=\"text-destructive hover:bg-destructive/10 shrink-0 rounded p-1\"><svg aria-hidden=\"true\" width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"3\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><path d=\"M21 12a9 9 0 1 1-2.64-6.36M21 3v6h-6\" /></svg></button> : null}\n        </div>\n        <div className={`${styles.motion} grid transition-[grid-template-rows,opacity] duration-300`} style={{ gridTemplateRows: open ? \"1fr\" : \"0fr\", opacity: open ? 1 : 0 }}><div className=\"min-h-0 overflow-hidden\"><div className=\"grid grid-cols-[24px_1fr] gap-2.5 px-3 pb-3\"><span aria-hidden=\"true\" className=\"bg-border mx-auto h-full w-px\" /><div className=\"flex flex-col gap-1.5\">{row.error ? <span className=\"text-destructive text-xs\">{row.error}</span> : null}{row.details?.map((detail) => <div key={detail.label} className=\"flex items-center justify-between gap-3 text-xs\"><span className=\"text-muted-foreground truncate\">{detail.label}</span>{detail.meta ? <span className=\"text-muted-foreground shrink-0 font-mono text-[11px] tabular-nums\">{detail.meta}</span> : null}</div>)}</div></div></div></div>\n      </div>;\n    })}\n  </div>;\n}\n\nexport default TaskRows;\n",
      "type": "registry:component",
      "target": "@components/ai/TaskRows.tsx"
    }
  ],
  "type": "registry:component"
}
