{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "confirmation",
  "title": "Confirmation",
  "author": "Jamie Thompson <jamie@jamiethompson.design> (https://jamiethompson.design)",
  "description": "Human-in-the-loop approval prompt for AI tool calls, rendered as an Alert. Reads the tool part's state and approval object and shows exactly one of: the pending request with Approve/Reject action buttons (approval-requested), the accepted outcome (approval-responded, output-available or output-error with approved: true), or the rejected outcome (approval-responded or output-denied with approved: false); renders nothing while input is streaming, before an approval exists, or when a responded part carries no decision. Use it with useChat addToolApprovalResponse for tools that require user consent.",
  "dependencies": [
    "ai@^7",
    "cn"
  ],
  "registryDependencies": [
    "alert",
    "button"
  ],
  "files": [
    {
      "path": "registry/ai/confirmation.tsx",
      "content": "// Derived from Vercel AI Elements confirmation.tsx (Apache-2.0, Copyright 2023 Vercel, Inc.).\n// Modified for uifiles: ported from Radix UI to Base UI; dependencies point at @uifiles.\n\"use client\"\n\nimport type { ToolUIPart } from \"ai\"\nimport { cn } from \"cn\"\nimport type { ComponentProps, ReactNode } from \"react\"\nimport { createContext, useContext, useMemo } from \"react\"\nimport { Alert, AlertDescription } from \"@/components/ui/alert\"\nimport { Button } from \"@/components/ui/button\"\n\ntype ToolUIPartApproval =\n  | {\n      id: string\n      approved?: never\n      reason?: never\n    }\n  | {\n      id: string\n      approved: boolean\n      reason?: string | undefined\n    }\n  | {\n      id: string\n      approved: true\n      reason?: string | undefined\n    }\n  | {\n      id: string\n      approved: false\n      reason?: string | undefined\n    }\n  | undefined\n\ninterface ConfirmationContextValue {\n  approval: ToolUIPartApproval\n  state: ToolUIPart[\"state\"]\n}\n\nconst ConfirmationContext = createContext<ConfirmationContextValue | null>(null)\n\nconst useConfirmation = () => {\n  const context = useContext(ConfirmationContext)\n\n  if (!context) {\n    throw new Error(\"Confirmation components must be used within Confirmation\")\n  }\n\n  return context\n}\n\n// States in which the user has already answered the request. `output-error`\n// belongs here: ai@7 keeps `approval.approved === true` on a tool that was\n// approved and then failed, and the failure itself is ToolOutput's to show.\nconst respondedStates: ReadonlySet<ToolUIPart[\"state\"]> = new Set([\n  \"approval-responded\",\n  \"output-available\",\n  \"output-denied\",\n  \"output-error\",\n])\n\nexport type ConfirmationProps = ComponentProps<typeof Alert> & {\n  approval?: ToolUIPartApproval\n  state: ToolUIPart[\"state\"]\n}\n\nexport const Confirmation = ({\n  className,\n  approval,\n  state,\n  ...props\n}: ConfirmationProps) => {\n  const contextValue = useMemo(() => ({ approval, state }), [approval, state])\n\n  const showsRequest = state === \"approval-requested\"\n  const showsOutcome =\n    respondedStates.has(state) && typeof approval?.approved === \"boolean\"\n\n  // Without a request or a decision none of the parts below has anything to\n  // show, and an empty role=\"alert\" would still be announced.\n  if (!approval || !(showsRequest || showsOutcome)) {\n    return null\n  }\n\n  return (\n    <ConfirmationContext.Provider value={contextValue}>\n      <Alert className={cn(\"flex flex-col gap-2\", className)} {...props} />\n    </ConfirmationContext.Provider>\n  )\n}\n\nexport type ConfirmationTitleProps = ComponentProps<typeof AlertDescription>\n\nexport const ConfirmationTitle = ({\n  className,\n  ...props\n}: ConfirmationTitleProps) => (\n  <AlertDescription className={cn(\"inline\", className)} {...props} />\n)\n\nexport interface ConfirmationRequestProps {\n  children?: ReactNode\n}\n\nexport const ConfirmationRequest = ({ children }: ConfirmationRequestProps) => {\n  const { state } = useConfirmation()\n\n  if (state !== \"approval-requested\") {\n    return null\n  }\n\n  return children\n}\n\nexport interface ConfirmationAcceptedProps {\n  children?: ReactNode\n}\n\nexport const ConfirmationAccepted = ({\n  children,\n}: ConfirmationAcceptedProps) => {\n  const { approval, state } = useConfirmation()\n\n  if (approval?.approved !== true || !respondedStates.has(state)) {\n    return null\n  }\n\n  return children\n}\n\nexport interface ConfirmationRejectedProps {\n  children?: ReactNode\n  className?: string | undefined\n}\n\nexport const ConfirmationRejected = ({\n  children,\n  className,\n}: ConfirmationRejectedProps) => {\n  const { approval, state } = useConfirmation()\n\n  if (approval?.approved !== false || !respondedStates.has(state)) {\n    return null\n  }\n\n  // uifiles: upstream returns the children bare, so both outcomes read in\n  // the same foreground color; the destructive token tells them apart at a\n  // glance (the wording still says which, so color is not the only cue).\n  return <span className={cn(\"text-destructive\", className)}>{children}</span>\n}\n\nexport type ConfirmationActionsProps = ComponentProps<\"div\">\n\nexport const ConfirmationActions = ({\n  className,\n  ...props\n}: ConfirmationActionsProps) => {\n  const { state } = useConfirmation()\n\n  if (state !== \"approval-requested\") {\n    return null\n  }\n\n  return (\n    <div\n      className={cn(\"flex items-center justify-end gap-2 self-end\", className)}\n      {...props}\n    />\n  )\n}\n\nexport type ConfirmationActionProps = ComponentProps<typeof Button>\n\nexport const ConfirmationAction = ({\n  className,\n  ...props\n}: ConfirmationActionProps) => (\n  <Button\n    className={cn(\"h-8 px-3 text-sm\", className)}\n    type=\"button\"\n    {...props}\n  />\n)\n",
      "type": "registry:component",
      "target": "components/ai/confirmation.tsx"
    }
  ],
  "docs": "Derived from Vercel AI Elements (Apache-2.0, Copyright 2023 Vercel, Inc.); ported to Base UI and maintained by uifiles. Built for the Base UI styles (base-nova): it composes the Base UI wrappers from components/ui and relies on their render prop and event signatures, so it does not work on Radix or React Aria styles. No Radix primitives upstream; only imports changed. ConfirmationAction is the Base UI Button: use `render` instead of `asChild` (add nativeButton={false} when the rendered element is not a <button>), and its className is merged with the default h-8 px-3 text-sm through cn instead of replacing it. ConfirmationRejected wraps its children in a <span className=\"text-destructive\"> (upstream returned them bare in the foreground color) so the rejected outcome reads differently from the accepted one; pass className to adjust, and keep the children phrasing content. ConfirmationAccepted also shows for output-error, because ai@7 keeps approval.approved === true on an approved tool whose execution then failed (the error itself is ToolOutput's to show). Confirmation renders nothing, rather than an empty role=\"alert\" box, when a responded state carries no approved boolean.",
  "categories": [
    "ai",
    "chat",
    "agent"
  ],
  "type": "registry:component"
}