"use client";
import { Check, Terminal, X } from "lucide-react";
import { AnimatePresence, motion, useReducedMotion } from "motion/react";
import type { ReactNode } from "react";
import { EASE_OUT } from "@/lib/ease";
import { cn } from "@/lib/utils";
export type ToolApprovalStatus = "pending" | "approved" | "denied";
export interface ToolApprovalProps {
/** What the agent wants to do. */
title: ReactNode;
/** The exact command, file path, or other detail worth reading before approving. */
description?: ReactNode;
/** Default "pending". Acting on the request is up to the caller: set this once onApprove or onDeny fires. */
status?: ToolApprovalStatus;
onApprove?: () => void;
onDeny?: () => void;
approveLabel?: string;
denyLabel?: string;
className?: string;
}
const RESOLVED_ICON: Record<"approved" | "denied", ReactNode> = {
approved: ,
denied: ,
};
const RESOLVED_LABEL: Record<"approved" | "denied", string> = {
approved: "Approved",
denied: "Denied",
};
const RESOLVED_CLASS: Record<"approved" | "denied", string> = {
approved: "bg-success/15 text-success",
denied: "bg-destructive/15 text-destructive",
};
/**
* A card for an agent action waiting on approval, with Approve and Deny.
* Resolving it collapses the buttons into a single status pill.
*/
export function ToolApproval({
title,
description,
status = "pending",
onApprove,
onDeny,
approveLabel = "Approve",
denyLabel = "Deny",
className,
}: ToolApprovalProps) {
const reduce = useReducedMotion();
const pending = status === "pending";
return (
{title}
{description ? (
{description}
) : null}
{pending ? (
) : (
{RESOLVED_ICON[status]}
{RESOLVED_LABEL[status]}
)}
);
}