{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "activity",
  "type": "registry:component",
  "title": "Activity",
  "description": "Shared static and collapsible activity anatomy with bounded, scrollable detail content.",
  "dependencies": [
    "lucide-react@^1.27.0"
  ],
  "registryDependencies": [
    "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/scroll-area.json"
  ],
  "files": [
    {
      "path": "src/registry/sources/control-ui/activity.tsx",
      "target": "@components/control-ui/activity.tsx",
      "type": "registry:component",
      "content": "\"use client\";\n\nimport { Check, ChevronRight, CircleAlert, CircleDashed, LoaderCircle } from \"lucide-react\";\nimport type { ComponentProps, ReactNode } from \"react\";\nimport { createContext, useContext } from \"react\";\n\nimport type { ActivityKind, ActivityProps, ActivityState, ScrollAreaProps } from \"@/components/control-ui/contracts\";\nimport { cn } from \"@/components/control-ui/lib/cn\";\nimport { skinSlot } from \"@/components/control-ui/skin\";\nimport { Collapsible, CollapsibleContent, CollapsibleTrigger } from \"@/components/control-ui/ui/collapsible\";\nimport { ScrollArea } from \"@/components/control-ui/ui/scroll-area\";\n\nconst activityStatusLabels = {\n  pending: \"Pending\",\n  running: \"Running\",\n  success: \"Complete\",\n  error: \"Failed\",\n} satisfies Record<ActivityState, string>;\n\nconst activityStatusIcons = {\n  pending: CircleDashed,\n  running: LoaderCircle,\n  success: Check,\n  error: CircleAlert,\n} satisfies Record<ActivityState, typeof CircleDashed>;\n\ntype ActivityContextValue = {\n  isError: boolean;\n  isRunning: boolean;\n  kind: ActivityKind;\n  name?: string;\n  state: ActivityState;\n  statusLabel: ReactNode;\n};\n\nconst ActivityContext = createContext<ActivityContextValue | null>(null);\n\nfunction useActivityContext() {\n  const context = useContext(ActivityContext);\n  if (!context) throw new Error(\"Activity compound components must be rendered inside <Activity>.\");\n  return context;\n}\n\nfunction formatActivityTitle(value: string) {\n  const words = value\n    .replace(/([a-z\\d])([A-Z])/g, \"$1 $2\")\n    .replace(/[_-]+/g, \" \")\n    .trim();\n\n  return words ? `${words[0].toUpperCase()}${words.slice(1)}` : value;\n}\n\nexport function Activity({ kind = \"default\", name, state = \"pending\", statusLabel, className, children, ...props }: ActivityProps) {\n  const context = {\n    isError: state === \"error\",\n    isRunning: state === \"running\",\n    kind,\n    name,\n    state,\n    statusLabel: statusLabel ?? activityStatusLabels[state],\n  };\n  return (\n    <ActivityContext.Provider value={context}>\n      <Collapsible\n        data-control-ui=\"activity\"\n        data-slot=\"root\"\n        data-activity-state={state}\n        data-activity-kind={kind}\n        data-activity-name={name}\n        data-status={state}\n        data-surface={kind === \"tool\" ? \"panel\" : undefined}\n        aria-busy={context.isRunning || undefined}\n        className={cn(\"group/activity my-1 min-w-0 text-body\", skinSlot(\"activity\", \"root\", { kind, state }), className)}\n        {...props}\n      >\n        <span\n          role={context.isError ? \"alert\" : \"status\"}\n          aria-live={context.isError ? \"assertive\" : \"polite\"}\n          aria-atomic=\"true\"\n          data-control-ui=\"activity\"\n          data-slot=\"announcement\"\n          data-status={state}\n          className={cn(\"sr-only\", skinSlot(\"activity\", \"announcement\", {}))}\n        >\n          {context.statusLabel}\n        </span>\n        {children}\n      </Collapsible>\n    </ActivityContext.Provider>\n  );\n}\n\ntype ActivityRowProps = ComponentProps<\"div\">;\n\nconst activityRowClassName = \"flex min-h-8 w-fit max-w-full items-center gap-2 px-1.5 py-1 text-left text-muted-foreground\";\n\nexport function ActivityRow({ className, ...props }: ActivityRowProps) {\n  return (\n    <div\n      data-control-ui=\"activity\"\n      data-slot=\"row\"\n      {...props}\n      className={cn(activityRowClassName, skinSlot(\"activity\", \"row\", {}), className)}\n    />\n  );\n}\n\nexport type ActivityTriggerProps = ComponentProps<typeof CollapsibleTrigger> & {\n  chevronProps?: ComponentProps<typeof ChevronRight> & { \"data-slot\"?: string };\n};\n\nexport function ActivityTrigger({ className, children, chevronProps, ...props }: ActivityTriggerProps) {\n  const activity = useActivityContext();\n  return (\n    <CollapsibleTrigger\n      data-control-ui=\"activity\"\n      data-slot=\"trigger\"\n      {...props}\n      className={cn(\n        activityRowClassName,\n        \"rounded-[var(--radius-control)] hover:bg-muted/45 hover:text-foreground focus-visible:bg-muted/45\",\n        skinSlot(\"activity\", \"trigger\", { kind: activity.kind }),\n        className,\n      )}\n    >\n      {children}\n      <ChevronRight\n        aria-hidden=\"true\"\n        data-control-ui=\"activity\"\n        data-slot=\"chevron\"\n        {...chevronProps}\n        className={cn(\"size-3.5 shrink-0 text-muted-foreground/75\", chevronProps?.className)}\n      />\n    </CollapsibleTrigger>\n  );\n}\n\nexport type ActivityIconProps = ComponentProps<\"span\">;\n\nexport function ActivityIcon({ className, children, ...props }: ActivityIconProps) {\n  const activity = useActivityContext();\n  const Icon = activityStatusIcons[activity.state];\n  return (\n    <span\n      aria-hidden=\"true\"\n      data-control-ui=\"activity\"\n      data-slot=\"icon\"\n      {...props}\n      className={cn(\n        \"flex size-4 shrink-0 items-center justify-center text-muted-foreground [&_svg]:size-4\",\n        activity.isRunning && \"text-foreground\",\n        activity.isRunning && !children && \"[&_svg]:animate-spin\",\n        activity.isError && \"text-destructive-text\",\n        skinSlot(\"activity\", \"icon\", { kind: activity.kind, state: activity.state }),\n        className,\n      )}\n    >\n      {children ?? <Icon />}\n    </span>\n  );\n}\n\nexport type ActivityTitleProps = ComponentProps<\"span\">;\n\nexport function ActivityTitle({ className, children, ...props }: ActivityTitleProps) {\n  const activity = useActivityContext();\n  return (\n    <span\n      data-control-ui=\"activity\"\n      data-slot=\"title\"\n      {...props}\n      className={cn(\"min-w-0 flex-1 truncate text-label font-normal text-current\", skinSlot(\"activity\", \"title\", {}), className)}\n    >\n      {children ?? (activity.name ? formatActivityTitle(activity.name) : undefined)}\n    </span>\n  );\n}\n\nexport type ActivityStatusProps = ComponentProps<\"span\">;\n\nexport function ActivityStatus({ className, children, ...props }: ActivityStatusProps) {\n  const activity = useActivityContext();\n  return (\n    <span\n      data-control-ui=\"activity\"\n      data-slot=\"status\"\n      {...props}\n      data-status={activity.state}\n      className={cn(\n        \"shrink-0 text-caption text-muted-foreground\",\n        activity.isError && \"text-destructive-text\",\n        skinSlot(\"activity\", \"status\", { kind: activity.kind, state: activity.state }),\n        className,\n      )}\n    >\n      {children ?? activity.statusLabel}\n    </span>\n  );\n}\n\nexport type ActivityContentProps = Omit<ComponentProps<typeof CollapsibleContent>, \"children\"> & {\n  children?: ReactNode;\n  maxHeight?: string | false;\n  scrollAreaProps?: Omit<ScrollAreaProps, \"children\" | \"maxHeight\">;\n};\n\nexport function ActivityContent({\n  className,\n  children,\n  maxHeight = \"var(--activity-content-max-height, min(24rem, 50dvh))\",\n  scrollAreaProps,\n  ...props\n}: ActivityContentProps) {\n  const { className: scrollAreaClassName, viewportProps, lockAxis = \"x\", ...resolvedScrollAreaProps } = scrollAreaProps ?? {};\n  const resolvedViewportProps = {\n    ...viewportProps,\n    \"data-control-ui\": viewportProps?.[\"data-control-ui\"] ?? \"activity\",\n    \"data-slot\": viewportProps?.[\"data-slot\"] ?? \"content-viewport\",\n  };\n  return (\n    <CollapsibleContent\n      data-control-ui=\"activity\"\n      data-slot=\"content\"\n      className={cn(skinSlot(\"activity\", \"content\", {}), className)}\n      {...props}\n    >\n      <ScrollArea\n        maxHeight={maxHeight || undefined}\n        lockAxis={lockAxis}\n        viewportProps={resolvedViewportProps}\n        className={cn(\"min-w-0\", scrollAreaClassName)}\n        {...resolvedScrollAreaProps}\n      >\n        <div className=\"grid min-w-0 gap-2 px-1 pb-2 pt-1\">{children}</div>\n      </ScrollArea>\n    </CollapsibleContent>\n  );\n}\n\nexport type ActivityDetailProps = ComponentProps<\"div\">;\n\nexport function ActivityDetail({ className, ...props }: ActivityDetailProps) {\n  return (\n    <div\n      data-control-ui=\"activity\"\n      data-slot=\"detail\"\n      {...props}\n      className={cn(\"grid min-w-0 gap-1\", skinSlot(\"activity\", \"detail\", {}), className)}\n    />\n  );\n}\n\nexport type ActivityDetailLabelProps = ComponentProps<\"div\">;\n\nexport function ActivityDetailLabel({ className, ...props }: ActivityDetailLabelProps) {\n  return (\n    <div\n      data-control-ui=\"activity\"\n      data-slot=\"detail-label\"\n      {...props}\n      className={cn(\"text-caption font-medium text-muted-foreground\", skinSlot(\"activity\", \"detail-label\", {}), className)}\n    />\n  );\n}\n\nexport type ActivityDetailContentProps = ComponentProps<\"div\">;\n\nexport function ActivityDetailContent({ className, ...props }: ActivityDetailContentProps) {\n  return (\n    <div\n      data-control-ui=\"activity\"\n      data-slot=\"detail-content\"\n      {...props}\n      className={cn(\n        \"min-w-0 whitespace-pre-wrap wrap-anywhere text-label leading-5 text-foreground\",\n        skinSlot(\"activity\", \"detail-content\", {}),\n        className,\n      )}\n    />\n  );\n}\n"
    }
  ],
  "meta": {}
}
