{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "coding-agent-block",
  "type": "registry:block",
  "title": "Coding agent",
  "description": "Desktop coding workspace with project tasks, a focused conversation, and a persistent controlled composer.",
  "dependencies": [
    "lucide-react@^1.27.0"
  ],
  "registryDependencies": [
    "http://127.0.0.1:3000/r/button.json",
    "http://127.0.0.1:3000/r/chat-layout.json",
    "http://127.0.0.1:3000/r/collapsible.json",
    "http://127.0.0.1:3000/r/core.json",
    "http://127.0.0.1:3000/r/sidebar.json"
  ],
  "files": [
    {
      "path": "src/registry/blocks/control-ui/coding-agent.tsx",
      "target": "@components/control-ui/blocks/coding-agent.tsx",
      "type": "registry:block",
      "content": "\"use client\";\n\nimport { BotIcon, ChevronRightIcon, CircleIcon, FolderIcon, PlusIcon, SearchIcon } from \"lucide-react\";\nimport type { ComponentProps, ReactNode } from \"react\";\nimport { ChatLayout, ChatThread } from \"@/components/control-ui/chat-layout\";\nimport { cn } from \"@/components/control-ui/lib/cn\";\nimport { Button } from \"@/components/control-ui/ui/button\";\nimport { Collapsible, CollapsibleContent, CollapsibleTrigger } from \"@/components/control-ui/ui/collapsible\";\nimport {\n  Sidebar,\n  SidebarContent,\n  SidebarFooter,\n  SidebarGroup,\n  SidebarGroupLabel,\n  SidebarHeader,\n  SidebarInset,\n  SidebarMenu,\n  SidebarMenuButton,\n  SidebarMenuItem,\n  SidebarProvider,\n  SidebarRail,\n  type SidebarStyle,\n  SidebarTrigger,\n  useSidebar,\n} from \"@/components/control-ui/ui/sidebar\";\n\nexport type CodingAgentNavigationItem = {\n  id: string;\n  label: string;\n  icon?: ReactNode;\n  trailing?: ReactNode;\n};\n\nexport type CodingAgentTask = {\n  id: string;\n  title: string;\n  trailing?: ReactNode;\n};\n\nexport type CodingAgentProject = {\n  id: string;\n  name: string;\n  icon?: ReactNode;\n  tasks: readonly CodingAgentTask[];\n  open?: boolean;\n};\n\nexport type CodingAgentBlockProps = Omit<ComponentProps<\"div\">, \"children\"> & {\n  appName?: string;\n  brand?: ReactNode;\n  sidebarTop?: ReactNode;\n  navigation?: readonly CodingAgentNavigationItem[];\n  projects: readonly CodingAgentProject[];\n  activeTaskId?: string;\n  activeTaskTitle?: string;\n  onNavigationSelect?: (item: CodingAgentNavigationItem) => void;\n  onTaskSelect?: (task: CodingAgentTask, project: CodingAgentProject) => void;\n  onNewTask?: () => void;\n  onSearch?: () => void;\n  headerActions?: ReactNode;\n  sidebarFooter?: ReactNode;\n  children: ReactNode;\n  layout?: \"viewport\" | \"contained\";\n};\n\nexport function CodingAgentBlock({\n  appName = \"Agent\",\n  brand,\n  sidebarTop,\n  navigation = [],\n  projects,\n  activeTaskId,\n  activeTaskTitle = \"New task\",\n  onNavigationSelect,\n  onTaskSelect,\n  onNewTask,\n  onSearch,\n  headerActions,\n  sidebarFooter,\n  children,\n  layout = \"viewport\",\n  className,\n  style,\n  ...props\n}: CodingAgentBlockProps) {\n  const contained = layout === \"contained\";\n\n  const providerStyle: SidebarStyle = { \"--sidebar-width\": \"17.5rem\", ...style };\n\n  return (\n    <SidebarProvider\n      className={cn(\"overflow-hidden bg-background text-foreground\", contained ? \"relative h-full min-h-0\" : \"h-svh min-h-svh\", className)}\n      style={providerStyle}\n      {...props}\n    >\n      <CodingAgentSidebar\n        appName={appName}\n        brand={brand}\n        sidebarTop={sidebarTop}\n        navigation={navigation}\n        projects={projects}\n        activeTaskId={activeTaskId}\n        contained={contained}\n        sidebarFooter={sidebarFooter}\n        onNavigationSelect={onNavigationSelect}\n        onTaskSelect={onTaskSelect}\n        onNewTask={onNewTask}\n        onSearch={onSearch}\n      />\n      <SidebarInset className=\"h-full min-h-0 min-w-0 overflow-hidden\">\n        <header className=\"flex h-12 shrink-0 items-center gap-2 border-b border-border/70 px-3\">\n          <SidebarTrigger size=\"xs\" className=\"md:hidden\" />\n          <FolderIcon className=\"size-4 shrink-0 text-muted-foreground\" aria-hidden=\"true\" />\n          <h1 className=\"min-w-0 flex-1 truncate text-label font-medium\">{activeTaskTitle}</h1>\n          {headerActions ? <div className=\"flex shrink-0 items-center gap-1\">{headerActions}</div> : null}\n        </header>\n        <div className=\"min-h-0 flex-1 overflow-hidden\">{children}</div>\n      </SidebarInset>\n    </SidebarProvider>\n  );\n}\n\nfunction CodingAgentSidebar({\n  appName,\n  brand,\n  sidebarTop,\n  navigation,\n  projects,\n  activeTaskId,\n  contained,\n  sidebarFooter,\n  onNavigationSelect,\n  onTaskSelect,\n  onNewTask,\n  onSearch,\n}: {\n  appName: string;\n  brand?: ReactNode;\n  sidebarTop?: ReactNode;\n  navigation: readonly CodingAgentNavigationItem[];\n  projects: readonly CodingAgentProject[];\n  activeTaskId?: string;\n  contained: boolean;\n  sidebarFooter?: ReactNode;\n  onNavigationSelect?: (item: CodingAgentNavigationItem) => void;\n  onTaskSelect?: (task: CodingAgentTask, project: CodingAgentProject) => void;\n  onNewTask?: () => void;\n  onSearch?: () => void;\n}) {\n  const { setOpenMobile } = useSidebar();\n\n  function finishMobileNavigation() {\n    setOpenMobile(false);\n  }\n\n  function startTask() {\n    onNewTask?.();\n    finishMobileNavigation();\n  }\n\n  return (\n    <Sidebar collapsible=\"offcanvas\" className={contained ? \"absolute! inset-y-0! h-full border-r border-sidebar-border\" : undefined}>\n      <SidebarHeader className=\"gap-1.5 border-b border-sidebar-border px-2 pb-2 pt-3\">\n        {sidebarTop}\n        <div className=\"flex min-w-0 items-center gap-2 px-1 py-1\">\n          <div className=\"flex min-w-0 flex-1 items-center gap-2\">\n            {brand ?? (\n              <span className=\"flex size-6 shrink-0 items-center justify-center rounded-md bg-foreground text-background\">\n                <BotIcon className=\"size-3.5\" aria-hidden=\"true\" />\n              </span>\n            )}\n            <span className=\"truncate text-label font-semibold\">{appName}</span>\n          </div>\n          <Button variant=\"ghost\" size=\"sm\" iconOnly aria-label=\"Search tasks\" onClick={onSearch}>\n            <SearchIcon className=\"size-3.5\" />\n          </Button>\n        </div>\n        <SidebarMenu>\n          <SidebarMenuItem>\n            <SidebarMenuButton size=\"sm\" onClick={startTask}>\n              <PlusIcon />\n              <span>New task</span>\n            </SidebarMenuButton>\n          </SidebarMenuItem>\n        </SidebarMenu>\n      </SidebarHeader>\n\n      <SidebarContent>\n        {navigation.length > 0 ? (\n          <SidebarGroup className=\"pb-0\">\n            <SidebarMenu>\n              {navigation.map((item) => (\n                <SidebarMenuItem key={item.id}>\n                  <SidebarMenuButton\n                    size=\"sm\"\n                    onClick={() => {\n                      onNavigationSelect?.(item);\n                      finishMobileNavigation();\n                    }}\n                  >\n                    {item.icon}\n                    <span>{item.label}</span>\n                    {item.trailing ? <span className=\"ml-auto shrink-0\">{item.trailing}</span> : null}\n                  </SidebarMenuButton>\n                </SidebarMenuItem>\n              ))}\n            </SidebarMenu>\n          </SidebarGroup>\n        ) : null}\n\n        <SidebarGroup className=\"gap-1 pt-1\">\n          <SidebarGroupLabel>Projects</SidebarGroupLabel>\n          <div className=\"grid gap-1\">\n            {projects.map((project) => (\n              <Collapsible key={project.id} defaultOpen={project.open ?? true} className=\"min-w-0\">\n                <CollapsibleTrigger className=\"flex h-[var(--control-h-sm)] w-full items-center gap-2 rounded-[var(--radius-popup-item)] px-2 text-caption font-medium hover:bg-foreground/6\">\n                  <ChevronRightIcon className=\"size-3.5 shrink-0 text-muted-foreground\" aria-hidden=\"true\" />\n                  <span className=\"flex shrink-0 items-center text-muted-foreground\">\n                    {project.icon ?? <FolderIcon className=\"size-4\" aria-hidden=\"true\" />}\n                  </span>\n                  <span className=\"truncate\">{project.name}</span>\n                </CollapsibleTrigger>\n                <CollapsibleContent>\n                  <SidebarMenu className=\"mt-1 pl-5\">\n                    {project.tasks.map((task) => {\n                      const isActive = task.id === activeTaskId;\n                      return (\n                        <SidebarMenuItem key={task.id}>\n                          <SidebarMenuButton\n                            size=\"sm\"\n                            isActive={isActive}\n                            aria-current={isActive ? \"page\" : undefined}\n                            onClick={() => {\n                              onTaskSelect?.(task, project);\n                              finishMobileNavigation();\n                            }}\n                          >\n                            <CircleIcon className=\"size-1.5 fill-current opacity-35\" aria-hidden=\"true\" />\n                            <span>{task.title}</span>\n                            {task.trailing ? <span className=\"ml-auto shrink-0\">{task.trailing}</span> : null}\n                          </SidebarMenuButton>\n                        </SidebarMenuItem>\n                      );\n                    })}\n                  </SidebarMenu>\n                </CollapsibleContent>\n              </Collapsible>\n            ))}\n          </div>\n        </SidebarGroup>\n      </SidebarContent>\n\n      {sidebarFooter ? <SidebarFooter className=\"border-t border-sidebar-border\">{sidebarFooter}</SidebarFooter> : null}\n      <SidebarRail />\n    </Sidebar>\n  );\n}\n\nexport type CodingAgentConversationProps = Omit<ComponentProps<typeof ChatLayout>, \"children\"> & {\n  children: ReactNode;\n  composer: ReactNode;\n};\n\nexport function CodingAgentConversation({ children, composer, className, ...props }: CodingAgentConversationProps) {\n  return (\n    <ChatLayout className={cn(\"h-full min-h-0 max-w-none rounded-none border-0 bg-transparent shadow-none\", className)} {...props}>\n      <ChatThread className=\"px-0 py-8 sm:px-0\">\n        <div className=\"mx-auto flex min-h-full w-full max-w-3xl flex-col gap-8 px-4 sm:px-8\">{children}</div>\n      </ChatThread>\n      <div className=\"mx-auto w-full max-w-3xl px-2 sm:px-6\">{composer}</div>\n    </ChatLayout>\n  );\n}\n\nexport type CodingAgentSuggestion = {\n  id: string;\n  title: string;\n  description?: string;\n  icon?: ReactNode;\n  prompt?: string;\n};\n\nexport type CodingAgentEmptyStateProps = Omit<ComponentProps<\"section\">, \"title\"> & {\n  title?: ReactNode;\n  description?: ReactNode;\n  icon?: ReactNode;\n  suggestions?: readonly CodingAgentSuggestion[];\n  onSuggestionSelect?: (prompt: string, suggestion: CodingAgentSuggestion) => void;\n};\n\nexport function CodingAgentEmptyState({\n  title = \"What should we build?\",\n  description,\n  icon,\n  suggestions = [],\n  onSuggestionSelect,\n  className,\n  ...props\n}: CodingAgentEmptyStateProps) {\n  return (\n    <section className={cn(\"my-auto flex w-full flex-col items-center py-10 text-center\", className)} {...props}>\n      <div className=\"mb-5 flex size-11 items-center justify-center rounded-2xl border bg-card text-muted-foreground shadow-sm\">\n        {icon ?? <BotIcon className=\"size-5\" aria-hidden=\"true\" />}\n      </div>\n      <h2 className=\"text-balance text-xl font-medium tracking-tight sm:text-2xl\">{title}</h2>\n      {description ? <p className=\"mt-2 max-w-xl text-sm leading-6 text-muted-foreground\">{description}</p> : null}\n      {suggestions.length > 0 ? (\n        <div className=\"mt-8 grid w-full grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4\">\n          {suggestions.map((suggestion) => (\n            <Button\n              key={suggestion.id}\n              variant=\"surface\"\n              size=\"lg\"\n              className=\"h-auto min-h-28 w-full items-start justify-start whitespace-normal rounded-[var(--radius-panel)] px-4 py-4 text-left\"\n              onClick={() => onSuggestionSelect?.(suggestion.prompt ?? suggestion.title, suggestion)}\n            >\n              <span className=\"flex min-w-0 flex-col items-start gap-3\">\n                <span className=\"text-primary-text\">{suggestion.icon}</span>\n                <span className=\"text-sm font-medium leading-5 text-foreground\">{suggestion.title}</span>\n                {suggestion.description ? (\n                  <span className=\"text-caption leading-5 text-muted-foreground\">{suggestion.description}</span>\n                ) : null}\n              </span>\n            </Button>\n          ))}\n        </div>\n      ) : null}\n    </section>\n  );\n}\n"
    }
  ],
  "meta": {}
}
