feat(cli): support single Ctrl+C to cancel streaming, preserving double Ctrl+C to exit (#5838)

This commit is contained in:
JAYADITYA 2025-08-12 09:43:57 +05:30 committed by GitHub
parent f9efb2e24f
commit 2d1a6af890
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 40 additions and 21 deletions

View File

@ -545,6 +545,7 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => {
initError, initError,
pendingHistoryItems: pendingGeminiHistoryItems, pendingHistoryItems: pendingGeminiHistoryItems,
thought, thought,
cancelOngoingRequest,
} = useGeminiStream( } = useGeminiStream(
config.getGeminiClient(), config.getGeminiClient(),
history, history,
@ -655,6 +656,9 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => {
if (isAuthenticating) { if (isAuthenticating) {
return; return;
} }
if (!ctrlCPressedOnce) {
cancelOngoingRequest?.();
}
handleExit(ctrlCPressedOnce, setCtrlCPressedOnce, ctrlCTimerRef); handleExit(ctrlCPressedOnce, setCtrlCPressedOnce, ctrlCTimerRef);
} else if (keyMatchers[Command.EXIT](key)) { } else if (keyMatchers[Command.EXIT](key)) {
if (buffer.text.length > 0) { if (buffer.text.length > 0) {
@ -686,6 +690,7 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => {
ctrlDTimerRef, ctrlDTimerRef,
handleSlashCommand, handleSlashCommand,
isAuthenticating, isAuthenticating,
cancelOngoingRequest,
], ],
); );

View File

@ -56,9 +56,9 @@ export const ToolConfirmationMessage: React.FC<
onConfirm(outcome); onConfirm(outcome);
}; };
useInput((_, key) => { useInput((input, key) => {
if (!isFocused) return; if (!isFocused) return;
if (key.escape) { if (key.escape || (key.ctrl && (input === 'c' || input === 'C'))) {
handleConfirm(ToolConfirmationOutcome.Cancel); handleConfirm(ToolConfirmationOutcome.Cancel);
} }
}); });

View File

@ -183,26 +183,39 @@ export const useGeminiStream = (
return StreamingState.Idle; return StreamingState.Idle;
}, [isResponding, toolCalls]); }, [isResponding, toolCalls]);
const cancelOngoingRequest = useCallback(() => {
if (streamingState !== StreamingState.Responding) {
return;
}
if (turnCancelledRef.current) {
return;
}
turnCancelledRef.current = true;
abortControllerRef.current?.abort();
if (pendingHistoryItemRef.current) {
addItem(pendingHistoryItemRef.current, Date.now());
}
addItem(
{
type: MessageType.INFO,
text: 'Request cancelled.',
},
Date.now(),
);
setPendingHistoryItem(null);
onCancelSubmit();
setIsResponding(false);
}, [
streamingState,
addItem,
setPendingHistoryItem,
onCancelSubmit,
pendingHistoryItemRef,
]);
useInput((_input, key) => { useInput((_input, key) => {
if (streamingState === StreamingState.Responding && key.escape) { if (key.escape) {
if (turnCancelledRef.current) { cancelOngoingRequest();
return;
}
turnCancelledRef.current = true;
abortControllerRef.current?.abort();
if (pendingHistoryItemRef.current) {
addItem(pendingHistoryItemRef.current, Date.now());
}
addItem(
{
type: MessageType.INFO,
text: 'Request cancelled.',
},
Date.now(),
);
setPendingHistoryItem(null);
onCancelSubmit();
setIsResponding(false);
} }
}); });
@ -954,5 +967,6 @@ export const useGeminiStream = (
initError, initError,
pendingHistoryItems, pendingHistoryItems,
thought, thought,
cancelOngoingRequest,
}; };
}; };