qwen-code/packages/cli/src/config/keyBindings.ts
易良 c353fbbfa3
feat(cli): add Ctrl+Y shortcut to retry failed requests (#2011)
* feat: add Ctrl+Y shortcut to retry failed requests

- Add Ctrl+Y keyboard shortcut for retrying the last failed request
- Add isNetworkError() to detect transient network failures (ECONNREFUSED, ETIMEDOUT, etc.)
- Add DashScope 1305 error code to rate limit detection
- Add error hint \"Press Ctrl+Y to retry\" in error messages
- Support user-defined error codes for retry via config
- Add retryLastPrompt() hook in useGeminiStream
- Update keyboard shortcuts documentation

* feat: improve Ctrl+Y retry feature with tests, docs, and rate limit config

- Add comprehensive tests for Ctrl+Y retry shortcut in InputPrompt
- Add unit tests for retryLastPrompt in useGeminiStream hook
- Add detailed JSDoc comments for retryLastPrompt function and Ctrl+Y shortcut
- Extend isRateLimitError to support custom error codes via retryErrorCodes config
- Fix rate limit retry log variable reference (RATE_LIMIT_RETRY_OPTIONS → maxRateLimitRetries)
- Add Eclipse IDE files to .gitignore

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* refactor(ui): consolidate retry countdown as inline hint in error messages

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* feat(cli): enhance error handling with improved retry mechanism

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

- Modify ErrorMessage component to remove dim color from hint text

- Update useGeminiStream hook to improve retry countdown behavior with option to preserve or clear hints

- Adjust tests to match new error handling implementation

* feat: add Ctrl+Y shortcut to retry the last failed request

When a request errors out, the error message shows an inline hint
"(Press Ctrl+Y to retry.)" in secondary color. Pressing Ctrl+Y
re-submits the same prompt, commits the error text to history
(without the hint), and clears the hint from the UI.

- Add retryLastPrompt action wired to Ctrl+Y via keyBindings and InputPrompt
- Track last submitted prompt and error state in useGeminiStream refs
- Show retry hint inline with error text in ErrorMessage component,
  wrapping naturally on narrow terminals while preserving hint color
- Expose retryLastPrompt through UIActionsContext
- Add keyboard shortcut entry in KeyboardShortcuts display
- Add i18n strings for hint and no-retry-available message
- Document Ctrl+Y in keyboard-shortcuts.md

* docs(configuration): Update model provider configuration document

* chore: remove YOLO mode code from core

* fix: prevent Ctrl+Y hint from overriding auto-retry countdown

When an auto-retry countdown is active (retryCountdownTimerRef is set),
handleErrorEvent should not overwrite it with the Ctrl+Y hint. The auto-retry
hint ("retrying in Xs...") and manual retry hint ("Press Ctrl+Y to retry.")
are mutually exclusive:

- Auto-retry errors (e.g., rate limits): show countdown hint
- Other errors: show Ctrl+Y hint

Also removed retryErrorCodes from ContentGeneratorConfig as it's not part
of the minimal Ctrl+Y feature scope.

* simplify: remove complex options from clearRetryCountdown

Revert clearRetryCountdown to simplest form without options parameter.
The function now just clears the timer and pending item without any
automatic history commit logic.

* fix: restore pendingRetryCountdownItem as separate state from pendingRetryErrorItem

Auto-retry countdown and manual retry hint are now independent:
- pendingRetryErrorItem: displays error message with optional hint
- pendingRetryCountdownItem: displays separate countdown line for auto-retry

This ensures both can be shown simultaneously without overriding each other.

* fix: restore RetryCountdownMessage rendering in HistoryItemDisplay

The retry_countdown type should be rendered as a separate message,
not inline in ErrorMessage. This allows auto-retry countdown and
manual retry hint to coexist properly.

* fix(cli): properly commit retry error item to history before clearing

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(cli): remove trailing period from retry hint translations

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

Remove unnecessary period from 'Press Ctrl+Y to retry' translation strings in both en.js and zh.js locales. Also update the corresponding usage in useGeminiStream hook.

* chore(sdk-java): add Eclipse project configuration files

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

Add .project configuration files for client and qwencode modules to support Eclipse IDE development environment.

* feat(cli): add retry countdown hint to error message

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* Revert "chore(sdk-java): add Eclipse project configuration files"

This reverts commit da83b5e571.

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-03-02 17:59:18 +08:00

186 lines
5.5 KiB
TypeScript

/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* Command enum for all available keyboard shortcuts
*/
export enum Command {
// Basic bindings
RETURN = 'return',
ESCAPE = 'escape',
// Cursor movement
HOME = 'home',
END = 'end',
// Text deletion
KILL_LINE_RIGHT = 'killLineRight',
KILL_LINE_LEFT = 'killLineLeft',
CLEAR_INPUT = 'clearInput',
DELETE_WORD_BACKWARD = 'deleteWordBackward',
// Screen control
CLEAR_SCREEN = 'clearScreen',
// History navigation
HISTORY_UP = 'historyUp',
HISTORY_DOWN = 'historyDown',
NAVIGATION_UP = 'navigationUp',
NAVIGATION_DOWN = 'navigationDown',
// Auto-completion
ACCEPT_SUGGESTION = 'acceptSuggestion',
COMPLETION_UP = 'completionUp',
COMPLETION_DOWN = 'completionDown',
// Text input
SUBMIT = 'submit',
NEWLINE = 'newline',
// External tools
OPEN_EXTERNAL_EDITOR = 'openExternalEditor',
PASTE_CLIPBOARD_IMAGE = 'pasteClipboardImage',
// App level bindings
TOGGLE_TOOL_DESCRIPTIONS = 'toggleToolDescriptions',
TOGGLE_IDE_CONTEXT_DETAIL = 'toggleIDEContextDetail',
QUIT = 'quit',
EXIT = 'exit',
SHOW_MORE_LINES = 'showMoreLines',
RETRY_LAST = 'retryLast',
// Shell commands
REVERSE_SEARCH = 'reverseSearch',
SUBMIT_REVERSE_SEARCH = 'submitReverseSearch',
ACCEPT_SUGGESTION_REVERSE_SEARCH = 'acceptSuggestionReverseSearch',
TOGGLE_SHELL_INPUT_FOCUS = 'toggleShellInputFocus',
// Suggestion expansion
EXPAND_SUGGESTION = 'expandSuggestion',
COLLAPSE_SUGGESTION = 'collapseSuggestion',
}
/**
* Data-driven key binding structure for user configuration
*/
export interface KeyBinding {
/** The key name (e.g., 'a', 'return', 'tab', 'escape') */
key?: string;
/** The key sequence (e.g., '\x18' for Ctrl+X) - alternative to key name */
sequence?: string;
/** Control key requirement: true=must be pressed, false=must not be pressed, undefined=ignore */
ctrl?: boolean;
/** Shift key requirement: true=must be pressed, false=must not be pressed, undefined=ignore */
shift?: boolean;
/** Command/meta key requirement: true=must be pressed, false=must not be pressed, undefined=ignore */
command?: boolean;
/** Paste operation requirement: true=must be paste, false=must not be paste, undefined=ignore */
paste?: boolean;
meta?: boolean;
}
/**
* Configuration type mapping commands to their key bindings
*/
export type KeyBindingConfig = {
readonly [C in Command]: readonly KeyBinding[];
};
/**
* Default key binding configuration
* Matches the original hard-coded logic exactly
*/
export const defaultKeyBindings: KeyBindingConfig = {
// Basic bindings
[Command.RETURN]: [{ key: 'return' }],
[Command.ESCAPE]: [{ key: 'escape' }],
// Cursor movement
[Command.HOME]: [{ key: 'a', ctrl: true }],
[Command.END]: [{ key: 'e', ctrl: true }],
// Text deletion
[Command.KILL_LINE_RIGHT]: [{ key: 'k', ctrl: true }],
[Command.KILL_LINE_LEFT]: [{ key: 'u', ctrl: true }],
[Command.CLEAR_INPUT]: [{ key: 'c', ctrl: true }],
// Added command (meta/alt/option) for mac compatibility
[Command.DELETE_WORD_BACKWARD]: [
{ key: 'backspace', ctrl: true },
{ key: 'backspace', command: true },
],
// Screen control
[Command.CLEAR_SCREEN]: [{ key: 'l', ctrl: true }],
// History navigation
[Command.HISTORY_UP]: [{ key: 'p', ctrl: true }],
[Command.HISTORY_DOWN]: [{ key: 'n', ctrl: true }],
[Command.NAVIGATION_UP]: [{ key: 'up' }],
[Command.NAVIGATION_DOWN]: [{ key: 'down' }],
// Auto-completion
[Command.ACCEPT_SUGGESTION]: [{ key: 'tab' }, { key: 'return', ctrl: false }],
// Completion navigation uses only arrow keys
// Ctrl+P/N are reserved for history navigation (HISTORY_UP/DOWN)
[Command.COMPLETION_UP]: [{ key: 'up' }],
[Command.COMPLETION_DOWN]: [{ key: 'down' }],
// Text input
// Must also exclude shift to allow shift+enter for newline
[Command.SUBMIT]: [
{
key: 'return',
ctrl: false,
command: false,
paste: false,
shift: false,
},
],
// Split into multiple data-driven bindings
// Now also includes shift+enter for multi-line input
[Command.NEWLINE]: [
{ key: 'return', ctrl: true },
{ key: 'return', command: true },
{ key: 'return', paste: true },
{ key: 'return', shift: true },
{ key: 'j', ctrl: true },
],
// External tools
[Command.OPEN_EXTERNAL_EDITOR]: [
{ key: 'x', ctrl: true },
{ sequence: '\x18', ctrl: true },
],
[Command.PASTE_CLIPBOARD_IMAGE]:
process.platform === 'win32'
? [
{ key: 'v', command: true },
{ key: 'v', meta: true },
]
: [
{ key: 'v', ctrl: true },
{ key: 'v', command: true },
],
// App level bindings
[Command.TOGGLE_TOOL_DESCRIPTIONS]: [{ key: 't', ctrl: true }],
[Command.TOGGLE_IDE_CONTEXT_DETAIL]: [{ key: 'g', ctrl: true }],
[Command.QUIT]: [{ key: 'c', ctrl: true }],
[Command.EXIT]: [{ key: 'd', ctrl: true }],
[Command.SHOW_MORE_LINES]: [{ key: 's', ctrl: true }],
[Command.RETRY_LAST]: [{ key: 'y', ctrl: true }],
// Shell commands
[Command.REVERSE_SEARCH]: [{ key: 'r', ctrl: true }],
// Note: original logic ONLY checked ctrl=false, ignored meta/shift/paste
[Command.SUBMIT_REVERSE_SEARCH]: [{ key: 'return', ctrl: false }],
[Command.ACCEPT_SUGGESTION_REVERSE_SEARCH]: [{ key: 'tab' }],
[Command.TOGGLE_SHELL_INPUT_FOCUS]: [{ key: 'f', ctrl: true }],
// Suggestion expansion
[Command.EXPAND_SUGGESTION]: [{ key: 'right' }],
[Command.COLLAPSE_SUGGESTION]: [{ key: 'left' }],
};