# Warrant — AI Chat UI Component for React

> Agent approval request card — springs up with a command or unified-diff preview and a low/medium/high risk badge, then collapses on a height spring into a one-line receipt. High-risk actions require press-and-hold (pointer or Space/Enter): an accent fill sweeps across Approve and drains back on release. Non-modal group semantics, Escape to deny, live-region outcome, stepped-opacity hold under reduced motion.

- Page: https://crucibleui.com/components/ai/warrant
- Category: [ai](https://crucibleui.com/components/ai)
- Tier: pro (one-time purchase, see https://crucibleui.com/pricing)
- Engine: motion
- npm dependencies: motion

## Install

```bash
npx shadcn@latest add @crucible/warrant
```

Pro install requires adding the token-bearing `@crucible` registry to components.json (issued at https://crucibleui.com/account after purchase).

Import from `@/components/crucible/warrant`.

## Props (`WarrantProps`)

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `title` | `string` | — | The action the agent wants to take, e.g. "run_tests". Also appears in the receipt. |
| `description?` | `string` | — | Optional sentence explaining why the agent wants to do this. |
| `preview?` | `WarrantPreview` | — | Command or diff preview of the action. Omit for a text-only request. |
| `risk?` | `WarrantRisk` | `"low"` | Risk badge level; `high` requires press-and-hold to approve. |
| `holdMs?` | `number` | `1200` | How long Approve must be held when `risk="high"`, in ms. |
| `onApprove?` | `(always: boolean) => void` | — | Fires when the request is approved, with the state of "Always allow this action". |
| `onDeny?` | `() => void` | — | Fires when the request is denied (Deny button, or Escape while focus is inside). |
| `status?` | `WarrantStatus` | — | Controlled status. Leave undefined to let the card track its own status. |
| `agentName?` | `string` | `"Assistant"` | Name of the requester in the header line. |
| `accentColor?` | `string` | `"#a78bfa" (ion violet)` | Accent for the hold fill, Approve button, and focus rings. |

Also accepts: `Omit<React.ComponentPropsWithoutRef<"div">, "title" | "children">`.

## Usage example

```tsx
"use client";

import * as React from "react";

import { Warrant } from "@/components/crucible/warrant";

export default function WarrantDemo() {
  const [round, setRound] = React.useState(0);

  return (
    <div className="relative isolate flex h-full min-h-[360px] w-full items-center justify-center overflow-hidden rounded-xl bg-neutral-950 px-4 py-10">
      <div
        aria-hidden
        className="pointer-events-none absolute inset-0 bg-[radial-gradient(ellipse_at_center,rgba(167,139,250,0.05),transparent_70%)]"
      />
      <div className="relative z-10 flex w-full max-w-md flex-col items-center gap-4">
        <div key={round} className="flex w-full flex-col gap-3">
          <Warrant
            title="run_tests"
            description="Run the unit suite to check the refactor."
            risk="low"
            preview={{ kind: "command", command: "npm test -- --run src/utils" }}
          />
          <Warrant
            title="apply_patch"
            description="Rewrite the session timeout and drop the legacy fallback."
            risk="high"
            preview={{
              kind: "diff",
              file: "src/config/session.ts",
              startLine: 12,
              lines: [
                { type: "ctx", text: "export const session = {" },
                { type: "del", text: "  timeoutMs: 30 * 60 * 1000," },
                { type: "del", text: "  legacyFallback: true," },
                { type: "add", text: "  timeoutMs: 15 * 60 * 1000," },
                { type: "ctx", text: "};" },
              ],
            }}
          />
        </div>
        <div className="flex items-center gap-4">
          <p className="text-xs text-neutral-500">High-risk actions need a press-and-hold.</p>
          <button
            type="button"
            onClick={() => setRound((r) => r + 1)}
            className="rounded-md border border-white/10 px-2.5 py-1 text-xs text-neutral-300 transition-colors hover:bg-white/5 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#a78bfa]"
          >
            Reset
          </button>
        </div>
      </div>
    </div>
  );
}
```

## Accessibility & performance

Honors `prefers-reduced-motion` with a designed static frame or fade (never a blank box) and passes `className` through.
