feat: add double Ctrl+C to exit from approval screen (#200)

Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
Charles Packer
2025-12-12 20:14:38 -08:00
committed by GitHub
parent 8bb588e5af
commit 28094b6cac
2 changed files with 37 additions and 0 deletions

View File

@@ -2561,6 +2561,32 @@ ${recentCommits}
],
);
// Cancel all pending approvals - queue denials to send with next message
// Similar to interrupt flow during tool execution
const handleCancelApprovals = useCallback(() => {
if (pendingApprovals.length === 0) return;
// Create denial results for all pending approvals and queue for next message
const denialResults = pendingApprovals.map((approval) => ({
type: "approval" as const,
tool_call_id: approval.toolCallId,
approve: false,
reason: "User cancelled the approval",
}));
setQueuedApprovalResults(denialResults);
// Mark the pending approval tool calls as cancelled in the buffers
markIncompleteToolsAsCancelled(buffersRef.current);
refreshDerived();
// Clear all approval state
setPendingApprovals([]);
setApprovalContexts([]);
setApprovalResults([]);
setAutoHandledResults([]);
setAutoDeniedApprovals([]);
}, [pendingApprovals, refreshDerived]);
const handleModelSelect = useCallback(
async (modelId: string) => {
setModelSelectorOpen(false);
@@ -3447,6 +3473,7 @@ Plan file path: ${planFilePath}`;
onApproveAll={handleApproveCurrent}
onApproveAlways={handleApproveAlways}
onDenyAll={handleDenyCurrent}
onCancel={handleCancelApprovals}
/>
</>
)}

View File

@@ -19,6 +19,7 @@ type Props = {
onApproveAll: () => void;
onApproveAlways: (scope?: "project" | "session") => void;
onDenyAll: (reason: string) => void;
onCancel?: () => void; // Cancel all approvals without sending to server
};
type DynamicPreviewProps = {
@@ -397,6 +398,7 @@ export const ApprovalDialog = memo(function ApprovalDialog({
onApproveAll,
onApproveAlways,
onDenyAll,
onCancel,
}: Props) {
const [selectedOption, setSelectedOption] = useState(0);
const [isEnteringReason, setIsEnteringReason] = useState(false);
@@ -453,6 +455,14 @@ export const ApprovalDialog = memo(function ApprovalDialog({
useInput((_input, key) => {
if (isExecuting) return;
// Handle CTRL-C to cancel all approvals
if (key.ctrl && _input === "c") {
if (onCancel) {
onCancel();
}
return;
}
if (isEnteringReason) {
// When entering reason, only handle enter/escape
if (key.return) {