fix(tui): preserved prompt drafts on esc

Stopped Esc from clearing typed TUI prompt text while preserving double-Esc disarming behavior.\n\nFixes #3869
This commit is contained in:
roboomp
2026-06-30 06:08:17 +00:00
committed by can1357
parent 63b812320d
commit 9aa729c586
3 changed files with 11 additions and 8 deletions
+3
View File
@@ -192,6 +192,9 @@
- Fixed status-line redraws crashing when tool-call arguments contain `BigInt` values.
- Fixed committed native scrollback rows retaining the previous palette after committed theme switches; direct-terminal theme swaps now replay the transcript so scrolling history matches the live viewport without making theme previews destructive.
### Fixed
- Fixed Esc preserving typed TUI prompt drafts instead of clearing unrecoverable input. ([#3869](https://github.com/can1357/oh-my-pi/issues/3869))
## [16.2.7] - 2026-06-30
@@ -387,9 +387,7 @@ export class InputController {
} else if (this.ctx.session.isStreaming) {
this.#handleStreamingEscape();
} else if (this.ctx.editor.getText().trim()) {
// Esc with typed text clears the draft instead of (or before) any double-Esc action
this.ctx.editor.setText("");
this.ctx.ui.requestRender();
// Esc must not destroy an in-progress draft; it only disarms a previous empty-editor Esc.
this.ctx.lastEscapeTime = 0;
this.#clearStreamingEscapeArm();
} else {
@@ -672,7 +672,7 @@ describe("InputController escape behavior", () => {
expect(ctx.showTreeSelector).not.toHaveBeenCalled();
expect(spies.resetDisplay).toHaveBeenCalledTimes(1);
});
it("clears typed editor text on Esc without opening selectors or aborting", () => {
it("preserves typed editor text on Esc without opening selectors or aborting", () => {
const { ctx, editor, spies } = createContext();
const controller = new InputController(ctx);
@@ -680,24 +680,26 @@ describe("InputController escape behavior", () => {
editor.setText("draft message");
editor.onEscape?.();
expect(editor.getText()).toBe("");
expect(spies.requestRender).toHaveBeenCalledTimes(1);
expect(editor.getText()).toBe("draft message");
expect(spies.requestRender).not.toHaveBeenCalled();
expect(ctx.showTreeSelector).not.toHaveBeenCalled();
expect(ctx.showUserMessageSelector).not.toHaveBeenCalled();
expect(spies.resetDisplay).not.toHaveBeenCalled();
expect(spies.abort).not.toHaveBeenCalled();
});
it("does not treat the Esc after a text-clearing Esc as a double-Esc", () => {
it("does not treat the Esc after a text-preserving Esc as a double-Esc", () => {
const { ctx, editor } = createContext();
const controller = new InputController(ctx);
controller.setupKeyHandlers();
editor.onEscape?.(); // empty editor: arms double-Esc timer
editor.setText("draft");
editor.onEscape?.(); // clears text, must also reset the timer
editor.onEscape?.(); // preserves text, must also reset the timer
editor.setText("");
editor.onEscape?.(); // empty again: should only re-arm, not trigger
expect(ctx.showTreeSelector).not.toHaveBeenCalled();
expect(ctx.showUserMessageSelector).not.toHaveBeenCalled();
});
});