{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "thinking",
  "title": "Thinking",
  "description": "A composable activity disclosure with explicit states and structured evidence.",
  "dependencies": [
    "class-variance-authority",
    "lucide-react"
  ],
  "registryDependencies": [
    "collapsible",
    "https://uaiblocks.dev/r/uai-theme.json",
    "https://uaiblocks.dev/r/uai-utils.json"
  ],
  "files": [
    {
      "path": "src/registry/uai/components/thinking.tsx",
      "content": "\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport {\n  AlertTriangle,\n  Check,\n  ChevronDown,\n  Circle,\n  FileText,\n  LoaderCircle,\n  type LucideIcon,\n  Search,\n  Wrench,\n} from \"lucide-react\";\nimport {\n  Children,\n  type ComponentProps,\n  createContext,\n  type ReactNode,\n  useContext,\n  useId,\n  useState,\n} from \"react\";\n\nimport { Collapsible, CollapsibleTrigger } from \"@/components/ui/collapsible\";\nimport { cn } from \"@/lib/uai-utils\";\n\nexport const THINKING_VARIANTS = [\"card\", \"inline\", \"compact\"] as const;\nexport const THINKING_STATUSES = [\"thinking\", \"complete\", \"error\"] as const;\n\nexport type ThinkingVariant = (typeof THINKING_VARIANTS)[number];\nexport type ThinkingStatus = (typeof THINKING_STATUSES)[number];\n\nexport type ThinkingProps = ComponentProps<\"section\"> & {\n  variant?: ThinkingVariant;\n  status?: ThinkingStatus;\n  defaultOpen?: boolean;\n};\n\nconst statusDetails: Record<\n  ThinkingStatus,\n  { title: string; summary: string; label: string; icon: LucideIcon }\n> = {\n  thinking: {\n    title: \"Thinking\",\n    summary: \"Working through the request.\",\n    label: \"Working\",\n    icon: LoaderCircle,\n  },\n  complete: {\n    title: \"Work complete\",\n    summary: \"The response is ready.\",\n    label: \"Complete\",\n    icon: Check,\n  },\n  error: {\n    title: \"Work stopped\",\n    summary: \"Something interrupted this run.\",\n    label: \"Error\",\n    icon: AlertTriangle,\n  },\n};\n\ntype ThinkingContextValue = {\n  contentId: string;\n  variant: ThinkingVariant;\n  status: ThinkingStatus;\n  open: boolean;\n};\n\nconst ThinkingContext = createContext<ThinkingContextValue | null>(null);\n\nfunction useThinking(name: string) {\n  const context = useContext(ThinkingContext);\n  if (!context) throw new Error(`${name} must be used within Thinking`);\n  return context;\n}\n\nconst thinkingVariants = cva(\n  \"overflow-hidden text-card-foreground transition-colors duration-150 motion-reduce:transition-none\",\n  {\n    variants: {\n      variant: {\n        card: \"rounded-[14px] border bg-card\",\n        inline: \"rounded-none border-0 bg-transparent\",\n        compact: \"rounded-xl border bg-card\",\n      },\n      error: { true: \"\", false: \"\" },\n    },\n    compoundVariants: [\n      {\n        variant: [\"card\", \"compact\"],\n        error: true,\n        className: \"border-[color-mix(in_oklab,var(--destructive)_40%,var(--border))]\",\n      },\n    ],\n  },\n);\n\nexport function Thinking({\n  variant = \"card\",\n  status = \"thinking\",\n  defaultOpen = true,\n  className,\n  children,\n  ...props\n}: ThinkingProps) {\n  const [open, setOpen] = useState(defaultOpen);\n  const contentId = `${useId()}-content`;\n\n  return (\n    <ThinkingContext.Provider value={{ contentId, variant, status, open }}>\n      <Collapsible asChild open={open} onOpenChange={setOpen}>\n        <section\n          data-slot=\"thinking\"\n          className={cn(thinkingVariants({ variant, error: status === \"error\" }), className)}\n          {...props}\n          data-variant={variant}\n          data-status={status}\n          aria-busy={status === \"thinking\"}\n        >\n          {children}\n        </section>\n      </Collapsible>\n    </ThinkingContext.Provider>\n  );\n}\n\nexport type ThinkingTriggerProps = Omit<ComponentProps<\"button\">, \"title\"> & {\n  title?: ReactNode;\n  summary?: ReactNode;\n  duration?: ReactNode;\n};\n\nexport function ThinkingTrigger({\n  title,\n  summary,\n  duration,\n  className,\n  ...props\n}: ThinkingTriggerProps) {\n  const context = useThinking(\"ThinkingTrigger\");\n  const copy = statusDetails[context.status];\n  const StatusIcon = copy.icon;\n\n  return (\n    <CollapsibleTrigger\n      type=\"button\"\n      data-slot=\"thinking-trigger\"\n      className={cn(\n        \"group/trigger flex w-full items-center gap-3 px-3.5 py-3 text-left outline-none transition-colors duration-120 ease-out hover:bg-accent/60 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset motion-reduce:transition-none\",\n        context.variant === \"inline\" && \"rounded-[10px] px-2 py-2\",\n        context.variant === \"compact\" && \"gap-2.5 px-3 py-2\",\n        className,\n      )}\n      {...props}\n      aria-controls={context.contentId}\n    >\n      <span\n        className={cn(\n          \"grid size-7 shrink-0 place-items-center rounded-lg bg-muted\",\n          context.variant === \"compact\" && \"size-6 rounded-md\",\n          context.status === \"complete\" && \"bg-success/14\",\n          context.status === \"error\" && \"bg-destructive/14\",\n        )}\n      >\n        <StatusIcon\n          key={context.status}\n          strokeWidth={2}\n          className={cn(\n            \"size-3.5\",\n            context.status !== \"thinking\" &&\n              \"animate-in duration-200 ease-out-quint fade-in-0 zoom-in-50 motion-reduce:animate-none\",\n            context.status === \"thinking\" &&\n              \"text-muted-foreground motion-safe:animate-spin motion-reduce:animate-none\",\n            context.status === \"complete\" && \"text-success-ink\",\n            context.status === \"error\" && \"text-destructive-ink\",\n          )}\n          aria-hidden=\"true\"\n        />\n      </span>\n      <span className=\"min-w-0 flex-1\">\n        <span className=\"flex min-w-0 items-center gap-2\">\n          <span\n            className={cn(\n              \"truncate text-[13px]/[18px] font-medium\",\n              context.status === \"thinking\" && \"shimmer-text\",\n            )}\n          >\n            {title ?? copy.title}\n          </span>\n          <span\n            className={cn(\n              \"shrink-0 rounded-full px-2 py-px text-[11.5px]/4 font-medium\",\n              context.status === \"thinking\" && \"bg-muted text-muted-foreground\",\n              context.status === \"complete\" && \"bg-success/14 text-success-ink\",\n              context.status === \"error\" && \"bg-destructive/14 text-destructive-ink\",\n            )}\n          >\n            {copy.label}\n          </span>\n        </span>\n        <span\n          className=\"mt-0.5 block truncate text-[12.5px]/[18px] text-muted-foreground\"\n          role=\"status\"\n          aria-live=\"polite\"\n          aria-atomic=\"true\"\n        >\n          {summary ?? copy.summary}\n        </span>\n      </span>\n      {duration ? (\n        <span className=\"shrink-0 font-mono text-[11.5px]/4 tabular-nums text-subtle-foreground\">\n          {duration}\n        </span>\n      ) : null}\n      <ChevronDown\n        className={cn(\n          \"size-4 shrink-0 text-subtle-foreground transition-[rotate,color] duration-180 ease-out-quint group-hover/trigger:text-muted-foreground motion-reduce:transition-none\",\n          context.open && \"rotate-180\",\n        )}\n        aria-hidden=\"true\"\n      />\n    </CollapsibleTrigger>\n  );\n}\n\nfunction getEmptyLabel(status: ThinkingStatus) {\n  if (status === \"thinking\") return \"Waiting for the first activity…\";\n  if (status === \"error\") return \"No activity details are available.\";\n  return \"No activity was recorded.\";\n}\n\nexport type ThinkingContentProps = ComponentProps<\"div\"> & {\n  emptyLabel?: ReactNode;\n};\n\nexport function ThinkingContent({\n  emptyLabel,\n  children,\n  className,\n  ...props\n}: ThinkingContentProps) {\n  const context = useThinking(\"ThinkingContent\");\n  const hasActivity = Children.count(children) > 0;\n\n  return (\n    <div\n      inert={!context.open}\n      className={cn(\n        \"grid transition-[grid-template-rows,opacity] duration-300 ease-out-quint motion-reduce:transition-none\",\n        context.open ? \"grid-rows-[1fr] opacity-100\" : \"grid-rows-[0fr] opacity-0\",\n      )}\n    >\n      <div className=\"min-h-0 overflow-hidden\">\n        {/* Always mounted: the outer grid animates the height, so the region never unmounts. */}\n        <div\n          data-slot=\"thinking-content\"\n          data-state={context.open ? \"open\" : \"closed\"}\n          className={cn(\n            \"border-t px-3.5 pt-1 pb-1.5\",\n            context.variant === \"inline\" && \"ml-[18px] border-t-0 border-l pl-3 pr-2\",\n            context.variant === \"compact\" && \"px-3 pb-1\",\n            className,\n          )}\n          {...props}\n          id={context.contentId}\n        >\n          {hasActivity ? (\n            <div role=\"log\" aria-live=\"polite\" aria-relevant=\"additions text\">\n              <ol>{children}</ol>\n            </div>\n          ) : (\n            <p className=\"py-3 text-[13px]/[18px] text-subtle-foreground\">\n              {emptyLabel ?? getEmptyLabel(context.status)}\n            </p>\n          )}\n        </div>\n      </div>\n    </div>\n  );\n}\n\ntype ThinkingActivityBaseProps = Omit<ComponentProps<\"li\">, \"children\"> & {\n  children: ReactNode;\n  elapsed?: ReactNode;\n};\n\nexport type ThinkingActivityProps = ThinkingActivityBaseProps &\n  (\n    | { type: \"progress\"; tool?: never; path?: never; query?: never }\n    | { type: \"tool\"; tool: ReactNode; path?: never; query?: never }\n    | { type: \"file\"; path: ReactNode; tool?: never; query?: never }\n    | { type: \"search\"; query: ReactNode; tool?: never; path?: never }\n  );\n\nconst activityDetails: Record<ThinkingActivityProps[\"type\"], { label: string; icon: LucideIcon }> =\n  {\n    progress: { label: \"Update\", icon: Circle },\n    tool: { label: \"Tool\", icon: Wrench },\n    file: { label: \"File\", icon: FileText },\n    search: { label: \"Search\", icon: Search },\n  };\n\nexport function ThinkingActivity(props: ThinkingActivityProps) {\n  const { type, children, elapsed, className, ...itemProps } = props;\n  const details = activityDetails[type];\n  const ActivityIcon = details.icon;\n  const evidence =\n    type === \"tool\"\n      ? props.tool\n      : type === \"file\"\n        ? props.path\n        : type === \"search\"\n          ? props.query\n          : null;\n  const { tool: _tool, path: _path, query: _query, ...domProps } = itemProps;\n\n  return (\n    <li\n      data-slot=\"thinking-activity\"\n      className={cn(\n        \"group grid min-w-0 grid-cols-[24px_minmax(0,1fr)_auto] gap-x-2.5 py-2.5\",\n        \"animate-in fade-in-0 slide-in-from-bottom-1 duration-240 ease-out-quint fill-mode-backwards motion-reduce:animate-none\",\n        \"nth-2:[animation-delay:40ms] nth-3:[animation-delay:80ms] nth-4:[animation-delay:120ms] nth-5:[animation-delay:160ms] nth-6:[animation-delay:200ms]\",\n        className,\n      )}\n      {...domProps}\n    >\n      <span className=\"relative grid size-6 place-items-center text-subtle-foreground after:absolute after:top-6 after:bottom-[-10px] after:left-1/2 after:w-px after:-translate-x-1/2 after:bg-border group-last:after:hidden\">\n        <ActivityIcon className=\"size-3.5\" strokeWidth={1.75} aria-hidden=\"true\" />\n      </span>\n      <span className=\"min-w-0\">\n        <span className=\"flex min-w-0 items-baseline gap-2 pt-[3px]\">\n          <span className=\"min-w-0 text-[13px]/[18px] font-medium text-card-foreground\">\n            {children}\n          </span>\n          <span className=\"shrink-0 text-[11.5px]/4 text-subtle-foreground\">{details.label}</span>\n        </span>\n        {evidence ? (\n          <span className=\"mt-1.5 inline-block max-w-full rounded-md bg-muted px-1.5 py-0.5 font-mono text-[11.5px]/4 break-all text-muted-foreground\">\n            {evidence}\n          </span>\n        ) : null}\n      </span>\n      {elapsed ? (\n        <span className=\"pt-1 pl-2 font-mono text-[11.5px]/4 tabular-nums text-subtle-foreground\">\n          {elapsed}\n        </span>\n      ) : null}\n    </li>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/uai/thinking.tsx"
    }
  ],
  "categories": [
    "ai",
    "feedback"
  ],
  "type": "registry:ui"
}