{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "elements-task-card",
  "type": "registry:component",
  "title": "Task card",
  "description": "A delegated task with its state, timing, result, and transcript in one card.",
  "registryDependencies": [
    "https://r.assistant-ui.com/elements-surfaces.json"
  ],
  "dependencies": [
    "lucide-react"
  ],
  "files": [
    {
      "type": "registry:component",
      "path": "components/assistant-ui/elements/task-card.tsx",
      "sourcePath": "packages/ui/src/components/react/assistant-ui/elements/task-card.tsx",
      "content": "\"use client\";\n\nimport { Children, type ComponentProps, type ReactNode, useState } from \"react\";\nimport {\n  Ban,\n  CheckIcon,\n  ChevronRightIcon,\n  Loader2Icon,\n  XIcon,\n} from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport { mono, paper } from \"./surfaces\";\n\nexport type TaskCardState =\n  | \"working\"\n  | \"waiting\"\n  | \"done\"\n  | \"failed\"\n  | \"cancelled\";\n\nconst isRenderable = (node: ReactNode) =>\n  node !== undefined && node !== null && node !== false && node !== true;\n\nexport function TaskStateIcon({\n  state,\n  className,\n}: {\n  state: TaskCardState;\n  className?: string;\n}) {\n  if (state === \"done\") {\n    return (\n      <CheckIcon\n        aria-hidden\n        className={cn(\"size-3.5 shrink-0 text-emerald-500\", className)}\n      />\n    );\n  }\n  if (state === \"failed\") {\n    return (\n      <XIcon\n        aria-hidden\n        className={cn(\"text-destructive size-3.5 shrink-0\", className)}\n      />\n    );\n  }\n  if (state === \"cancelled\") {\n    return (\n      <Ban\n        aria-hidden\n        className={cn(\"text-foreground/35 size-3.5 shrink-0\", className)}\n      />\n    );\n  }\n  if (state === \"working\") {\n    return (\n      <Loader2Icon\n        aria-hidden\n        className={cn(\n          \"text-foreground/35 size-3.5 shrink-0 animate-spin motion-reduce:animate-none\",\n          className,\n        )}\n      />\n    );\n  }\n  return (\n    <span\n      aria-hidden\n      className={cn(\n        \"border-foreground/35 m-1 size-1.5 shrink-0 rounded-full border\",\n        className,\n      )}\n    />\n  );\n}\n\nexport function TaskCard({\n  label,\n  meta,\n  state,\n  elapsed,\n  actions,\n  result,\n  open,\n  onOpenChange,\n  children,\n  className,\n  ...props\n}: Omit<\n  ComponentProps<\"div\">,\n  \"children\" | \"label\" | \"state\" | \"result\" | \"open\" | \"onOpenChange\"\n> & {\n  label: string;\n  meta?: string | undefined;\n  state: TaskCardState;\n  elapsed?: string | undefined;\n  actions?: ReactNode | undefined;\n  result?: ReactNode | undefined;\n  open?: boolean | undefined;\n  onOpenChange?: ((open: boolean) => void) | undefined;\n  children?: ReactNode | undefined;\n}) {\n  const hasTranscript = Children.toArray(children).length > 0;\n  const inert = open !== undefined && onOpenChange === undefined;\n  const [uncontrolledOpen, setUncontrolledOpen] = useState(false);\n  const isOpen = open ?? uncontrolledOpen;\n  const toggle = () => {\n    const next = !isOpen;\n    if (open === undefined) setUncontrolledOpen(next);\n    onOpenChange?.(next);\n  };\n\n  return (\n    <div\n      data-slot=\"task-card\"\n      data-state={state}\n      className={cn(\n        paper,\n        \"flex w-full max-w-sm flex-col overflow-hidden rounded-2xl\",\n        className,\n      )}\n      {...props}\n    >\n      <button\n        type=\"button\"\n        aria-expanded={hasTranscript ? isOpen : undefined}\n        disabled={!hasTranscript || inert}\n        onClick={toggle}\n        className=\"hover:enabled:bg-foreground/[0.03] flex items-center gap-2.5 px-3.5 py-2.5 text-start transition-colors disabled:cursor-default\"\n      >\n        <TaskStateIcon state={state} />\n        <span className=\"sr-only\">{state}</span>\n        <span className=\"min-w-0 flex-1 truncate text-[13.5px]\">{label}</span>\n        {meta !== undefined && (\n          <span\n            className={cn(\n              mono,\n              \"text-foreground/35 max-w-24 shrink-0 truncate\",\n            )}\n          >\n            {meta}\n          </span>\n        )}\n        {elapsed !== undefined && (\n          <span\n            className={cn(mono, \"text-foreground/30 shrink-0 tabular-nums\")}\n          >\n            {elapsed}\n          </span>\n        )}\n        {hasTranscript && (\n          <ChevronRightIcon\n            aria-hidden\n            className={cn(\n              \"text-foreground/25 size-3 shrink-0 transition-transform duration-200 motion-reduce:transition-none\",\n              isOpen && \"rotate-90\",\n            )}\n          />\n        )}\n      </button>\n      {isRenderable(actions) && (\n        <div\n          data-slot=\"task-card-actions\"\n          className=\"border-border/60 border-t px-3.5 py-2.5\"\n        >\n          {actions}\n        </div>\n      )}\n      {hasTranscript && isOpen && (\n        <div\n          data-slot=\"task-card-transcript\"\n          className=\"border-border/60 flex flex-col gap-2 border-t px-3.5 py-2.5\"\n        >\n          {children}\n        </div>\n      )}\n      {isRenderable(result) && (\n        <div\n          data-slot=\"task-card-result\"\n          className=\"border-border/60 text-foreground/70 border-t px-3.5 py-2 text-xs leading-relaxed\"\n        >\n          {result}\n        </div>\n      )}\n    </div>\n  );\n}\n"
    }
  ]
}