{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "agent-context",
  "title": "AI Agent Context",
  "description": "Display and optionally edit agent context data with type indicators, nested object support, and collapsible sections. Visualizes user/team context for AI agents.",
  "dependencies": [
    "@radix-ui/react-collapsible",
    "lucide-react"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/blocks/ai/ai-agent-context/components/elements/ai-agent-context.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport * as CollapsiblePrimitive from \"@radix-ui/react-collapsible\";\nimport {\n  Braces,\n  Check,\n  ChevronDown,\n  ChevronRight,\n  Database,\n  Hash,\n  List,\n  Pencil,\n  ToggleLeft,\n  Type,\n  X,\n} from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\n\ntype ValueType = \"string\" | \"number\" | \"boolean\" | \"object\" | \"array\" | \"null\";\n\ninterface AiAgentContextContextValue {\n  editable: boolean;\n  onChange?: (key: string, value: unknown) => void;\n}\n\nconst AiAgentContextContext =\n  React.createContext<AiAgentContextContextValue | null>(null);\n\nfunction useAgentContextContext() {\n  const context = React.useContext(AiAgentContextContext);\n  if (!context) {\n    throw new Error(\n      \"AiAgentContext components must be used within <AiAgentContext>\",\n    );\n  }\n  return context;\n}\n\nfunction getValueType(value: unknown): ValueType {\n  if (value === null) return \"null\";\n  if (Array.isArray(value)) return \"array\";\n  return typeof value as ValueType;\n}\n\ninterface AiAgentContextProps {\n  context: Record<string, unknown>;\n  title?: string;\n  editable?: boolean;\n  onChange?: (key: string, value: unknown) => void;\n  children?: React.ReactNode;\n  className?: string;\n}\n\nfunction AiAgentContext({\n  context,\n  title,\n  editable = false,\n  onChange,\n  children,\n  className,\n}: AiAgentContextProps) {\n  const contextValue = React.useMemo(\n    () => ({ editable, onChange }),\n    [editable, onChange],\n  );\n\n  return (\n    <AiAgentContextContext.Provider value={contextValue}>\n      <div\n        data-slot=\"ai-agent-context\"\n        className={cn(\n          \"rounded-lg border border-border bg-card text-card-foreground\",\n          className,\n        )}\n      >\n        {children || (\n          <>\n            <AiAgentContextHeader>{title}</AiAgentContextHeader>\n            <AiAgentContextContent context={context} />\n          </>\n        )}\n      </div>\n    </AiAgentContextContext.Provider>\n  );\n}\n\ninterface AiAgentContextHeaderProps {\n  children?: React.ReactNode;\n  className?: string;\n}\n\nfunction AiAgentContextHeader({\n  children,\n  className,\n}: AiAgentContextHeaderProps) {\n  return (\n    <div\n      data-slot=\"ai-agent-context-header\"\n      className={cn(\n        \"flex items-center gap-2 border-b border-border px-4 py-3\",\n        className,\n      )}\n    >\n      <div className=\"flex size-8 shrink-0 items-center justify-center rounded-md bg-muted\">\n        <Database className=\"size-4 text-muted-foreground\" />\n      </div>\n      <h3 className=\"font-semibold text-sm\">{children || \"Agent Context\"}</h3>\n    </div>\n  );\n}\n\ninterface AiAgentContextContentProps {\n  context: Record<string, unknown>;\n  className?: string;\n}\n\nfunction AiAgentContextContent({\n  context,\n  className,\n}: AiAgentContextContentProps) {\n  const entries = React.useMemo(() => Object.entries(context), [context]);\n\n  if (entries.length === 0) {\n    return <AiAgentContextEmpty />;\n  }\n\n  return (\n    <div\n      data-slot=\"ai-agent-context-content\"\n      className={cn(\"p-4 space-y-1\", className)}\n    >\n      {entries.map(([key, value]) => (\n        <AiAgentContextEntry key={key} path={key} name={key} value={value} />\n      ))}\n    </div>\n  );\n}\n\ninterface AiAgentContextEntryProps {\n  path: string;\n  name: string;\n  value: unknown;\n  depth?: number;\n  className?: string;\n}\n\nfunction AiAgentContextEntry({\n  path,\n  name,\n  value,\n  depth = 0,\n  className,\n}: AiAgentContextEntryProps) {\n  const { editable, onChange } = useAgentContextContext();\n  const [isOpen, setIsOpen] = React.useState(depth < 2);\n  const [isEditing, setIsEditing] = React.useState(false);\n  const [editValue, setEditValue] = React.useState(\"\");\n\n  const valueType = getValueType(value);\n  const isExpandable = valueType === \"object\" || valueType === \"array\";\n  const childEntries = React.useMemo(() => {\n    if (valueType === \"object\" && value !== null) {\n      return Object.entries(value as Record<string, unknown>);\n    }\n    if (valueType === \"array\") {\n      return (value as unknown[]).map((v, i) => [String(i), v] as const);\n    }\n    return [];\n  }, [value, valueType]);\n\n  const typeIcon = React.useMemo(() => {\n    const icons: Record<ValueType, React.ReactNode> = {\n      string: <Type className=\"size-3\" />,\n      number: <Hash className=\"size-3\" />,\n      boolean: <ToggleLeft className=\"size-3\" />,\n      object: <Braces className=\"size-3\" />,\n      array: <List className=\"size-3\" />,\n      null: <X className=\"size-3\" />,\n    };\n    return icons[valueType];\n  }, [valueType]);\n\n  const typeColor = React.useMemo(() => {\n    const colors: Record<ValueType, string> = {\n      string: \"text-green-600 dark:text-green-400\",\n      number: \"text-blue-600 dark:text-blue-400\",\n      boolean: \"text-amber-600 dark:text-amber-400\",\n      object: \"text-purple-600 dark:text-purple-400\",\n      array: \"text-cyan-600 dark:text-cyan-400\",\n      null: \"text-muted-foreground\",\n    };\n    return colors[valueType];\n  }, [valueType]);\n\n  const displayValue = React.useMemo(() => {\n    if (valueType === \"null\") return \"null\";\n    if (valueType === \"boolean\") return String(value);\n    if (valueType === \"string\") return `\"${value}\"`;\n    if (valueType === \"number\") return String(value);\n    if (valueType === \"array\") return `Array(${childEntries.length})`;\n    if (valueType === \"object\") return `{${childEntries.length} keys}`;\n    return String(value);\n  }, [value, valueType, childEntries.length]);\n\n  const handleStartEdit = React.useCallback(() => {\n    if (editable && !isExpandable) {\n      setEditValue(\n        valueType === \"string\" ? String(value) : JSON.stringify(value),\n      );\n      setIsEditing(true);\n    }\n  }, [editable, isExpandable, value, valueType]);\n\n  const handleSaveEdit = React.useCallback(() => {\n    if (onChange) {\n      let parsedValue: unknown = editValue;\n      if (valueType === \"number\") {\n        parsedValue = Number(editValue);\n      } else if (valueType === \"boolean\") {\n        parsedValue = editValue === \"true\";\n      }\n      onChange(path, parsedValue);\n    }\n    setIsEditing(false);\n  }, [onChange, path, editValue, valueType]);\n\n  const handleCancelEdit = React.useCallback(() => {\n    setIsEditing(false);\n  }, []);\n\n  const handleKeyDown = React.useCallback(\n    (e: React.KeyboardEvent) => {\n      if (e.key === \"Enter\") {\n        handleSaveEdit();\n      } else if (e.key === \"Escape\") {\n        handleCancelEdit();\n      }\n    },\n    [handleSaveEdit, handleCancelEdit],\n  );\n\n  if (isExpandable) {\n    return (\n      <CollapsiblePrimitive.Root\n        data-slot=\"ai-agent-context-entry\"\n        open={isOpen}\n        onOpenChange={setIsOpen}\n        className={cn(\"\", className)}\n        style={{ paddingLeft: depth > 0 ? `${depth * 12}px` : undefined }}\n      >\n        <CollapsiblePrimitive.Trigger className=\"flex w-full items-center gap-1.5 py-1 text-sm hover:bg-muted/50 rounded px-1 -mx-1 transition-colors\">\n          <ChevronRight\n            className={cn(\n              \"size-3.5 text-muted-foreground transition-transform\",\n              isOpen && \"rotate-90\",\n            )}\n          />\n          <span\n            className={cn(\"size-4 flex items-center justify-center\", typeColor)}\n          >\n            {typeIcon}\n          </span>\n          <span className=\"font-mono text-xs font-medium\">{name}</span>\n          <span className=\"text-muted-foreground\">:</span>\n          <span className={cn(\"text-xs font-mono\", typeColor)}>\n            {displayValue}\n          </span>\n        </CollapsiblePrimitive.Trigger>\n        <CollapsiblePrimitive.Content className=\"data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down overflow-hidden\">\n          <div className=\"py-0.5\">\n            {childEntries.map(([key, val]) => (\n              <AiAgentContextEntry\n                key={key}\n                path={`${path}.${key}`}\n                name={key}\n                value={val}\n                depth={depth + 1}\n              />\n            ))}\n          </div>\n        </CollapsiblePrimitive.Content>\n      </CollapsiblePrimitive.Root>\n    );\n  }\n\n  return (\n    <div\n      data-slot=\"ai-agent-context-entry\"\n      className={cn(\n        \"group flex items-center gap-1.5 py-1 text-sm rounded px-1 -mx-1\",\n        editable && \"hover:bg-muted/50\",\n        className,\n      )}\n      style={{ paddingLeft: depth > 0 ? `${depth * 12 + 16}px` : \"16px\" }}\n    >\n      <span\n        className={cn(\"size-4 flex items-center justify-center\", typeColor)}\n      >\n        {typeIcon}\n      </span>\n      <span className=\"font-mono text-xs font-medium\">{name}</span>\n      <span className=\"text-muted-foreground\">:</span>\n      {isEditing ? (\n        <div className=\"flex items-center gap-1 flex-1\">\n          <input\n            type=\"text\"\n            value={editValue}\n            onChange={(e) => setEditValue(e.target.value)}\n            onKeyDown={handleKeyDown}\n            className=\"flex-1 min-w-0 bg-background border border-border rounded px-1.5 py-0.5 text-xs font-mono focus:outline-none focus:ring-2 focus:ring-ring\"\n          />\n          <button\n            type=\"button\"\n            onClick={handleSaveEdit}\n            className=\"size-5 flex items-center justify-center rounded hover:bg-green-100 dark:hover:bg-green-950 text-green-600 dark:text-green-400\"\n          >\n            <Check className=\"size-3\" />\n          </button>\n          <button\n            type=\"button\"\n            onClick={handleCancelEdit}\n            className=\"size-5 flex items-center justify-center rounded hover:bg-red-100 dark:hover:bg-red-950 text-red-600 dark:text-red-400\"\n          >\n            <X className=\"size-3\" />\n          </button>\n        </div>\n      ) : (\n        <>\n          <span className={cn(\"text-xs font-mono flex-1\", typeColor)}>\n            {displayValue}\n          </span>\n          {editable && (\n            <button\n              type=\"button\"\n              onClick={handleStartEdit}\n              className=\"size-5 flex items-center justify-center rounded opacity-0 group-hover:opacity-100 hover:bg-muted text-muted-foreground transition-opacity\"\n            >\n              <Pencil className=\"size-3\" />\n            </button>\n          )}\n        </>\n      )}\n    </div>\n  );\n}\n\ninterface AiAgentContextGroupProps {\n  title: string;\n  defaultOpen?: boolean;\n  children?: React.ReactNode;\n  className?: string;\n}\n\nfunction AiAgentContextGroup({\n  title,\n  defaultOpen = true,\n  children,\n  className,\n}: AiAgentContextGroupProps) {\n  const [isOpen, setIsOpen] = React.useState(defaultOpen);\n\n  return (\n    <CollapsiblePrimitive.Root\n      data-slot=\"ai-agent-context-group\"\n      open={isOpen}\n      onOpenChange={setIsOpen}\n      className={cn(\"\", className)}\n    >\n      <CollapsiblePrimitive.Trigger className=\"flex w-full items-center gap-2 py-2 text-xs font-medium text-muted-foreground uppercase tracking-wider hover:text-foreground transition-colors\">\n        <ChevronDown\n          className={cn(\"size-3 transition-transform\", !isOpen && \"-rotate-90\")}\n        />\n        {title}\n      </CollapsiblePrimitive.Trigger>\n      <CollapsiblePrimitive.Content className=\"data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down overflow-hidden\">\n        {children}\n      </CollapsiblePrimitive.Content>\n    </CollapsiblePrimitive.Root>\n  );\n}\n\ninterface AiAgentContextEmptyProps {\n  children?: React.ReactNode;\n  className?: string;\n}\n\nfunction AiAgentContextEmpty({\n  children,\n  className,\n}: AiAgentContextEmptyProps) {\n  return (\n    <div\n      data-slot=\"ai-agent-context-empty\"\n      className={cn(\n        \"flex flex-col items-center justify-center py-8 text-center\",\n        className,\n      )}\n    >\n      <div className=\"flex size-12 items-center justify-center rounded-full bg-muted mb-3\">\n        <Database className=\"size-6 text-muted-foreground\" />\n      </div>\n      <p className=\"text-sm text-muted-foreground\">\n        {children || \"No context data available\"}\n      </p>\n    </div>\n  );\n}\n\nexport {\n  AiAgentContext,\n  AiAgentContextHeader,\n  AiAgentContextContent,\n  AiAgentContextEntry,\n  AiAgentContextGroup,\n  AiAgentContextEmpty,\n};\nexport type { AiAgentContextProps, ValueType };\n",
      "type": "registry:component"
    }
  ],
  "docs": "Compound component for context visualization. Sub-components: AiAgentContext, AiAgentContextHeader, AiAgentContextContent, AiAgentContextEntry, AiAgentContextGroup, AiAgentContextEmpty. Supports editing primitive values.",
  "categories": [
    "ai"
  ],
  "type": "registry:ui"
}