{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "permission-card",
  "type": "registry:ui",
  "title": "Permission Card",
  "description": "Inline approval — allow once / allow for session / deny, settling into a receipt.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "https://seamui.dev/r/utils.json",
    "https://seamui.dev/r/button.json",
    "https://seamui.dev/r/badge.json"
  ],
  "files": [
    {
      "path": "registry/seam/ui/permission-card.tsx",
      "content": "\"use client\"\n\nimport type * as React from \"react\"\nimport { Check, ShieldCheck, ShieldQuestion, X, Zap } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Badge } from \"./badge\"\nimport { Button } from \"./button\"\n\n// The permission prompt rendered in the thread. Pending = a raised key\n// demanding attention; once decided the card settles into a quiet receipt so\n// the thread keeps an audit trail instead of a dead prompt.\n\ntype PermissionDecision = \"allowed\" | \"allowed-session\" | \"denied\" | \"auto\"\n\nconst RECEIPT: Record<\n  PermissionDecision,\n  { icon: React.ReactNode; label: string }\n> = {\n  allowed: { icon: <Check className=\"size-3.5\" />, label: \"Allowed once\" },\n  \"allowed-session\": {\n    icon: <ShieldCheck className=\"size-3.5\" />,\n    label: \"Allowed for this session\",\n  },\n  denied: { icon: <X className=\"size-3.5\" />, label: \"Denied\" },\n  auto: { icon: <Zap className=\"size-3.5\" />, label: \"Auto-approved\" },\n}\n\nfunction PermissionCard({\n  title,\n  description,\n  step,\n  decision,\n  onAllow,\n  onDeny,\n  className,\n  children,\n  ...props\n}: React.ComponentProps<\"div\"> & {\n  title: React.ReactNode\n  description?: React.ReactNode\n  /** Multi-question runs: renders a \"2 of 3\" chip. */\n  step?: { current: number; total: number }\n  /** Set once resolved — replaces the actions with a settled receipt. */\n  decision?: PermissionDecision\n  onAllow?: (scope: \"once\" | \"session\") => void\n  onDeny?: () => void\n}) {\n  const pending = decision === undefined\n\n  return (\n    <div\n      data-slot=\"permission-card\"\n      data-decision={decision}\n      role=\"group\"\n      aria-label=\"Permission request\"\n      className={cn(\n        \"bg-card text-card-foreground w-full max-w-md space-y-3 rounded-xl squircle border border-border/60 p-4 shadow-resting\",\n        className\n      )}\n      {...props}\n    >\n      <div className=\"flex items-start justify-between gap-2\">\n        <div className=\"flex min-w-0 items-start gap-2.5\">\n          <ShieldQuestion\n            aria-hidden\n            className=\"text-muted-foreground mt-0.5 size-4 shrink-0\"\n          />\n          <div className=\"min-w-0\">\n            <div className=\"text-sm font-medium\">{title}</div>\n            {description ? (\n              <div className=\"text-muted-foreground text-xs\">{description}</div>\n            ) : null}\n          </div>\n        </div>\n        {step ? (\n          <Badge\n            variant=\"muted\"\n            aria-label={`Question ${step.current} of ${step.total}`}\n            className=\"shrink-0 tabular-nums\"\n          >\n            {step.current} of {step.total}\n          </Badge>\n        ) : null}\n      </div>\n\n      {children}\n\n      {/* Announced politely so the resolution is heard without interrupting. */}\n      <div aria-live=\"polite\">\n        {pending ? (\n          <div\n            data-slot=\"permission-card-actions\"\n            className=\"flex flex-wrap items-center gap-2\"\n          >\n            <Button size=\"sm\" onClick={() => onAllow?.(\"once\")}>\n              Allow once\n            </Button>\n            <Button\n              size=\"sm\"\n              variant=\"secondary\"\n              onClick={() => onAllow?.(\"session\")}\n            >\n              Allow for session\n            </Button>\n            <Button\n              size=\"sm\"\n              variant=\"ghost\"\n              className=\"text-destructive hover:text-destructive\"\n              onClick={() => onDeny?.()}\n            >\n              Deny\n            </Button>\n          </div>\n        ) : (\n          <div\n            data-slot=\"permission-card-receipt\"\n            className={cn(\n              \"bg-muted text-muted-foreground flex w-fit items-center gap-1.5 rounded-md squircle border border-border/60 px-2.5 py-1 text-xs font-medium shadow-well\",\n              decision === \"denied\" && \"text-destructive\"\n            )}\n          >\n            {RECEIPT[decision].icon}\n            {RECEIPT[decision].label}\n          </div>\n        )}\n      </div>\n    </div>\n  )\n}\n\n// A one-line command carved into the card. For multi-line payloads compose\n// code-block instead.\nfunction PermissionCardCommand({\n  className,\n  ...props\n}: React.ComponentProps<\"code\">) {\n  return (\n    <code\n      data-slot=\"permission-card-command\"\n      className={cn(\n        \"bg-muted block overflow-x-auto rounded-md squircle border border-border/60 px-2.5 py-1.5 font-mono text-xs whitespace-nowrap shadow-well\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nexport { PermissionCard, PermissionCardCommand, type PermissionDecision }\n",
      "type": "registry:ui",
      "target": "components/ui/permission-card.tsx"
    }
  ]
}