diff --git a/docs/reference/keyboard-shortcuts.md b/docs/reference/keyboard-shortcuts.md
index e5691c43ee2..5ad55a2c749 100644
--- a/docs/reference/keyboard-shortcuts.md
+++ b/docs/reference/keyboard-shortcuts.md
@@ -19,12 +19,12 @@ available combinations.
| Action | Keys |
| ------------------------------------------- | ------------------------------------------------------------ |
-| Move the cursor to the start of the line. | `Ctrl + A`
`Home (no Shift, Ctrl)` |
-| Move the cursor to the end of the line. | `Ctrl + E`
`End (no Shift, Ctrl)` |
-| Move the cursor up one line. | `Up Arrow (no Shift, Alt, Ctrl, Cmd)` |
-| Move the cursor down one line. | `Down Arrow (no Shift, Alt, Ctrl, Cmd)` |
-| Move the cursor one character to the left. | `Left Arrow (no Shift, Alt, Ctrl, Cmd)` |
-| Move the cursor one character to the right. | `Right Arrow (no Shift, Alt, Ctrl, Cmd)`
`Ctrl + F` |
+| Move the cursor to the start of the line. | `Ctrl + A`
`Home` |
+| Move the cursor to the end of the line. | `Ctrl + E`
`End` |
+| Move the cursor up one line. | `Up Arrow` |
+| Move the cursor down one line. | `Down Arrow` |
+| Move the cursor one character to the left. | `Left Arrow` |
+| Move the cursor one character to the right. | `Right Arrow`
`Ctrl + F` |
| Move the cursor one word to the left. | `Ctrl + Left Arrow`
`Alt + Left Arrow`
`Alt + B` |
| Move the cursor one word to the right. | `Ctrl + Right Arrow`
`Alt + Right Arrow`
`Alt + F` |
@@ -39,7 +39,7 @@ available combinations.
| Delete the next word. | `Ctrl + Delete`
`Alt + Delete`
`Alt + D` |
| Delete the character to the left. | `Backspace`
`Ctrl + H` |
| Delete the character to the right. | `Delete`
`Ctrl + D` |
-| Undo the most recent text edit. | `Cmd + Z (no Shift)`
`Alt + Z (no Shift)` |
+| Undo the most recent text edit. | `Cmd + Z`
`Alt + Z` |
| Redo the most recent undone text edit. | `Shift + Ctrl + Z`
`Shift + Cmd + Z`
`Shift + Alt + Z` |
#### Scrolling
@@ -55,72 +55,72 @@ available combinations.
#### History & Search
-| Action | Keys |
-| -------------------------------------------- | --------------------- |
-| Show the previous entry in history. | `Ctrl + P (no Shift)` |
-| Show the next entry in history. | `Ctrl + N (no Shift)` |
-| Start reverse search through history. | `Ctrl + R` |
-| Submit the selected reverse-search match. | `Enter (no Ctrl)` |
-| Accept a suggestion while reverse searching. | `Tab (no Shift)` |
-| Browse and rewind previous interactions. | `Double Esc` |
+| Action | Keys |
+| -------------------------------------------- | ------------ |
+| Show the previous entry in history. | `Ctrl + P` |
+| Show the next entry in history. | `Ctrl + N` |
+| Start reverse search through history. | `Ctrl + R` |
+| Submit the selected reverse-search match. | `Enter` |
+| Accept a suggestion while reverse searching. | `Tab` |
+| Browse and rewind previous interactions. | `Double Esc` |
#### Navigation
-| Action | Keys |
-| -------------------------------------------------- | ------------------------------------------- |
-| Move selection up in lists. | `Up Arrow (no Shift)` |
-| Move selection down in lists. | `Down Arrow (no Shift)` |
-| Move up within dialog options. | `Up Arrow (no Shift)`
`K (no Shift)` |
-| Move down within dialog options. | `Down Arrow (no Shift)`
`J (no Shift)` |
-| Move to the next item or question in a dialog. | `Tab (no Shift)` |
-| Move to the previous item or question in a dialog. | `Shift + Tab` |
+| Action | Keys |
+| -------------------------------------------------- | --------------------- |
+| Move selection up in lists. | `Up Arrow` |
+| Move selection down in lists. | `Down Arrow` |
+| Move up within dialog options. | `Up Arrow`
`K` |
+| Move down within dialog options. | `Down Arrow`
`J` |
+| Move to the next item or question in a dialog. | `Tab` |
+| Move to the previous item or question in a dialog. | `Shift + Tab` |
#### Suggestions & Completions
-| Action | Keys |
-| --------------------------------------- | -------------------------------------------------- |
-| Accept the inline suggestion. | `Tab (no Shift)`
`Enter (no Ctrl)` |
-| Move to the previous completion option. | `Up Arrow (no Shift)`
`Ctrl + P (no Shift)` |
-| Move to the next completion option. | `Down Arrow (no Shift)`
`Ctrl + N (no Shift)` |
-| Expand an inline suggestion. | `Right Arrow` |
-| Collapse an inline suggestion. | `Left Arrow` |
+| Action | Keys |
+| --------------------------------------- | ---------------------------- |
+| Accept the inline suggestion. | `Tab`
`Enter` |
+| Move to the previous completion option. | `Up Arrow`
`Ctrl + P` |
+| Move to the next completion option. | `Down Arrow`
`Ctrl + N` |
+| Expand an inline suggestion. | `Right Arrow` |
+| Collapse an inline suggestion. | `Left Arrow` |
#### Text Input
| Action | Keys |
| ---------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
-| Submit the current prompt. | `Enter (no Shift, Alt, Ctrl, Cmd)` |
+| Submit the current prompt. | `Enter` |
| Insert a newline without submitting. | `Ctrl + Enter`
`Cmd + Enter`
`Alt + Enter`
`Shift + Enter`
`Ctrl + J` |
| Open the current prompt or the plan in an external editor. | `Ctrl + X` |
| Paste from the clipboard. | `Ctrl + V`
`Cmd + V`
`Alt + V` |
#### App Controls
-| Action | Keys |
-| -------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- |
-| Toggle detailed error information. | `F12` |
-| Toggle the full TODO list. | `Ctrl + T` |
-| Show IDE context details. | `Ctrl + G` |
-| Toggle Markdown rendering. | `Alt + M` |
-| Toggle copy mode when in alternate buffer mode. | `Ctrl + S` |
-| Toggle YOLO (auto-approval) mode for tool calls. | `Ctrl + Y` |
-| Cycle through approval modes: default (prompt), auto_edit (auto-approve edits), and plan (read-only). Plan mode is skipped when the agent is busy. | `Shift + Tab` |
-| Expand and collapse blocks of content when not in alternate buffer mode. | `Ctrl + O` |
-| Expand or collapse a paste placeholder when cursor is over placeholder. | `Ctrl + O` |
-| Toggle current background shell visibility. | `Ctrl + B` |
-| Toggle background shell list. | `Ctrl + L` |
-| Kill the active background shell. | `Ctrl + K` |
-| Confirm selection in background shell list. | `Enter` |
-| Dismiss background shell list. | `Esc` |
-| Move focus from background shell to Gemini. | `Shift + Tab` |
-| Move focus from background shell list to Gemini. | `Tab (no Shift)` |
-| Show warning when trying to move focus away from background shell. | `Tab (no Shift)` |
-| Show warning when trying to move focus away from shell input. | `Tab (no Shift)` |
-| Move focus from Gemini to the active shell. | `Tab (no Shift)` |
-| Move focus from the shell back to Gemini. | `Shift + Tab` |
-| Clear the terminal screen and redraw the UI. | `Ctrl + L` |
-| Restart the application. | `R` |
-| Suspend the CLI and move it to the background. | `Ctrl + Z` |
+| Action | Keys |
+| -------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- |
+| Toggle detailed error information. | `F12` |
+| Toggle the full TODO list. | `Ctrl + T` |
+| Show IDE context details. | `Ctrl + G` |
+| Toggle Markdown rendering. | `Alt + M` |
+| Toggle copy mode when in alternate buffer mode. | `Ctrl + S` |
+| Toggle YOLO (auto-approval) mode for tool calls. | `Ctrl + Y` |
+| Cycle through approval modes: default (prompt), auto_edit (auto-approve edits), and plan (read-only). Plan mode is skipped when the agent is busy. | `Shift + Tab` |
+| Expand and collapse blocks of content when not in alternate buffer mode. | `Ctrl + O` |
+| Expand or collapse a paste placeholder when cursor is over placeholder. | `Ctrl + O` |
+| Toggle current background shell visibility. | `Ctrl + B` |
+| Toggle background shell list. | `Ctrl + L` |
+| Kill the active background shell. | `Ctrl + K` |
+| Confirm selection in background shell list. | `Enter` |
+| Dismiss background shell list. | `Esc` |
+| Move focus from background shell to Gemini. | `Shift + Tab` |
+| Move focus from background shell list to Gemini. | `Tab` |
+| Show warning when trying to move focus away from background shell. | `Tab` |
+| Show warning when trying to move focus away from shell input. | `Tab` |
+| Move focus from Gemini to the active shell. | `Tab` |
+| Move focus from the shell back to Gemini. | `Shift + Tab` |
+| Clear the terminal screen and redraw the UI. | `Ctrl + L` |
+| Restart the application. | `R`
`Shift + R` |
+| Suspend the CLI and move it to the background. | `Ctrl + Z` |
@@ -156,7 +156,7 @@ available combinations.
## Limitations
- On [Windows Terminal](https://en.wikipedia.org/wiki/Windows_Terminal):
- - `shift+enter` is not supported.
+ - `shift+enter` is only supported in version 1.25 and higher.
- `shift+tab`
[is not supported](https://github.com/google-gemini/gemini-cli/issues/20314)
on Node 20 and earlier versions of Node 22.
diff --git a/packages/cli/src/config/keyBindings.test.ts b/packages/cli/src/config/keyBindings.test.ts
index c2abc32d276..e450e68b719 100644
--- a/packages/cli/src/config/keyBindings.test.ts
+++ b/packages/cli/src/config/keyBindings.test.ts
@@ -58,46 +58,6 @@ describe('keyBindings config', () => {
const config: KeyBindingConfig = defaultKeyBindings;
expect(config[Command.HOME]).toBeDefined();
});
-
- it('should have correct specific bindings', () => {
- // Verify navigation ignores shift
- const navUp = defaultKeyBindings[Command.NAVIGATION_UP];
- expect(navUp).toContainEqual({ key: 'up', shift: false });
-
- const navDown = defaultKeyBindings[Command.NAVIGATION_DOWN];
- expect(navDown).toContainEqual({ key: 'down', shift: false });
-
- // Verify dialog navigation
- const dialogNavUp = defaultKeyBindings[Command.DIALOG_NAVIGATION_UP];
- expect(dialogNavUp).toContainEqual({ key: 'up', shift: false });
- expect(dialogNavUp).toContainEqual({ key: 'k', shift: false });
-
- const dialogNavDown = defaultKeyBindings[Command.DIALOG_NAVIGATION_DOWN];
- expect(dialogNavDown).toContainEqual({ key: 'down', shift: false });
- expect(dialogNavDown).toContainEqual({ key: 'j', shift: false });
-
- // Verify physical home/end keys for cursor movement
- expect(defaultKeyBindings[Command.HOME]).toContainEqual({
- key: 'home',
- ctrl: false,
- shift: false,
- });
- expect(defaultKeyBindings[Command.END]).toContainEqual({
- key: 'end',
- ctrl: false,
- shift: false,
- });
-
- // Verify physical home/end keys for scrolling
- expect(defaultKeyBindings[Command.SCROLL_HOME]).toContainEqual({
- key: 'home',
- ctrl: true,
- });
- expect(defaultKeyBindings[Command.SCROLL_END]).toContainEqual({
- key: 'end',
- ctrl: true,
- });
- });
});
describe('command metadata', () => {
diff --git a/packages/cli/src/config/keyBindings.ts b/packages/cli/src/config/keyBindings.ts
index 3122acef1d9..e2260d99d86 100644
--- a/packages/cli/src/config/keyBindings.ts
+++ b/packages/cli/src/config/keyBindings.ts
@@ -134,27 +134,12 @@ export const defaultKeyBindings: KeyBindingConfig = {
[Command.EXIT]: [{ key: 'd', ctrl: true }],
// Cursor Movement
- [Command.HOME]: [
- { key: 'a', ctrl: true },
- { key: 'home', shift: false, ctrl: false },
- ],
- [Command.END]: [
- { key: 'e', ctrl: true },
- { key: 'end', shift: false, ctrl: false },
- ],
- [Command.MOVE_UP]: [
- { key: 'up', shift: false, alt: false, ctrl: false, cmd: false },
- ],
- [Command.MOVE_DOWN]: [
- { key: 'down', shift: false, alt: false, ctrl: false, cmd: false },
- ],
- [Command.MOVE_LEFT]: [
- { key: 'left', shift: false, alt: false, ctrl: false, cmd: false },
- ],
- [Command.MOVE_RIGHT]: [
- { key: 'right', shift: false, alt: false, ctrl: false, cmd: false },
- { key: 'f', ctrl: true },
- ],
+ [Command.HOME]: [{ key: 'a', ctrl: true }, { key: 'home' }],
+ [Command.END]: [{ key: 'e', ctrl: true }, { key: 'end' }],
+ [Command.MOVE_UP]: [{ key: 'up' }],
+ [Command.MOVE_DOWN]: [{ key: 'down' }],
+ [Command.MOVE_LEFT]: [{ key: 'left' }],
+ [Command.MOVE_RIGHT]: [{ key: 'right' }, { key: 'f', ctrl: true }],
[Command.MOVE_WORD_LEFT]: [
{ key: 'left', ctrl: true },
{ key: 'left', alt: true },
@@ -183,8 +168,8 @@ export const defaultKeyBindings: KeyBindingConfig = {
[Command.DELETE_CHAR_LEFT]: [{ key: 'backspace' }, { key: 'h', ctrl: true }],
[Command.DELETE_CHAR_RIGHT]: [{ key: 'delete' }, { key: 'd', ctrl: true }],
[Command.UNDO]: [
- { key: 'z', cmd: true, shift: false },
- { key: 'z', alt: true, shift: false },
+ { key: 'z', cmd: true },
+ { key: 'z', alt: true },
],
[Command.REDO]: [
{ key: 'z', ctrl: true, shift: true },
@@ -207,56 +192,33 @@ export const defaultKeyBindings: KeyBindingConfig = {
[Command.PAGE_DOWN]: [{ key: 'pagedown' }],
// History & Search
- [Command.HISTORY_UP]: [{ key: 'p', shift: false, ctrl: true }],
- [Command.HISTORY_DOWN]: [{ key: 'n', shift: false, ctrl: true }],
+ [Command.HISTORY_UP]: [{ key: 'p', ctrl: true }],
+ [Command.HISTORY_DOWN]: [{ key: 'n', ctrl: true }],
[Command.REVERSE_SEARCH]: [{ key: 'r', ctrl: true }],
- [Command.REWIND]: [{ key: 'double escape' }],
- [Command.SUBMIT_REVERSE_SEARCH]: [{ key: 'return', ctrl: false }],
- [Command.ACCEPT_SUGGESTION_REVERSE_SEARCH]: [{ key: 'tab', shift: false }],
+ [Command.REWIND]: [{ key: 'double escape' }], // for documentation only
+ [Command.SUBMIT_REVERSE_SEARCH]: [{ key: 'return' }],
+ [Command.ACCEPT_SUGGESTION_REVERSE_SEARCH]: [{ key: 'tab' }],
// Navigation
- [Command.NAVIGATION_UP]: [{ key: 'up', shift: false }],
- [Command.NAVIGATION_DOWN]: [{ key: 'down', shift: false }],
+ [Command.NAVIGATION_UP]: [{ key: 'up' }],
+ [Command.NAVIGATION_DOWN]: [{ key: 'down' }],
// Navigation shortcuts appropriate for dialogs where we do not need to accept
// text input.
- [Command.DIALOG_NAVIGATION_UP]: [
- { key: 'up', shift: false },
- { key: 'k', shift: false },
- ],
- [Command.DIALOG_NAVIGATION_DOWN]: [
- { key: 'down', shift: false },
- { key: 'j', shift: false },
- ],
- [Command.DIALOG_NEXT]: [{ key: 'tab', shift: false }],
+ [Command.DIALOG_NAVIGATION_UP]: [{ key: 'up' }, { key: 'k' }],
+ [Command.DIALOG_NAVIGATION_DOWN]: [{ key: 'down' }, { key: 'j' }],
+ [Command.DIALOG_NEXT]: [{ key: 'tab' }],
[Command.DIALOG_PREV]: [{ key: 'tab', shift: true }],
// Suggestions & Completions
- [Command.ACCEPT_SUGGESTION]: [
- { key: 'tab', shift: false },
- { key: 'return', ctrl: false },
- ],
- [Command.COMPLETION_UP]: [
- { key: 'up', shift: false },
- { key: 'p', shift: false, ctrl: true },
- ],
- [Command.COMPLETION_DOWN]: [
- { key: 'down', shift: false },
- { key: 'n', shift: false, ctrl: true },
- ],
+ [Command.ACCEPT_SUGGESTION]: [{ key: 'tab' }, { key: 'return' }],
+ [Command.COMPLETION_UP]: [{ key: 'up' }, { key: 'p', ctrl: true }],
+ [Command.COMPLETION_DOWN]: [{ key: 'down' }, { key: 'n', ctrl: true }],
[Command.EXPAND_SUGGESTION]: [{ key: 'right' }],
[Command.COLLAPSE_SUGGESTION]: [{ key: 'left' }],
// Text Input
// Must also exclude shift to allow shift+enter for newline
- [Command.SUBMIT]: [
- {
- key: 'return',
- shift: false,
- alt: false,
- ctrl: false,
- cmd: false,
- },
- ],
+ [Command.SUBMIT]: [{ key: 'return' }],
[Command.NEWLINE]: [
{ key: 'return', ctrl: true },
{ key: 'return', cmd: true },
@@ -283,19 +245,17 @@ export const defaultKeyBindings: KeyBindingConfig = {
[Command.TOGGLE_BACKGROUND_SHELL_LIST]: [{ key: 'l', ctrl: true }],
[Command.KILL_BACKGROUND_SHELL]: [{ key: 'k', ctrl: true }],
[Command.UNFOCUS_BACKGROUND_SHELL]: [{ key: 'tab', shift: true }],
- [Command.UNFOCUS_BACKGROUND_SHELL_LIST]: [{ key: 'tab', shift: false }],
- [Command.SHOW_BACKGROUND_SHELL_UNFOCUS_WARNING]: [
- { key: 'tab', shift: false },
- ],
- [Command.SHOW_SHELL_INPUT_UNFOCUS_WARNING]: [{ key: 'tab', shift: false }],
+ [Command.UNFOCUS_BACKGROUND_SHELL_LIST]: [{ key: 'tab' }],
+ [Command.SHOW_BACKGROUND_SHELL_UNFOCUS_WARNING]: [{ key: 'tab' }],
+ [Command.SHOW_SHELL_INPUT_UNFOCUS_WARNING]: [{ key: 'tab' }],
[Command.BACKGROUND_SHELL_SELECT]: [{ key: 'return' }],
[Command.BACKGROUND_SHELL_ESCAPE]: [{ key: 'escape' }],
[Command.SHOW_MORE_LINES]: [{ key: 'o', ctrl: true }],
[Command.EXPAND_PASTE]: [{ key: 'o', ctrl: true }],
- [Command.FOCUS_SHELL_INPUT]: [{ key: 'tab', shift: false }],
+ [Command.FOCUS_SHELL_INPUT]: [{ key: 'tab' }],
[Command.UNFOCUS_SHELL_INPUT]: [{ key: 'tab', shift: true }],
[Command.CLEAR_SCREEN]: [{ key: 'l', ctrl: true }],
- [Command.RESTART_APP]: [{ key: 'r' }],
+ [Command.RESTART_APP]: [{ key: 'r' }, { key: 'r', shift: true }],
[Command.SUSPEND_APP]: [{ key: 'z', ctrl: true }],
};
diff --git a/packages/cli/src/ui/components/__snapshots__/AskUserDialog.test.tsx.snap b/packages/cli/src/ui/components/__snapshots__/AskUserDialog.test.tsx.snap
index 96440266340..06f509f1f65 100644
--- a/packages/cli/src/ui/components/__snapshots__/AskUserDialog.test.tsx.snap
+++ b/packages/cli/src/ui/components/__snapshots__/AskUserDialog.test.tsx.snap
@@ -11,17 +11,6 @@ Enter to submit · Esc to cancel
"
`;
-exports[`AskUserDialog > Choice question placeholder > uses default placeholder when not provided 2`] = `
-"Select your preferred language:
-
- 1. TypeScript
- 2. JavaScript
-● 3. Enter a custom value
-
-Enter to submit · Esc to cancel
-"
-`;
-
exports[`AskUserDialog > Choice question placeholder > uses placeholder for "Other" option when provided 1`] = `
"Select your preferred language:
@@ -33,17 +22,6 @@ Enter to submit · Esc to cancel
"
`;
-exports[`AskUserDialog > Choice question placeholder > uses placeholder for "Other" option when provided 2`] = `
-"Select your preferred language:
-
- 1. TypeScript
- 2. JavaScript
-● 3. Type another language...
-
-Enter to submit · Esc to cancel
-"
-`;
-
exports[`AskUserDialog > Scroll Arrows (useAlternateBuffer: false) > shows scroll arrows correctly when useAlternateBuffer is false 1`] = `
"Choose an option
@@ -58,20 +36,6 @@ Enter to select · ↑/↓ to navigate · Esc to cancel
"
`;
-exports[`AskUserDialog > Scroll Arrows (useAlternateBuffer: false) > shows scroll arrows correctly when useAlternateBuffer is false 2`] = `
-"Choose an option
-
-▲
-● 1. Option 1
- Description 1
- 2. Option 2
- Description 2
-▼
-
-Enter to select · ↑/↓ to navigate · Esc to cancel
-"
-`;
-
exports[`AskUserDialog > Scroll Arrows (useAlternateBuffer: true) > shows scroll arrows correctly when useAlternateBuffer is true 1`] = `
"Choose an option
@@ -111,45 +75,6 @@ Enter to select · ↑/↓ to navigate · Esc to cancel
"
`;
-exports[`AskUserDialog > Scroll Arrows (useAlternateBuffer: true) > shows scroll arrows correctly when useAlternateBuffer is true 2`] = `
-"Choose an option
-
-● 1. Option 1
- Description 1
- 2. Option 2
- Description 2
- 3. Option 3
- Description 3
- 4. Option 4
- Description 4
- 5. Option 5
- Description 5
- 6. Option 6
- Description 6
- 7. Option 7
- Description 7
- 8. Option 8
- Description 8
- 9. Option 9
- Description 9
- 10. Option 10
- Description 10
- 11. Option 11
- Description 11
- 12. Option 12
- Description 12
- 13. Option 13
- Description 13
- 14. Option 14
- Description 14
- 15. Option 15
- Description 15
- 16. Enter a custom value
-
-Enter to select · ↑/↓ to navigate · Esc to cancel
-"
-`;
-
exports[`AskUserDialog > Text type questions > renders text input for type: "text" 1`] = `
"What should we name this component?
diff --git a/packages/cli/src/ui/components/__snapshots__/ExitPlanModeDialog.test.tsx.snap b/packages/cli/src/ui/components/__snapshots__/ExitPlanModeDialog.test.tsx.snap
index 9e210e34381..073c106ceb3 100644
--- a/packages/cli/src/ui/components/__snapshots__/ExitPlanModeDialog.test.tsx.snap
+++ b/packages/cli/src/ui/components/__snapshots__/ExitPlanModeDialog.test.tsx.snap
@@ -27,33 +27,6 @@ Enter to select · ↑/↓ to navigate · Ctrl+X to edit plan · Esc to cancel
"
`;
-exports[`ExitPlanModeDialog > useAlternateBuffer: false > bubbles up Ctrl+C when feedback is empty while editing 2`] = `
-"Overview
-
-Add user authentication to the CLI application.
-
-Implementation Steps
-
- 1. Create src/auth/AuthService.ts with login/logout methods
- 2. Add session storage in src/storage/SessionStore.ts
- 3. Update src/commands/index.ts to check auth status
- 4. Add tests in src/auth/__tests__/
-
-Files to Modify
-
- - src/index.ts - Add auth middleware
- - src/config.ts - Add auth configuration options
-
- 1. Yes, automatically accept edits
- Approves plan and allows tools to run automatically
- 2. Yes, manually accept edits
- Approves plan but requires confirmation for each tool
-● 3. Type your feedback...
-
-Enter to submit · Ctrl+X to edit plan · Esc to cancel
-"
-`;
-
exports[`ExitPlanModeDialog > useAlternateBuffer: false > calls onFeedback when feedback is typed and submitted 1`] = `
"Overview
@@ -81,33 +54,6 @@ Enter to select · ↑/↓ to navigate · Ctrl+X to edit plan · Esc to cancel
"
`;
-exports[`ExitPlanModeDialog > useAlternateBuffer: false > calls onFeedback when feedback is typed and submitted 2`] = `
-"Overview
-
-Add user authentication to the CLI application.
-
-Implementation Steps
-
- 1. Create src/auth/AuthService.ts with login/logout methods
- 2. Add session storage in src/storage/SessionStore.ts
- 3. Update src/commands/index.ts to check auth status
- 4. Add tests in src/auth/__tests__/
-
-Files to Modify
-
- - src/index.ts - Add auth middleware
- - src/config.ts - Add auth configuration options
-
- 1. Yes, automatically accept edits
- Approves plan and allows tools to run automatically
- 2. Yes, manually accept edits
- Approves plan but requires confirmation for each tool
-● 3. Add tests
-
-Enter to submit · Ctrl+X to edit plan · Esc to cancel
-"
-`;
-
exports[`ExitPlanModeDialog > useAlternateBuffer: false > displays error state when file read fails 1`] = `
" Error reading plan: File not found
"
@@ -194,33 +140,6 @@ Enter to select · ↑/↓ to navigate · Ctrl+X to edit plan · Esc to cancel
"
`;
-exports[`ExitPlanModeDialog > useAlternateBuffer: true > bubbles up Ctrl+C when feedback is empty while editing 2`] = `
-"Overview
-
-Add user authentication to the CLI application.
-
-Implementation Steps
-
- 1. Create src/auth/AuthService.ts with login/logout methods
- 2. Add session storage in src/storage/SessionStore.ts
- 3. Update src/commands/index.ts to check auth status
- 4. Add tests in src/auth/__tests__/
-
-Files to Modify
-
- - src/index.ts - Add auth middleware
- - src/config.ts - Add auth configuration options
-
- 1. Yes, automatically accept edits
- Approves plan and allows tools to run automatically
- 2. Yes, manually accept edits
- Approves plan but requires confirmation for each tool
-● 3. Type your feedback...
-
-Enter to submit · Ctrl+X to edit plan · Esc to cancel
-"
-`;
-
exports[`ExitPlanModeDialog > useAlternateBuffer: true > calls onFeedback when feedback is typed and submitted 1`] = `
"Overview
@@ -248,33 +167,6 @@ Enter to select · ↑/↓ to navigate · Ctrl+X to edit plan · Esc to cancel
"
`;
-exports[`ExitPlanModeDialog > useAlternateBuffer: true > calls onFeedback when feedback is typed and submitted 2`] = `
-"Overview
-
-Add user authentication to the CLI application.
-
-Implementation Steps
-
- 1. Create src/auth/AuthService.ts with login/logout methods
- 2. Add session storage in src/storage/SessionStore.ts
- 3. Update src/commands/index.ts to check auth status
- 4. Add tests in src/auth/__tests__/
-
-Files to Modify
-
- - src/index.ts - Add auth middleware
- - src/config.ts - Add auth configuration options
-
- 1. Yes, automatically accept edits
- Approves plan and allows tools to run automatically
- 2. Yes, manually accept edits
- Approves plan but requires confirmation for each tool
-● 3. Add tests
-
-Enter to submit · Ctrl+X to edit plan · Esc to cancel
-"
-`;
-
exports[`ExitPlanModeDialog > useAlternateBuffer: true > displays error state when file read fails 1`] = `
" Error reading plan: File not found
"
diff --git a/packages/cli/src/ui/components/__snapshots__/InputPrompt.test.tsx.snap b/packages/cli/src/ui/components/__snapshots__/InputPrompt.test.tsx.snap
index f40887b3b9c..5a2819702e0 100644
--- a/packages/cli/src/ui/components/__snapshots__/InputPrompt.test.tsx.snap
+++ b/packages/cli/src/ui/components/__snapshots__/InputPrompt.test.tsx.snap
@@ -78,27 +78,6 @@ exports[`InputPrompt > mouse interaction > should toggle paste expansion on doub
"
`;
-exports[`InputPrompt > mouse interaction > should toggle paste expansion on double-click 4`] = `
-"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
- > [Pasted Text: 10 lines]
-▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
-"
-`;
-
-exports[`InputPrompt > mouse interaction > should toggle paste expansion on double-click 5`] = `
-"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
- > [Pasted Text: 10 lines]
-▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
-"
-`;
-
-exports[`InputPrompt > mouse interaction > should toggle paste expansion on double-click 6`] = `
-"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
- > [Pasted Text: 10 lines]
-▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
-"
-`;
-
exports[`InputPrompt > snapshots > should not show inverted cursor when shell is focused 1`] = `
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
> Type your message or @path/to/file
diff --git a/packages/cli/src/ui/contexts/KeypressContext.test.tsx b/packages/cli/src/ui/contexts/KeypressContext.test.tsx
index e25ff576428..bc8e1981683 100644
--- a/packages/cli/src/ui/contexts/KeypressContext.test.tsx
+++ b/packages/cli/src/ui/contexts/KeypressContext.test.tsx
@@ -288,7 +288,7 @@ describe('KeypressContext', () => {
expect.objectContaining({
name: 'escape',
shift: false,
- alt: true,
+ alt: false,
cmd: false,
}),
);
@@ -297,7 +297,7 @@ describe('KeypressContext', () => {
expect.objectContaining({
name: 'escape',
shift: false,
- alt: true,
+ alt: false,
cmd: false,
}),
);
@@ -326,7 +326,7 @@ describe('KeypressContext', () => {
expect.objectContaining({
name: 'escape',
shift: false,
- alt: true,
+ alt: false,
cmd: false,
}),
);
diff --git a/packages/cli/src/ui/contexts/KeypressContext.tsx b/packages/cli/src/ui/contexts/KeypressContext.tsx
index 814404d84bd..d3f9031ffe6 100644
--- a/packages/cli/src/ui/contexts/KeypressContext.tsx
+++ b/packages/cli/src/ui/contexts/KeypressContext.tsx
@@ -178,8 +178,7 @@ function nonKeyboardEventFilter(
}
/**
- * Converts return keys pressed quickly after other keys into plain
- * insertable return characters.
+ * Converts return keys pressed quickly after insertable keys into a shift+return
*
* This is to accommodate older terminals that paste text without bracketing.
*/
@@ -201,7 +200,7 @@ function bufferFastReturn(keypressHandler: KeypressHandler): KeypressHandler {
} else {
keypressHandler(key);
}
- lastKeyTime = now;
+ lastKeyTime = key.insertable ? now : 0;
};
}
@@ -630,7 +629,7 @@ function* emitKeys(
} else if (sequence === `${ESC}${ESC}`) {
// Double escape
name = 'escape';
- alt = true;
+ alt = false;
// Emit first escape key here, then continue processing
keypressHandler({
@@ -645,7 +644,7 @@ function* emitKeys(
} else if (escaped) {
// Escape sequence timeout
name = ch.length ? undefined : 'escape';
- alt = true;
+ alt = ch.length > 0;
} else {
// Any other character is considered printable.
insertable = true;
diff --git a/packages/cli/src/ui/keyMatchers.test.ts b/packages/cli/src/ui/keyMatchers.test.ts
index 763754ec951..888393be83a 100644
--- a/packages/cli/src/ui/keyMatchers.test.ts
+++ b/packages/cli/src/ui/keyMatchers.test.ts
@@ -32,8 +32,12 @@ describe('keyMatchers', () => {
},
{
command: Command.ESCAPE,
- positive: [createKey('escape'), createKey('escape', { ctrl: true })],
- negative: [createKey('e'), createKey('esc')],
+ positive: [createKey('escape')],
+ negative: [
+ createKey('e'),
+ createKey('esc'),
+ createKey('escape', { ctrl: true }),
+ ],
},
// Cursor movement
@@ -192,13 +196,21 @@ describe('keyMatchers', () => {
},
{
command: Command.PAGE_UP,
- positive: [createKey('pageup'), createKey('pageup', { shift: true })],
- negative: [createKey('pagedown'), createKey('up')],
+ positive: [createKey('pageup')],
+ negative: [
+ createKey('pagedown'),
+ createKey('up'),
+ createKey('pageup', { shift: true }),
+ ],
},
{
command: Command.PAGE_DOWN,
- positive: [createKey('pagedown'), createKey('pagedown', { ctrl: true })],
- negative: [createKey('pageup'), createKey('down')],
+ positive: [createKey('pagedown')],
+ negative: [
+ createKey('pageup'),
+ createKey('down'),
+ createKey('pagedown', { ctrl: true }),
+ ],
},
// History navigation
@@ -214,13 +226,21 @@ describe('keyMatchers', () => {
},
{
command: Command.NAVIGATION_UP,
- positive: [createKey('up'), createKey('up', { ctrl: true })],
- negative: [createKey('p'), createKey('u')],
+ positive: [createKey('up')],
+ negative: [
+ createKey('p'),
+ createKey('u'),
+ createKey('up', { ctrl: true }),
+ ],
},
{
command: Command.NAVIGATION_DOWN,
- positive: [createKey('down'), createKey('down', { ctrl: true })],
- negative: [createKey('n'), createKey('d')],
+ positive: [createKey('down')],
+ negative: [
+ createKey('n'),
+ createKey('d'),
+ createKey('down', { ctrl: true }),
+ ],
},
// Dialog navigation
@@ -333,14 +353,12 @@ describe('keyMatchers', () => {
},
{
command: Command.SUSPEND_APP,
- positive: [
- createKey('z', { ctrl: true }),
- createKey('z', { ctrl: true, shift: true }),
- ],
+ positive: [createKey('z', { ctrl: true })],
negative: [
createKey('z'),
createKey('y', { ctrl: true }),
createKey('z', { alt: true }),
+ createKey('z', { ctrl: true, shift: true }),
],
},
{
@@ -365,8 +383,12 @@ describe('keyMatchers', () => {
},
{
command: Command.ACCEPT_SUGGESTION_REVERSE_SEARCH,
- positive: [createKey('tab'), createKey('tab', { ctrl: true })],
- negative: [createKey('return'), createKey('space')],
+ positive: [createKey('tab')],
+ negative: [
+ createKey('return'),
+ createKey('space'),
+ createKey('tab', { ctrl: true }),
+ ],
},
{
command: Command.FOCUS_SHELL_INPUT,
@@ -413,22 +435,6 @@ describe('keyMatchers', () => {
});
});
});
-
- it('should properly handle ACCEPT_SUGGESTION_REVERSE_SEARCH cases', () => {
- expect(
- keyMatchers[Command.ACCEPT_SUGGESTION_REVERSE_SEARCH](
- createKey('return', { ctrl: true }),
- ),
- ).toBe(false); // ctrl must be false
- expect(
- keyMatchers[Command.ACCEPT_SUGGESTION_REVERSE_SEARCH](createKey('tab')),
- ).toBe(true);
- expect(
- keyMatchers[Command.ACCEPT_SUGGESTION_REVERSE_SEARCH](
- createKey('tab', { ctrl: true }),
- ),
- ).toBe(true); // modifiers ignored
- });
});
describe('Custom key bindings', () => {
diff --git a/packages/cli/src/ui/keyMatchers.ts b/packages/cli/src/ui/keyMatchers.ts
index 7c61db10163..f833e5ee09a 100644
--- a/packages/cli/src/ui/keyMatchers.ts
+++ b/packages/cli/src/ui/keyMatchers.ts
@@ -13,16 +13,15 @@ import { Command, defaultKeyBindings } from '../config/keyBindings.js';
* Pure data-driven matching logic
*/
function matchKeyBinding(keyBinding: KeyBinding, key: Key): boolean {
- // Check modifiers - follow original logic:
- // undefined = ignore this modifier (original behavior)
+ // Check modifiers:
// true = modifier must be pressed
- // false = modifier must NOT be pressed
+ // false or undefined = modifier must NOT be pressed
return (
keyBinding.key === key.name &&
- (keyBinding.shift === undefined || key.shift === keyBinding.shift) &&
- (keyBinding.alt === undefined || key.alt === keyBinding.alt) &&
- (keyBinding.ctrl === undefined || key.ctrl === keyBinding.ctrl) &&
- (keyBinding.cmd === undefined || key.cmd === keyBinding.cmd)
+ !!key.shift === !!keyBinding.shift &&
+ !!key.alt === !!keyBinding.alt &&
+ !!key.ctrl === !!keyBinding.ctrl &&
+ !!key.cmd === !!keyBinding.cmd
);
}