fix(coding-agent): fixed keybindings and input handling for TUI editor and Cursor provider
- Changed temporary model selector keybinding from Ctrl+Y to Alt+P to avoid conflicts. - Fixed Alt+Enter to insert newline when not streaming instead of submitting. - Fixed Cursor provider to avoid advertising Edit tool, using full-file Write instead. - Added Ctrl+Enter support for inserting newlines in TUI editor using Kitty and legacy terminal formats. - Removed duplicate Ctrl+Y handler and refactored input controller to use trimmedText variable.
This commit is contained in:
@@ -1,15 +1,23 @@
|
||||
# Changelog
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Added grouped display for consecutive Read tool calls, showing multiple file reads in a compact tree view
|
||||
|
||||
### Changed
|
||||
|
||||
- Changed temporary model selector keybinding from Ctrl+Y to Alt+P
|
||||
- Changed expand hint text from "Ctrl+O to expand" to "Ctrl+O for more"
|
||||
- Changed Read tool result display to hide content by default, showing only file path and status
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed Alt+Enter to insert a newline when not streaming, instead of submitting the message
|
||||
- Fixed Alt+Enter inserting a new line when not streaming instead of submitting a message
|
||||
- Fixed Cursor provider to avoid advertising the Edit tool, relying on full-file Write operations instead
|
||||
|
||||
## [4.3.2] - 2026-01-11
|
||||
### Changed
|
||||
|
||||
|
||||
@@ -855,6 +855,9 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
||||
toolRegistry.set(tool.name, wrapToolWithExtensions(tool, extensionRunner));
|
||||
}
|
||||
}
|
||||
if (model?.provider === "cursor") {
|
||||
toolRegistry.delete("edit");
|
||||
}
|
||||
time("combineTools");
|
||||
|
||||
let cursorEventEmitter: ((event: AgentEvent) => void) | undefined;
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
isCtrlP,
|
||||
isCtrlT,
|
||||
isCtrlV,
|
||||
isCtrlY,
|
||||
isCtrlZ,
|
||||
isEscape,
|
||||
isShiftCtrlP,
|
||||
@@ -36,7 +35,7 @@ export class CustomEditor extends Editor {
|
||||
public onCtrlZ?: () => void;
|
||||
public onQuestionMark?: () => void;
|
||||
public onCapsLock?: () => void;
|
||||
public onCtrlY?: () => void;
|
||||
public onAltP?: () => void;
|
||||
/** Called when Ctrl+V is pressed. Returns true if handled (image found), false to fall through to text paste. */
|
||||
public onCtrlV?: () => Promise<boolean>;
|
||||
/** Called when Alt+Up is pressed (dequeue keybinding). */
|
||||
@@ -84,9 +83,9 @@ export class CustomEditor extends Editor {
|
||||
return;
|
||||
}
|
||||
|
||||
// Intercept Ctrl+Y for voice input
|
||||
if (isCtrlY(data) && this.onCtrlY) {
|
||||
this.onCtrlY();
|
||||
// Intercept Alt+P for quick model switching
|
||||
if (matchesKey(data, "alt+p") && this.onAltP) {
|
||||
this.onAltP();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -102,12 +101,6 @@ export class CustomEditor extends Editor {
|
||||
return;
|
||||
}
|
||||
|
||||
// Intercept Ctrl+Y for role-based model cycling
|
||||
if (isCtrlY(data) && this.onCtrlY) {
|
||||
this.onCtrlY();
|
||||
return;
|
||||
}
|
||||
|
||||
// Intercept Ctrl+L for model selector
|
||||
if (isCtrlL(data) && this.onCtrlL) {
|
||||
this.onCtrlL();
|
||||
|
||||
@@ -334,7 +334,7 @@ export class CommandController {
|
||||
| \`Shift+Tab\` | Cycle thinking level |
|
||||
| \`Ctrl+P\` | Cycle role models (slow/default/smol) |
|
||||
| \`Shift+Ctrl+P\` | Cycle role models (temporary) |
|
||||
| \`Ctrl+Y\` | Select model (temporary) |
|
||||
| \`Alt+P\` | Select model (temporary) |
|
||||
| \`Ctrl+L\` | Select model (set roles) |
|
||||
| \`Ctrl+R\` | Search prompt history |
|
||||
| \`Ctrl+O\` | Toggle tool output expansion |
|
||||
|
||||
@@ -60,7 +60,7 @@ export class InputController {
|
||||
this.ctx.editor.onShiftTab = () => this.cycleThinkingLevel();
|
||||
this.ctx.editor.onCtrlP = () => this.cycleRoleModel();
|
||||
this.ctx.editor.onShiftCtrlP = () => this.cycleRoleModel({ temporary: true });
|
||||
this.ctx.editor.onCtrlY = () => this.ctx.showModelSelector({ temporaryOnly: true });
|
||||
this.ctx.editor.onAltP = () => this.ctx.showModelSelector({ temporaryOnly: true });
|
||||
|
||||
// Global debug handler on TUI (works regardless of focus)
|
||||
this.ctx.ui.onDebug = () => this.ctx.handleDebugCommand();
|
||||
@@ -85,28 +85,34 @@ export class InputController {
|
||||
};
|
||||
|
||||
this.ctx.editor.onAltEnter = async (text: string) => {
|
||||
text = text.trim();
|
||||
if (!text) return;
|
||||
const trimmedText = text.trim();
|
||||
|
||||
// Queue follow-up messages while compaction is running
|
||||
if (this.ctx.session.isCompacting) {
|
||||
this.ctx.queueCompactionMessage(text, "followUp");
|
||||
if (!trimmedText) {
|
||||
this.ctx.editor.handleInput("\n");
|
||||
return;
|
||||
}
|
||||
this.ctx.queueCompactionMessage(trimmedText, "followUp");
|
||||
return;
|
||||
}
|
||||
|
||||
// Alt+Enter queues a follow-up message (waits until agent finishes)
|
||||
// This handles extension commands (execute immediately), prompt template expansion, and queueing
|
||||
// Alt+Enter queues a follow-up message while streaming
|
||||
if (this.ctx.session.isStreaming) {
|
||||
this.ctx.editor.addToHistory(text);
|
||||
if (!trimmedText) {
|
||||
this.ctx.editor.handleInput("\n");
|
||||
return;
|
||||
}
|
||||
this.ctx.editor.addToHistory(trimmedText);
|
||||
this.ctx.editor.setText("");
|
||||
await this.ctx.session.prompt(text, { streamingBehavior: "followUp" });
|
||||
await this.ctx.session.prompt(trimmedText, { streamingBehavior: "followUp" });
|
||||
this.ctx.updatePendingMessagesDisplay();
|
||||
this.ctx.ui.requestRender();
|
||||
return;
|
||||
}
|
||||
// If not streaming, Alt+Enter acts like regular Enter (trigger onSubmit)
|
||||
else if (this.ctx.editor.onSubmit) {
|
||||
this.ctx.editor.onSubmit(text);
|
||||
}
|
||||
|
||||
// Default behavior: insert a new line
|
||||
this.ctx.editor.handleInput("\n");
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
# Changelog
|
||||
|
||||
## [Unreleased]
|
||||
### Fixed
|
||||
|
||||
- Fixed Ctrl+Enter sequences to insert new lines in the editor
|
||||
|
||||
## [4.3.2] - 2026-01-11
|
||||
|
||||
|
||||
@@ -708,6 +708,8 @@ export class Editor implements Component {
|
||||
// New line shortcuts (but not plain LF/CR which should be submit)
|
||||
else if (
|
||||
(data.charCodeAt(0) === 10 && data.length > 1) || // Ctrl+Enter with modifiers
|
||||
data === "\x1b[13;5u" || // Ctrl+Enter (Kitty protocol)
|
||||
data === "\x1b[27;5;13~" || // Ctrl+Enter (legacy format)
|
||||
data === "\x1b\r" || // Option+Enter in some terminals (legacy)
|
||||
data === "\x1b[13;2~" || // Shift+Enter in some terminals (legacy format)
|
||||
isShiftEnter(data) || // Shift+Enter (Kitty protocol, handles lock bits)
|
||||
|
||||
Reference in New Issue
Block a user