{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "request-inspector",
  "title": "AI Request Inspector",
  "description": "Display raw AI request/response data with collapsible sections, JSON syntax highlighting, copy buttons, and status code badges for debugging API calls.",
  "dependencies": [
    "@radix-ui/react-collapsible",
    "lucide-react"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/blocks/ai/ai-request-inspector/components/elements/ai-request-inspector.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport * as CollapsiblePrimitive from \"@radix-ui/react-collapsible\";\nimport {\n  ArrowDown,\n  ArrowUp,\n  Check,\n  ChevronDown,\n  Clock,\n  Copy,\n} from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\n\ninterface AiRequestInspectorContextValue {\n  request?: RequestData;\n  response?: ResponseData;\n  timestamp?: Date;\n  duration?: number;\n}\n\ninterface RequestData {\n  url?: string;\n  method?: string;\n  headers?: Record<string, string>;\n  body?: unknown;\n}\n\ninterface ResponseData {\n  status?: number;\n  headers?: Record<string, string>;\n  body?: unknown;\n}\n\nconst AiRequestInspectorContext =\n  React.createContext<AiRequestInspectorContextValue | null>(null);\n\nfunction useRequestInspectorContext() {\n  const context = React.useContext(AiRequestInspectorContext);\n  if (!context) {\n    throw new Error(\n      \"AiRequestInspector components must be used within <AiRequestInspector>\",\n    );\n  }\n  return context;\n}\n\ninterface AiRequestInspectorProps {\n  request?: RequestData;\n  response?: ResponseData;\n  timestamp?: Date;\n  duration?: number;\n  children?: React.ReactNode;\n  className?: string;\n}\n\nfunction AiRequestInspector({\n  request,\n  response,\n  timestamp,\n  duration,\n  children,\n  className,\n}: AiRequestInspectorProps) {\n  const contextValue = React.useMemo(\n    () => ({ request, response, timestamp, duration }),\n    [request, response, timestamp, duration],\n  );\n\n  return (\n    <AiRequestInspectorContext.Provider value={contextValue}>\n      <div\n        data-slot=\"ai-request-inspector\"\n        className={cn(\n          \"rounded-lg border border-border bg-card text-card-foreground overflow-hidden\",\n          className,\n        )}\n      >\n        {children}\n      </div>\n    </AiRequestInspectorContext.Provider>\n  );\n}\n\ninterface AiRequestInspectorHeaderProps {\n  children?: React.ReactNode;\n  className?: string;\n}\n\nfunction AiRequestInspectorHeader({\n  children,\n  className,\n}: AiRequestInspectorHeaderProps) {\n  const { request, response, timestamp, duration } =\n    useRequestInspectorContext();\n\n  const statusColor = React.useMemo(() => {\n    if (!response?.status) return \"bg-muted text-muted-foreground\";\n    if (response.status >= 200 && response.status < 300) {\n      return \"bg-green-100 text-green-700 dark:bg-green-950 dark:text-green-300\";\n    }\n    if (response.status >= 400) {\n      return \"bg-red-100 text-red-700 dark:bg-red-950 dark:text-red-300\";\n    }\n    return \"bg-yellow-100 text-yellow-700 dark:bg-yellow-950 dark:text-yellow-300\";\n  }, [response?.status]);\n\n  const formattedDuration = React.useMemo(() => {\n    if (duration === undefined) return null;\n    if (duration < 1000) return `${duration}ms`;\n    return `${(duration / 1000).toFixed(2)}s`;\n  }, [duration]);\n\n  const formattedTime = React.useMemo(() => {\n    if (!timestamp) return null;\n    return timestamp.toLocaleTimeString();\n  }, [timestamp]);\n\n  return (\n    <div\n      data-slot=\"ai-request-inspector-header\"\n      className={cn(\n        \"flex items-center gap-3 px-4 py-3 border-b border-border bg-muted/30\",\n        className,\n      )}\n    >\n      <div className=\"flex items-center gap-2 flex-1 min-w-0\">\n        {request?.method && (\n          <span className=\"shrink-0 rounded bg-primary/10 px-2 py-0.5 text-xs font-semibold text-primary font-mono\">\n            {request.method}\n          </span>\n        )}\n        {request?.url && (\n          <span className=\"truncate text-sm font-mono text-muted-foreground\">\n            {request.url}\n          </span>\n        )}\n      </div>\n      <div className=\"flex items-center gap-2 shrink-0\">\n        {response?.status && (\n          <span\n            className={cn(\n              \"inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium\",\n              statusColor,\n            )}\n          >\n            {response.status}\n          </span>\n        )}\n        {formattedDuration && (\n          <span className=\"inline-flex items-center gap-1 text-xs text-muted-foreground\">\n            <Clock className=\"size-3\" />\n            {formattedDuration}\n          </span>\n        )}\n        {formattedTime && (\n          <span className=\"text-xs text-muted-foreground\">{formattedTime}</span>\n        )}\n      </div>\n      {children}\n    </div>\n  );\n}\n\ninterface AiRequestInspectorSectionProps {\n  title: string;\n  icon?: React.ReactNode;\n  defaultOpen?: boolean;\n  children?: React.ReactNode;\n  className?: string;\n}\n\nfunction AiRequestInspectorSection({\n  title,\n  icon,\n  defaultOpen = false,\n  children,\n  className,\n}: AiRequestInspectorSectionProps) {\n  const [isOpen, setIsOpen] = React.useState(defaultOpen);\n\n  return (\n    <CollapsiblePrimitive.Root\n      data-slot=\"ai-request-inspector-section\"\n      open={isOpen}\n      onOpenChange={setIsOpen}\n      className={cn(\"border-b border-border last:border-b-0\", className)}\n    >\n      <CollapsiblePrimitive.Trigger className=\"flex w-full items-center gap-2 px-4 py-2 text-sm font-medium transition-colors hover:bg-muted/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\">\n        {icon}\n        <span className=\"flex-1 text-left\">{title}</span>\n        <ChevronDown\n          className={cn(\n            \"size-4 text-muted-foreground transition-transform duration-200\",\n            isOpen && \"rotate-180\",\n          )}\n        />\n      </CollapsiblePrimitive.Trigger>\n      <CollapsiblePrimitive.Content className=\"data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down\">\n        <div className=\"px-4 pb-4\">{children}</div>\n      </CollapsiblePrimitive.Content>\n    </CollapsiblePrimitive.Root>\n  );\n}\n\ninterface AiRequestInspectorRequestProps {\n  defaultOpen?: boolean;\n  className?: string;\n}\n\nfunction AiRequestInspectorRequest({\n  defaultOpen = true,\n  className,\n}: AiRequestInspectorRequestProps) {\n  const { request } = useRequestInspectorContext();\n\n  if (!request) return null;\n\n  return (\n    <AiRequestInspectorSection\n      title=\"Request\"\n      icon={<ArrowUp className=\"size-4 text-blue-500\" />}\n      defaultOpen={defaultOpen}\n      className={className}\n    >\n      <div className=\"space-y-3\">\n        {request.headers && Object.keys(request.headers).length > 0 && (\n          <AiRequestInspectorJson label=\"Headers\" data={request.headers} />\n        )}\n        {request.body !== undefined && (\n          <AiRequestInspectorJson label=\"Body\" data={request.body} />\n        )}\n      </div>\n    </AiRequestInspectorSection>\n  );\n}\n\ninterface AiRequestInspectorResponseProps {\n  defaultOpen?: boolean;\n  className?: string;\n}\n\nfunction AiRequestInspectorResponse({\n  defaultOpen = true,\n  className,\n}: AiRequestInspectorResponseProps) {\n  const { response } = useRequestInspectorContext();\n\n  if (!response) return null;\n\n  return (\n    <AiRequestInspectorSection\n      title=\"Response\"\n      icon={<ArrowDown className=\"size-4 text-green-500\" />}\n      defaultOpen={defaultOpen}\n      className={className}\n    >\n      <div className=\"space-y-3\">\n        {response.headers && Object.keys(response.headers).length > 0 && (\n          <AiRequestInspectorJson label=\"Headers\" data={response.headers} />\n        )}\n        {response.body !== undefined && (\n          <AiRequestInspectorJson label=\"Body\" data={response.body} />\n        )}\n      </div>\n    </AiRequestInspectorSection>\n  );\n}\n\ninterface AiRequestInspectorJsonProps {\n  label: string;\n  data: unknown;\n  className?: string;\n}\n\nfunction AiRequestInspectorJson({\n  label,\n  data,\n  className,\n}: AiRequestInspectorJsonProps) {\n  const [copied, setCopied] = React.useState(false);\n\n  const formattedJson = React.useMemo(\n    () => JSON.stringify(data, null, 2),\n    [data],\n  );\n\n  const handleCopy = React.useCallback(async () => {\n    await navigator.clipboard.writeText(formattedJson);\n    setCopied(true);\n    setTimeout(() => setCopied(false), 2000);\n  }, [formattedJson]);\n\n  return (\n    <div\n      data-slot=\"ai-request-inspector-json\"\n      className={cn(\"space-y-1.5\", className)}\n    >\n      <div className=\"flex items-center justify-between\">\n        <span className=\"text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n          {label}\n        </span>\n        <button\n          type=\"button\"\n          onClick={handleCopy}\n          className=\"inline-flex items-center gap-1 rounded px-1.5 py-0.5 text-xs text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\"\n        >\n          {copied ? (\n            <>\n              <Check className=\"size-3\" />\n              Copied\n            </>\n          ) : (\n            <>\n              <Copy className=\"size-3\" />\n              Copy\n            </>\n          )}\n        </button>\n      </div>\n      <pre className=\"rounded-md bg-muted/50 p-3 overflow-x-auto text-xs font-mono text-foreground max-h-80 overflow-y-auto\">\n        <code>{formattedJson}</code>\n      </pre>\n    </div>\n  );\n}\n\nexport {\n  AiRequestInspector,\n  AiRequestInspectorHeader,\n  AiRequestInspectorSection,\n  AiRequestInspectorRequest,\n  AiRequestInspectorResponse,\n  AiRequestInspectorJson,\n};\nexport type { AiRequestInspectorProps, RequestData, ResponseData };\n",
      "type": "registry:component"
    }
  ],
  "docs": "Compound component for inspecting AI API requests. Features collapsible request/response sections, JSON formatting with copy-to-clipboard, HTTP method and URL display, status code badges with color coding (green: 2xx, yellow: 3xx, red: 4xx/5xx), duration and timestamp display.",
  "categories": [
    "ai"
  ],
  "type": "registry:ui"
}