Ballmac UI home

Approval Card

A human-in-the-loop confirmation for agent actions: what will happen, risk level in words, the exact command, Approve, Deny and Always allow, a timeout countdown and a recorded decision.

Run a shell command

The assistant wants to remove build output before compiling again.

Medium risk. Changes files or settings you can undo
Directory
~/projects/storefront
Runs as
your user
rm -rf .next dist && pnpm build
Denies itself in 45s

Installation

$ pnpm dlx shadcn@latest add @ballmac/approval-card

Usage

import { ApprovalCard } from "@/components/ballmac/approval-card"

The full example is in the Code tab above.

Examples

Three risk levels

Read a file

Needed to answer your question about the config.

Low risk. Reads data or changes nothing
Path
./config/app.json

Edit 3 files

Applies the change you asked for in the settings module.

Medium risk. Changes files or settings you can undo
Files
settings.ts, form.tsx, settings.test.ts

Send customer data to an external API

This leaves your network and cannot be recalled.

High risk. Hard to undo or sends data out
Host
api.analytics.example.com
Records
1,204 customers
POST /v1/import content-type: application/json { "customers": [ … 1204 items ] }

API reference

PropTypeDefault
title*

What the agent wants to do, as a short sentence, for example "Run a shell command".

string—
description

Why the agent wants to do it.

string—
risk

How much damage a mistake could do. Sets the accent color, icon and wording.

ApprovalRisk"medium"
details

Label/value rows under the description.

ApprovalDetail[]—
preview

The exact thing that will run: a command, a diff or a request body.

React.ReactNode—
previewLabel

Accessible name of the scrollable preview.

string—
status

Current status (controlled). Without it the card keeps its own status after a button is pressed.

ApprovalStatus—
onApprove

Called when Approve is pressed.

() => void—
onDeny

Called when Deny is pressed.

() => void—
onAlwaysAllow

Adds a third button that approves now and asks not to be asked again.

() => void—
alwaysAllowLabel

Text of the always-allow button.

string—
expiresIn

Seconds until the request is denied on its own. The card counts down and calls `onExpire`.

number—
onExpire

Called when the countdown reaches zero.

() => void—
resolvedNote

Text after the decision, such as "by you at 10:42".

string—
approveLabel

Approve button text.

string—
denyLabel

Deny button text.

string—

Also accepts the standard attributes of its root element.

Accessibility

KeyAction
TabApprove, Deny, Always allow, and the scrollable preview
Screen readersRisk is read as words; the decision is announced and focus moves to it
ColorRisk uses an icon and label as well as a colored edge

Use with AI

title, description, risk (low | medium | high), details rows and a preview of the exact action. onApprove, onDeny, optional onAlwaysAllow. expiresIn counts down and denies. After a decision the card shows the outcome and moves focus to it. With the shadcn MCP server set up (guide), ask your agent:

Add the Ballmac UI Approval Card (@ballmac/approval-card) to this project with the shadcn MCP, then use it where it fits.

Use it for

  • Before an agent runs commands, writes files or sends data out
  • Any action that needs a person's explicit yes

Not for

  • Confirming a destructive UI action; use alert-dialog
  • Reporting what a tool already did; use tool-call-card

Registry JSON: https://ui.ballmac.com/r/approval-card.json

Credits

Free to use in personal and commercial projects.