{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "task-list",
  "title": "AI Task List",
  "description": "Display AI workflow progress with task status indicators, file references, and progress tracking.",
  "dependencies": [
    "@radix-ui/react-collapsible",
    "lucide-react"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/blocks/ai/ai-task-list/components/elements/ai-task-list.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport * as CollapsiblePrimitive from \"@radix-ui/react-collapsible\";\nimport {\n  CheckCircle2,\n  ChevronDown,\n  Circle,\n  FileCode,\n  ListTodo,\n  Loader2,\n  XCircle,\n} from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\n\ntype TaskStatus = \"pending\" | \"in-progress\" | \"completed\" | \"error\";\n\ninterface TaskItem {\n  id: string;\n  title: string;\n  status: TaskStatus;\n  description?: string;\n}\n\ninterface AiTaskListContextValue {\n  tasks: TaskItem[];\n  completedCount: number;\n  totalCount: number;\n}\n\nconst AiTaskListContext = React.createContext<AiTaskListContextValue | null>(\n  null,\n);\n\nfunction useTaskListContext() {\n  const context = React.useContext(AiTaskListContext);\n  if (!context) {\n    throw new Error(\"AiTaskList components must be used within <AiTaskList>\");\n  }\n  return context;\n}\n\ninterface AiTaskListProps {\n  title?: string;\n  tasks?: TaskItem[];\n  defaultOpen?: boolean;\n  open?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  children?: React.ReactNode;\n  className?: string;\n}\n\nfunction AiTaskList({\n  title = \"Tasks\",\n  tasks = [],\n  defaultOpen = true,\n  open: controlledOpen,\n  onOpenChange,\n  children,\n  className,\n}: AiTaskListProps) {\n  const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen);\n\n  const isControlled = controlledOpen !== undefined;\n  const isOpen = isControlled ? controlledOpen : uncontrolledOpen;\n\n  const handleOpenChange = React.useCallback(\n    (open: boolean) => {\n      if (!isControlled) {\n        setUncontrolledOpen(open);\n      }\n      onOpenChange?.(open);\n    },\n    [isControlled, onOpenChange],\n  );\n\n  const completedCount = React.useMemo(\n    () => tasks.filter((t) => t.status === \"completed\").length,\n    [tasks],\n  );\n\n  const contextValue = React.useMemo(\n    () => ({\n      tasks,\n      completedCount,\n      totalCount: tasks.length,\n    }),\n    [tasks, completedCount],\n  );\n\n  const hasChildren = React.Children.count(children) > 0;\n\n  return (\n    <AiTaskListContext.Provider value={contextValue}>\n      <CollapsiblePrimitive.Root\n        data-slot=\"ai-task-list\"\n        open={isOpen}\n        onOpenChange={handleOpenChange}\n        className={cn(\n          \"rounded-lg border border-border bg-card text-card-foreground overflow-hidden\",\n          className,\n        )}\n      >\n        <CollapsiblePrimitive.Trigger\n          data-slot=\"ai-task-list-trigger\"\n          className=\"flex w-full items-center gap-3 px-4 py-3 text-sm font-medium transition-colors hover:bg-muted/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n        >\n          <div className=\"flex size-8 shrink-0 items-center justify-center rounded-md bg-muted\">\n            <ListTodo className=\"size-4 text-muted-foreground\" />\n          </div>\n          <div className=\"flex flex-1 items-center gap-2 text-left\">\n            <span className=\"font-medium\">{title}</span>\n            {tasks.length > 0 && (\n              <span className=\"inline-flex items-center rounded-full bg-muted px-2 py-0.5 text-xs font-medium text-muted-foreground\">\n                {completedCount}/{tasks.length}\n              </span>\n            )}\n          </div>\n          <ChevronDown\n            className={cn(\n              \"size-4 shrink-0 text-muted-foreground transition-transform duration-200\",\n              isOpen && \"rotate-180\",\n            )}\n          />\n        </CollapsiblePrimitive.Trigger>\n\n        <CollapsiblePrimitive.Content\n          data-slot=\"ai-task-list-content\"\n          className=\"border-t border-border data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down\"\n        >\n          <div className=\"p-3 space-y-1\">\n            {hasChildren\n              ? children\n              : tasks.map((task) => (\n                  <AiTaskListItem\n                    key={task.id}\n                    status={task.status}\n                    description={task.description}\n                  >\n                    {task.title}\n                  </AiTaskListItem>\n                ))}\n          </div>\n        </CollapsiblePrimitive.Content>\n      </CollapsiblePrimitive.Root>\n    </AiTaskListContext.Provider>\n  );\n}\n\ninterface AiTaskListItemProps {\n  status: TaskStatus;\n  description?: string;\n  children: React.ReactNode;\n  className?: string;\n}\n\nfunction AiTaskListItem({\n  status,\n  description,\n  children,\n  className,\n}: AiTaskListItemProps) {\n  const statusConfig = React.useMemo(() => {\n    const configs: Record<\n      TaskStatus,\n      { icon: React.ReactNode; className: string }\n    > = {\n      pending: {\n        icon: <Circle className=\"size-4\" />,\n        className: \"text-muted-foreground\",\n      },\n      \"in-progress\": {\n        icon: <Loader2 className=\"size-4 animate-spin\" />,\n        className: \"text-blue-600 dark:text-blue-400\",\n      },\n      completed: {\n        icon: <CheckCircle2 className=\"size-4\" />,\n        className: \"text-green-600 dark:text-green-400\",\n      },\n      error: {\n        icon: <XCircle className=\"size-4\" />,\n        className: \"text-red-600 dark:text-red-400\",\n      },\n    };\n    return configs[status];\n  }, [status]);\n\n  return (\n    <div\n      data-slot=\"ai-task-list-item\"\n      data-status={status}\n      className={cn(\n        \"flex items-start gap-3 rounded-md px-3 py-2 transition-colors\",\n        status === \"in-progress\" && \"bg-blue-50 dark:bg-blue-950/30\",\n        status === \"error\" && \"bg-red-50 dark:bg-red-950/30\",\n        className,\n      )}\n    >\n      <div className={cn(\"mt-0.5 shrink-0\", statusConfig.className)}>\n        {statusConfig.icon}\n      </div>\n      <div className=\"flex-1 min-w-0\">\n        <div\n          className={cn(\n            \"text-sm font-medium\",\n            status === \"completed\" && \"text-muted-foreground line-through\",\n          )}\n        >\n          {children}\n        </div>\n        {description && (\n          <p className=\"mt-0.5 text-xs text-muted-foreground\">{description}</p>\n        )}\n      </div>\n    </div>\n  );\n}\n\ninterface AiTaskListFileProps {\n  filename: string;\n  language?: string;\n  className?: string;\n}\n\nfunction AiTaskListFile({\n  filename,\n  language,\n  className,\n}: AiTaskListFileProps) {\n  return (\n    <div\n      data-slot=\"ai-task-list-file\"\n      className={cn(\n        \"inline-flex items-center gap-1.5 rounded bg-muted px-2 py-1 text-xs font-mono\",\n        className,\n      )}\n    >\n      <FileCode className=\"size-3 text-muted-foreground\" />\n      <span className=\"text-foreground\">{filename}</span>\n      {language && <span className=\"text-muted-foreground\">({language})</span>}\n    </div>\n  );\n}\n\ninterface AiTaskListProgressProps {\n  className?: string;\n}\n\nfunction AiTaskListProgress({ className }: AiTaskListProgressProps) {\n  const { completedCount, totalCount } = useTaskListContext();\n\n  const percentage = React.useMemo(() => {\n    if (totalCount === 0) return 0;\n    return Math.round((completedCount / totalCount) * 100);\n  }, [completedCount, totalCount]);\n\n  return (\n    <div\n      data-slot=\"ai-task-list-progress\"\n      className={cn(\"space-y-1\", className)}\n    >\n      <div className=\"flex items-center justify-between text-xs\">\n        <span className=\"text-muted-foreground\">Progress</span>\n        <span className=\"font-medium\">\n          {completedCount} of {totalCount} ({percentage}%)\n        </span>\n      </div>\n      <div className=\"h-2 w-full overflow-hidden rounded-full bg-muted\">\n        <div\n          className=\"h-full bg-primary transition-all duration-300\"\n          style={{ width: `${percentage}%` }}\n        />\n      </div>\n    </div>\n  );\n}\n\nexport { AiTaskList, AiTaskListItem, AiTaskListFile, AiTaskListProgress };\nexport type { AiTaskListProps, TaskItem, TaskStatus };\n",
      "type": "registry:component"
    }
  ],
  "docs": "Collapsible task list with status icons. Supports pending, in-progress, completed, error states with progress bar.",
  "categories": [
    "ai"
  ],
  "type": "registry:ui"
}