{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "guardrails",
  "title": "AI Guardrails",
  "description": "Display content moderation and safety check results with blocked/modified/approved states, before/after comparison, and override actions for AI safety workflows.",
  "dependencies": [
    "@radix-ui/react-collapsible",
    "lucide-react"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/blocks/ai/ai-guardrails/components/elements/ai-guardrails.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport * as CollapsiblePrimitive from \"@radix-ui/react-collapsible\";\nimport {\n  ArrowRight,\n  ChevronDown,\n  Loader2,\n  RotateCcw,\n  Shield,\n  ShieldAlert,\n  ShieldCheck,\n  ShieldX,\n} from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\n\ntype GuardrailStatus = \"blocked\" | \"modified\" | \"approved\" | \"pending\";\ntype GuardrailType = \"input\" | \"output\";\n\ninterface GuardrailCheck {\n  id: string;\n  type: GuardrailType;\n  status: GuardrailStatus;\n  category: string;\n  reason?: string;\n  originalContent?: string;\n  modifiedContent?: string;\n  confidence?: number;\n}\n\ninterface AiGuardrailsContextValue {\n  checks: GuardrailCheck[];\n  onOverride?: (checkId: string) => void;\n}\n\nconst AiGuardrailsContext =\n  React.createContext<AiGuardrailsContextValue | null>(null);\n\nfunction useGuardrailsContext() {\n  const context = React.useContext(AiGuardrailsContext);\n  if (!context) {\n    throw new Error(\n      \"AiGuardrails components must be used within <AiGuardrails>\",\n    );\n  }\n  return context;\n}\n\ninterface AiGuardrailsProps {\n  checks: GuardrailCheck[];\n  onOverride?: (checkId: string) => void;\n  children?: React.ReactNode;\n  className?: string;\n}\n\nfunction AiGuardrails({\n  checks,\n  onOverride,\n  children,\n  className,\n}: AiGuardrailsProps) {\n  const contextValue = React.useMemo(\n    () => ({ checks, onOverride }),\n    [checks, onOverride],\n  );\n\n  return (\n    <AiGuardrailsContext.Provider value={contextValue}>\n      <div\n        data-slot=\"ai-guardrails\"\n        className={cn(\n          \"rounded-lg border border-border bg-card text-card-foreground\",\n          className,\n        )}\n      >\n        {children || (\n          <>\n            <AiGuardrailsHeader />\n            <AiGuardrailsContent />\n          </>\n        )}\n      </div>\n    </AiGuardrailsContext.Provider>\n  );\n}\n\ninterface AiGuardrailsHeaderProps {\n  children?: React.ReactNode;\n  className?: string;\n}\n\nfunction AiGuardrailsHeader({ children, className }: AiGuardrailsHeaderProps) {\n  const { checks } = useGuardrailsContext();\n\n  const summary = React.useMemo(() => {\n    const blocked = checks.filter((c) => c.status === \"blocked\").length;\n    const modified = checks.filter((c) => c.status === \"modified\").length;\n    const approved = checks.filter((c) => c.status === \"approved\").length;\n    const pending = checks.filter((c) => c.status === \"pending\").length;\n    return { blocked, modified, approved, pending };\n  }, [checks]);\n\n  const overallStatus = React.useMemo(() => {\n    if (summary.blocked > 0) return \"blocked\";\n    if (summary.pending > 0) return \"pending\";\n    if (summary.modified > 0) return \"modified\";\n    return \"approved\";\n  }, [summary]);\n\n  const statusConfig = React.useMemo(() => {\n    const configs: Record<\n      GuardrailStatus,\n      { icon: React.ReactNode; className: string; bgClassName: string }\n    > = {\n      blocked: {\n        icon: <ShieldX className=\"size-4\" />,\n        className: \"text-red-700 dark:text-red-400\",\n        bgClassName: \"bg-red-100 dark:bg-red-950\",\n      },\n      modified: {\n        icon: <ShieldAlert className=\"size-4\" />,\n        className: \"text-amber-700 dark:text-amber-400\",\n        bgClassName: \"bg-amber-100 dark:bg-amber-950\",\n      },\n      approved: {\n        icon: <ShieldCheck className=\"size-4\" />,\n        className: \"text-green-700 dark:text-green-400\",\n        bgClassName: \"bg-green-100 dark:bg-green-950\",\n      },\n      pending: {\n        icon: <Loader2 className=\"size-4 animate-spin\" />,\n        className: \"text-muted-foreground\",\n        bgClassName: \"bg-muted\",\n      },\n    };\n    return configs[overallStatus];\n  }, [overallStatus]);\n\n  return (\n    <div\n      data-slot=\"ai-guardrails-header\"\n      className={cn(\n        \"flex items-center justify-between border-b border-border px-4 py-3\",\n        className,\n      )}\n    >\n      <div className=\"flex items-center gap-2\">\n        <div\n          className={cn(\n            \"flex size-8 shrink-0 items-center justify-center rounded-md\",\n            statusConfig.bgClassName,\n            statusConfig.className,\n          )}\n        >\n          {statusConfig.icon}\n        </div>\n        <div>\n          <h3 className=\"font-semibold text-sm\">\n            {children || \"Content Guardrails\"}\n          </h3>\n          <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n            {summary.blocked > 0 && (\n              <span className=\"text-red-600 dark:text-red-400\">\n                {summary.blocked} blocked\n              </span>\n            )}\n            {summary.modified > 0 && (\n              <span className=\"text-amber-600 dark:text-amber-400\">\n                {summary.modified} modified\n              </span>\n            )}\n            {summary.approved > 0 && (\n              <span className=\"text-green-600 dark:text-green-400\">\n                {summary.approved} approved\n              </span>\n            )}\n            {summary.pending > 0 && <span>{summary.pending} pending</span>}\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n\ninterface AiGuardrailsContentProps {\n  children?: React.ReactNode;\n  className?: string;\n}\n\nfunction AiGuardrailsContent({\n  children,\n  className,\n}: AiGuardrailsContentProps) {\n  const { checks } = useGuardrailsContext();\n\n  return (\n    <div\n      data-slot=\"ai-guardrails-content\"\n      className={cn(\"p-4 space-y-2\", className)}\n    >\n      {children ||\n        checks.map((check) => (\n          <AiGuardrailCheck key={check.id} check={check} />\n        ))}\n    </div>\n  );\n}\n\ninterface AiGuardrailCheckProps {\n  check: GuardrailCheck;\n  className?: string;\n}\n\nfunction AiGuardrailCheck({ check, className }: AiGuardrailCheckProps) {\n  const { onOverride } = useGuardrailsContext();\n  const [isOpen, setIsOpen] = React.useState(check.status === \"blocked\");\n\n  const statusConfig = React.useMemo(() => {\n    const configs: Record<\n      GuardrailStatus,\n      {\n        icon: React.ReactNode;\n        label: string;\n        className: string;\n        borderClassName: string;\n        bgClassName: string;\n      }\n    > = {\n      blocked: {\n        icon: <ShieldX className=\"size-3.5\" />,\n        label: \"Blocked\",\n        className: \"text-red-700 dark:text-red-400\",\n        borderClassName: \"border-red-200 dark:border-red-900\",\n        bgClassName: \"bg-red-50 dark:bg-red-950/30\",\n      },\n      modified: {\n        icon: <ShieldAlert className=\"size-3.5\" />,\n        label: \"Modified\",\n        className: \"text-amber-700 dark:text-amber-400\",\n        borderClassName: \"border-amber-200 dark:border-amber-900\",\n        bgClassName: \"bg-amber-50 dark:bg-amber-950/30\",\n      },\n      approved: {\n        icon: <ShieldCheck className=\"size-3.5\" />,\n        label: \"Approved\",\n        className: \"text-green-700 dark:text-green-400\",\n        borderClassName: \"border-green-200 dark:border-green-900\",\n        bgClassName: \"bg-green-50 dark:bg-green-950/30\",\n      },\n      pending: {\n        icon: <Loader2 className=\"size-3.5 animate-spin\" />,\n        label: \"Checking...\",\n        className: \"text-muted-foreground\",\n        borderClassName: \"border-border\",\n        bgClassName: \"bg-muted/30\",\n      },\n    };\n    return configs[check.status];\n  }, [check.status]);\n\n  const hasDetails =\n    check.reason ||\n    check.originalContent ||\n    check.modifiedContent ||\n    check.confidence !== undefined;\n\n  return (\n    <CollapsiblePrimitive.Root\n      data-slot=\"ai-guardrail-check\"\n      data-status={check.status}\n      open={isOpen}\n      onOpenChange={setIsOpen}\n      className={cn(\n        \"rounded-lg border overflow-hidden transition-colors\",\n        statusConfig.borderClassName,\n        statusConfig.bgClassName,\n        className,\n      )}\n    >\n      <CollapsiblePrimitive.Trigger\n        className={cn(\n          \"flex w-full items-center gap-3 px-3 py-2.5 text-sm transition-colors hover:bg-black/5 dark:hover:bg-white/5\",\n          !hasDetails && \"cursor-default\",\n        )}\n        disabled={!hasDetails}\n      >\n        <div\n          className={cn(\n            \"flex size-6 shrink-0 items-center justify-center rounded-md\",\n            statusConfig.bgClassName,\n            statusConfig.className,\n          )}\n        >\n          {statusConfig.icon}\n        </div>\n        <div className=\"flex flex-1 items-center gap-2 min-w-0\">\n          <span className=\"font-medium text-sm capitalize\">\n            {check.category.replace(/[-_]/g, \" \")}\n          </span>\n          <span\n            className={cn(\n              \"inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium\",\n              statusConfig.bgClassName,\n              statusConfig.className,\n            )}\n          >\n            {statusConfig.label}\n          </span>\n          <span className=\"text-xs text-muted-foreground capitalize\">\n            ({check.type})\n          </span>\n        </div>\n        {check.confidence !== undefined && (\n          <span className=\"text-xs text-muted-foreground font-mono\">\n            {Math.round(check.confidence * 100)}%\n          </span>\n        )}\n        {hasDetails && (\n          <ChevronDown\n            className={cn(\n              \"size-4 shrink-0 text-muted-foreground transition-transform duration-200\",\n              isOpen && \"rotate-180\",\n            )}\n          />\n        )}\n      </CollapsiblePrimitive.Trigger>\n\n      {hasDetails && (\n        <CollapsiblePrimitive.Content className=\"border-t border-inherit data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down\">\n          <div className=\"px-3 py-2.5 space-y-3\">\n            {check.reason && (\n              <p className=\"text-sm text-muted-foreground\">{check.reason}</p>\n            )}\n\n            {(check.originalContent || check.modifiedContent) && (\n              <AiGuardrailDiff\n                original={check.originalContent}\n                modified={check.modifiedContent}\n              />\n            )}\n\n            {onOverride && check.status === \"blocked\" && (\n              <div className=\"flex justify-end\">\n                <button\n                  type=\"button\"\n                  onClick={() => onOverride(check.id)}\n                  className=\"inline-flex items-center gap-1.5 rounded-md bg-background border border-border px-2.5 py-1.5 text-xs font-medium transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n                >\n                  <RotateCcw className=\"size-3\" />\n                  Override\n                </button>\n              </div>\n            )}\n          </div>\n        </CollapsiblePrimitive.Content>\n      )}\n    </CollapsiblePrimitive.Root>\n  );\n}\n\ninterface AiGuardrailDiffProps {\n  original?: string;\n  modified?: string;\n  className?: string;\n}\n\nfunction AiGuardrailDiff({\n  original,\n  modified,\n  className,\n}: AiGuardrailDiffProps) {\n  if (!original && !modified) return null;\n\n  return (\n    <div data-slot=\"ai-guardrail-diff\" className={cn(\"space-y-2\", className)}>\n      {original && modified ? (\n        <div className=\"flex items-start gap-2\">\n          <div className=\"flex-1 min-w-0\">\n            <span className=\"text-xs font-medium text-muted-foreground uppercase tracking-wider block mb-1\">\n              Original\n            </span>\n            <div className=\"rounded-md bg-red-100/50 dark:bg-red-950/50 p-2 text-xs font-mono text-foreground line-through opacity-70\">\n              {original}\n            </div>\n          </div>\n          <ArrowRight className=\"size-4 text-muted-foreground shrink-0 mt-6\" />\n          <div className=\"flex-1 min-w-0\">\n            <span className=\"text-xs font-medium text-muted-foreground uppercase tracking-wider block mb-1\">\n              Modified\n            </span>\n            <div className=\"rounded-md bg-green-100/50 dark:bg-green-950/50 p-2 text-xs font-mono text-foreground\">\n              {modified}\n            </div>\n          </div>\n        </div>\n      ) : (\n        <div>\n          <span className=\"text-xs font-medium text-muted-foreground uppercase tracking-wider block mb-1\">\n            {original ? \"Blocked Content\" : \"Modified Content\"}\n          </span>\n          <div\n            className={cn(\n              \"rounded-md p-2 text-xs font-mono text-foreground\",\n              original\n                ? \"bg-red-100/50 dark:bg-red-950/50 line-through opacity-70\"\n                : \"bg-green-100/50 dark:bg-green-950/50\",\n            )}\n          >\n            {original || modified}\n          </div>\n        </div>\n      )}\n    </div>\n  );\n}\n\ninterface AiGuardrailsEmptyProps {\n  children?: React.ReactNode;\n  className?: string;\n}\n\nfunction AiGuardrailsEmpty({ children, className }: AiGuardrailsEmptyProps) {\n  return (\n    <div\n      data-slot=\"ai-guardrails-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-green-100 dark:bg-green-950 mb-3\">\n        <Shield className=\"size-6 text-green-600 dark:text-green-400\" />\n      </div>\n      <p className=\"text-sm text-muted-foreground\">\n        {children || \"All content checks passed\"}\n      </p>\n    </div>\n  );\n}\n\nexport {\n  AiGuardrails,\n  AiGuardrailsHeader,\n  AiGuardrailsContent,\n  AiGuardrailCheck,\n  AiGuardrailDiff,\n  AiGuardrailsEmpty,\n};\nexport type {\n  AiGuardrailsProps,\n  GuardrailCheck,\n  GuardrailStatus,\n  GuardrailType,\n};\n",
      "type": "registry:component"
    }
  ],
  "docs": "Compound component for guardrail displays. Sub-components: AiGuardrails, AiGuardrailsHeader, AiGuardrailsContent, AiGuardrailCheck, AiGuardrailDiff, AiGuardrailsEmpty. Check statuses: blocked, modified, approved, pending.",
  "categories": [
    "ai"
  ],
  "type": "registry:ui"
}