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:
can1357
2026-01-11 22:59:04 +01:00
parent c11b2f9058
commit 8ba0498eb7
7 changed files with 39 additions and 24 deletions
+8
View File
@@ -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
+3
View File
@@ -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");
};
}
+3
View File
@@ -1,6 +1,9 @@
# Changelog
## [Unreleased]
### Fixed
- Fixed Ctrl+Enter sequences to insert new lines in the editor
## [4.3.2] - 2026-01-11
+2
View File
@@ -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)