Centralize Key Binding Logic and Refactor (Reopen) (#5356)

Co-authored-by: Lee-WonJun <10369528+Lee-WonJun@users.noreply.github.com>
This commit is contained in:
Lee Won Jun
2025-08-09 16:03:17 +09:00
committed by GitHub
parent 6487cc1689
commit b8084ba815
6 changed files with 788 additions and 72 deletions

View File

@@ -13,8 +13,6 @@ import {
Text,
useStdin,
useStdout,
useInput,
type Key as InkKeyType,
} from 'ink';
import { StreamingState, type HistoryItem, MessageType } from './types.js';
import { useTerminalSize } from './hooks/useTerminalSize.js';
@@ -81,6 +79,8 @@ import { useBracketedPaste } from './hooks/useBracketedPaste.js';
import { useTextBuffer } from './components/shared/text-buffer.js';
import { useVimMode, VimModeProvider } from './contexts/VimModeContext.js';
import { useVim } from './hooks/vim.js';
import { useKeypress, Key } from './hooks/useKeypress.js';
import { keyMatchers, Command } from './keyMatchers.js';
import * as fs from 'fs';
import { UpdateNotification } from './components/UpdateNotification.js';
import {
@@ -613,50 +613,71 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => {
[handleSlashCommand],
);
useInput((input: string, key: InkKeyType) => {
let enteringConstrainHeightMode = false;
if (!constrainHeight) {
// Automatically re-enter constrain height mode if the user types
// anything. When constrainHeight==false, the user will experience
// significant flickering so it is best to disable it immediately when
// the user starts interacting with the app.
enteringConstrainHeightMode = true;
setConstrainHeight(true);
}
const handleGlobalKeypress = useCallback(
(key: Key) => {
let enteringConstrainHeightMode = false;
if (!constrainHeight) {
enteringConstrainHeightMode = true;
setConstrainHeight(true);
}
if (key.ctrl && input === 'o') {
setShowErrorDetails((prev) => !prev);
} else if (key.ctrl && input === 't') {
const newValue = !showToolDescriptions;
setShowToolDescriptions(newValue);
if (keyMatchers[Command.SHOW_ERROR_DETAILS](key)) {
setShowErrorDetails((prev) => !prev);
} else if (keyMatchers[Command.TOGGLE_TOOL_DESCRIPTIONS](key)) {
const newValue = !showToolDescriptions;
setShowToolDescriptions(newValue);
const mcpServers = config.getMcpServers();
if (Object.keys(mcpServers || {}).length > 0) {
handleSlashCommand(newValue ? '/mcp desc' : '/mcp nodesc');
const mcpServers = config.getMcpServers();
if (Object.keys(mcpServers || {}).length > 0) {
handleSlashCommand(newValue ? '/mcp desc' : '/mcp nodesc');
}
} else if (
keyMatchers[Command.TOGGLE_IDE_CONTEXT_DETAIL](key) &&
config.getIdeMode() &&
ideContextState
) {
// Show IDE status when in IDE mode and context is available.
handleSlashCommand('/ide status');
} else if (keyMatchers[Command.QUIT](key)) {
// When authenticating, let AuthInProgress component handle Ctrl+C.
if (isAuthenticating) {
return;
}
handleExit(ctrlCPressedOnce, setCtrlCPressedOnce, ctrlCTimerRef);
} else if (keyMatchers[Command.EXIT](key)) {
if (buffer.text.length > 0) {
return;
}
handleExit(ctrlDPressedOnce, setCtrlDPressedOnce, ctrlDTimerRef);
} else if (
keyMatchers[Command.SHOW_MORE_LINES](key) &&
!enteringConstrainHeightMode
) {
setConstrainHeight(false);
}
} else if (
key.ctrl &&
input === 'e' &&
config.getIdeMode() &&
ideContextState
) {
handleSlashCommand('/ide status');
} else if (key.ctrl && (input === 'c' || input === 'C')) {
if (isAuthenticating) {
// Let AuthInProgress component handle the input.
return;
}
handleExit(ctrlCPressedOnce, setCtrlCPressedOnce, ctrlCTimerRef);
} else if (key.ctrl && (input === 'd' || input === 'D')) {
if (buffer.text.length > 0) {
// Do nothing if there is text in the input.
return;
}
handleExit(ctrlDPressedOnce, setCtrlDPressedOnce, ctrlDTimerRef);
} else if (key.ctrl && input === 's' && !enteringConstrainHeightMode) {
setConstrainHeight(false);
}
});
},
[
constrainHeight,
setConstrainHeight,
setShowErrorDetails,
showToolDescriptions,
setShowToolDescriptions,
config,
ideContextState,
handleExit,
ctrlCPressedOnce,
setCtrlCPressedOnce,
ctrlCTimerRef,
buffer.text.length,
ctrlDPressedOnce,
setCtrlDPressedOnce,
ctrlDTimerRef,
handleSlashCommand,
isAuthenticating,
],
);
useKeypress(handleGlobalKeypress, { isActive: true });
useEffect(() => {
if (config) {