An inline, non-modal AI-consent request that stays in scrollback after it resolves, switching between its decision and resolved parts as the host settles its status.
1
2
3'use client';
import { useRef, useState, type ReactNode } from 'react';Installation#
pnpm dlx shadcn@latest add https://ui.zeroxsolutions.com/r/permission-card.jsonUsage#
import {
PermissionCard,
PermissionCardActions,
PermissionCardResolved,
PermissionCardStatus,
PermissionCardTitle,
} from '@/components/feedback/permission-card';<PermissionCard status={status}>
<CardHeader>
<PermissionCardTitle>Run deploy.sh</PermissionCardTitle>
<CardDescription>Deploy the web app to production</CardDescription>
<PermissionCardStatus>{statusWord}</PermissionCardStatus>
</CardHeader>
<PermissionCardActions>
<Button variant="ghost" onClick={deny}>
Deny
</Button>
<Button onClick={allow}>Allow once</Button>
</PermissionCardActions>
<PermissionCardResolved>
<CardDescription>Allowed once - 2:14pm</CardDescription>
</PermissionCardResolved>
</PermissionCard>The decision row is asymmetric on purpose: pair a plain Deny with a graduated Allow, such as a ButtonGroup
holding a scope menu. Use it for an inline, non-modal consent request inside a chat transcript that needs a
decision; for a tool call with no decision to make, use
Tool Call Card instead.
Composition#
PermissionCard
|-- CardHeader
| |-- PermissionCardTitle
| |-- CardDescription
| `-- PermissionCardStatus
|-- PermissionCardActions
| |-- Button
| `-- ButtonGroup
| |-- Button
| `-- DropdownMenu
`-- PermissionCardResolved
`-- CardDescriptionAPI reference#
PermissionCard#
Renders shadcn's Card and takes every prop it takes,
plus:
| Prop | Type | Default |
|---|---|---|
status | 'pending' | 'approved' | 'denied' | required |
It defaults size to sm. It sets data-slot="permission-card" and data-status to its status.
PermissionCardTitle#
Renders shadcn's CardTitle and takes every prop it
takes. What the assistant asks to do, on one truncated line. It sets data-slot="permission-card-title".
PermissionCardStatus#
Renders shadcn's Badge and takes every prop it takes,
inside a CardAction that lands it in the header's action corner. Defaults variant to outline; pass
destructive for a denied request. Its icon - a circle for pending, a check for approved, a cross for
denied - follows the root's status, and children are the status word. It sets
data-slot="permission-card-status".
PermissionCardActions#
Renders shadcn's CardFooter and takes every prop it
takes. The decision row, right-aligned, shown only while status is pending. It sets its own
data-slot="permission-card-actions"; PermissionCard reads that slot (and permission-card-resolved) to
drop a footer's bottom padding, in place of CardFooter's own card-footer.
PermissionCardResolved#
Renders shadcn's CardFooter and takes every prop it
takes. The persisted outcome, shown once status is approved or denied. Like PermissionCardActions, it
sets its own data-slot (permission-card-resolved) in place of CardFooter's own card-footer.