mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-17 07:03:25 +00:00
Add Ctrl+D to exit when editor is empty
- Add isCtrlD helper to keys.ts - CustomEditor intercepts Ctrl+D and only triggers callback when editor is empty - Single Ctrl+D with empty input exits immediately - Update CHANGELOG to frame as feature (Kitty protocol support) not fix
This commit is contained in:
parent
c3c2bffc68
commit
727a7ab018
5 changed files with 31 additions and 3 deletions
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
### Fixed
|
### Added
|
||||||
|
|
||||||
- **Shift+Enter for newlines**: Fixed Shift+Enter not working for newlines in Ghostty, Kitty, WezTerm, and other terminals supporting the Kitty keyboard protocol. Also fixed Alt+Enter, Shift+Tab, and all Ctrl+key combinations (Ctrl+A/C/E/K/O/P/T/U/W). (by [@kim0](https://github.com/kim0))
|
- **Kitty keyboard protocol support**: Added support for the Kitty keyboard protocol, enabling Shift+Enter, Alt+Enter, Shift+Tab, Ctrl+D to exit, and all Ctrl+key combinations to work in Ghostty, Kitty, WezTerm, and other modern terminals. (by [@kim0](https://github.com/kim0))
|
||||||
|
|
||||||
## [0.23.4] - 2025-12-18
|
## [0.23.4] - 2025-12-18
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Editor, isCtrlC, isCtrlO, isCtrlP, isCtrlT, isShiftTab } from "@mariozechner/pi-tui";
|
import { Editor, isCtrlC, isCtrlD, isCtrlO, isCtrlP, isCtrlT, isShiftTab } from "@mariozechner/pi-tui";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom editor that handles Escape and Ctrl+C keys for coding-agent
|
* Custom editor that handles Escape and Ctrl+C keys for coding-agent
|
||||||
|
|
@ -6,6 +6,7 @@ import { Editor, isCtrlC, isCtrlO, isCtrlP, isCtrlT, isShiftTab } from "@marioze
|
||||||
export class CustomEditor extends Editor {
|
export class CustomEditor extends Editor {
|
||||||
public onEscape?: () => void;
|
public onEscape?: () => void;
|
||||||
public onCtrlC?: () => void;
|
public onCtrlC?: () => void;
|
||||||
|
public onCtrlD?: () => void;
|
||||||
public onShiftTab?: () => void;
|
public onShiftTab?: () => void;
|
||||||
public onCtrlP?: () => void;
|
public onCtrlP?: () => void;
|
||||||
public onCtrlO?: () => void;
|
public onCtrlO?: () => void;
|
||||||
|
|
@ -49,6 +50,15 @@ export class CustomEditor extends Editor {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Intercept Ctrl+D (only when editor is empty)
|
||||||
|
if (isCtrlD(data)) {
|
||||||
|
if (this.getText().length === 0 && this.onCtrlD) {
|
||||||
|
this.onCtrlD();
|
||||||
|
}
|
||||||
|
// Always consume Ctrl+D (don't pass to parent)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Pass to parent for normal handling
|
// Pass to parent for normal handling
|
||||||
super.handleInput(data);
|
super.handleInput(data);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -562,6 +562,7 @@ export class InteractiveMode {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.editor.onCtrlC = () => this.handleCtrlC();
|
this.editor.onCtrlC = () => this.handleCtrlC();
|
||||||
|
this.editor.onCtrlD = () => this.handleCtrlD();
|
||||||
this.editor.onShiftTab = () => this.cycleThinkingLevel();
|
this.editor.onShiftTab = () => this.cycleThinkingLevel();
|
||||||
this.editor.onCtrlP = () => this.cycleModel();
|
this.editor.onCtrlP = () => this.cycleModel();
|
||||||
this.editor.onCtrlO = () => this.toggleToolOutputExpansion();
|
this.editor.onCtrlO = () => this.toggleToolOutputExpansion();
|
||||||
|
|
@ -1128,6 +1129,12 @@ export class InteractiveMode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private handleCtrlD(): void {
|
||||||
|
// Only called when editor is empty (enforced by CustomEditor)
|
||||||
|
this.stop();
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
private updateEditorBorderColor(): void {
|
private updateEditorBorderColor(): void {
|
||||||
if (this.isBashMode) {
|
if (this.isBashMode) {
|
||||||
this.editor.borderColor = theme.getBashModeBorderColor();
|
this.editor.borderColor = theme.getBashModeBorderColor();
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ export {
|
||||||
isAltBackspace,
|
isAltBackspace,
|
||||||
isCtrlA,
|
isCtrlA,
|
||||||
isCtrlC,
|
isCtrlC,
|
||||||
|
isCtrlD,
|
||||||
isCtrlE,
|
isCtrlE,
|
||||||
isCtrlK,
|
isCtrlK,
|
||||||
isCtrlO,
|
isCtrlO,
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ const CODEPOINTS = {
|
||||||
// Letters (lowercase ASCII)
|
// Letters (lowercase ASCII)
|
||||||
a: 97,
|
a: 97,
|
||||||
c: 99,
|
c: 99,
|
||||||
|
d: 100,
|
||||||
e: 101,
|
e: 101,
|
||||||
k: 107,
|
k: 107,
|
||||||
o: 111,
|
o: 111,
|
||||||
|
|
@ -52,6 +53,7 @@ export const Keys = {
|
||||||
// Ctrl+<letter> combinations
|
// Ctrl+<letter> combinations
|
||||||
CTRL_A: kittySequence(CODEPOINTS.a, MODIFIERS.ctrl),
|
CTRL_A: kittySequence(CODEPOINTS.a, MODIFIERS.ctrl),
|
||||||
CTRL_C: kittySequence(CODEPOINTS.c, MODIFIERS.ctrl),
|
CTRL_C: kittySequence(CODEPOINTS.c, MODIFIERS.ctrl),
|
||||||
|
CTRL_D: kittySequence(CODEPOINTS.d, MODIFIERS.ctrl),
|
||||||
CTRL_E: kittySequence(CODEPOINTS.e, MODIFIERS.ctrl),
|
CTRL_E: kittySequence(CODEPOINTS.e, MODIFIERS.ctrl),
|
||||||
CTRL_K: kittySequence(CODEPOINTS.k, MODIFIERS.ctrl),
|
CTRL_K: kittySequence(CODEPOINTS.k, MODIFIERS.ctrl),
|
||||||
CTRL_O: kittySequence(CODEPOINTS.o, MODIFIERS.ctrl),
|
CTRL_O: kittySequence(CODEPOINTS.o, MODIFIERS.ctrl),
|
||||||
|
|
@ -97,6 +99,7 @@ export function isKittyKey(data: string, codepoint: number, modifier: number): b
|
||||||
const RAW = {
|
const RAW = {
|
||||||
CTRL_A: "\x01",
|
CTRL_A: "\x01",
|
||||||
CTRL_C: "\x03",
|
CTRL_C: "\x03",
|
||||||
|
CTRL_D: "\x04",
|
||||||
CTRL_E: "\x05",
|
CTRL_E: "\x05",
|
||||||
CTRL_K: "\x0b",
|
CTRL_K: "\x0b",
|
||||||
CTRL_O: "\x0f",
|
CTRL_O: "\x0f",
|
||||||
|
|
@ -122,6 +125,13 @@ export function isCtrlC(data: string): boolean {
|
||||||
return data === RAW.CTRL_C || data === Keys.CTRL_C;
|
return data === RAW.CTRL_C || data === Keys.CTRL_C;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if input matches Ctrl+D (raw byte or Kitty protocol).
|
||||||
|
*/
|
||||||
|
export function isCtrlD(data: string): boolean {
|
||||||
|
return data === RAW.CTRL_D || data === Keys.CTRL_D;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if input matches Ctrl+E (raw byte or Kitty protocol).
|
* Check if input matches Ctrl+E (raw byte or Kitty protocol).
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue