From 41e838852bcf1df8572e9aa75dd4032730041b8c Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Tue, 10 Feb 2026 13:41:50 -0500 Subject: [PATCH 1/3] add failing tests for Ctrl+W/Alt+Backspace not emitting INPUT event InputRenderable.deleteWordBackward() (inherited from Textarea) mutates the buffer but never emits INPUT, so consumers like DialogSelect never see the value change. --- packages/core/src/renderables/Input.test.ts | 36 +++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/packages/core/src/renderables/Input.test.ts b/packages/core/src/renderables/Input.test.ts index dd98deeaa..edcedf49c 100644 --- a/packages/core/src/renderables/Input.test.ts +++ b/packages/core/src/renderables/Input.test.ts @@ -173,6 +173,42 @@ describe("InputRenderable", () => { expect(input.value).toBe("hel") }) + it("should emit INPUT event on Ctrl+W (delete-word-backward)", () => { + const { input } = createInputRenderable({ + value: "hello world", + }) + + input.focus() + + const inputValues: string[] = [] + input.on(InputRenderableEvents.INPUT, (value: string) => { + inputValues.push(value) + }) + + // Ctrl+W should delete "world" and emit INPUT with updated value + mockInput.pressKey("w", { ctrl: true }) + expect(input.value).toBe("hello ") + expect(inputValues).toEqual(["hello "]) + }) + + it("should emit INPUT event on Alt+Backspace (delete-word-backward)", () => { + const { input } = createInputRenderable({ + value: "foo bar baz", + }) + + input.focus() + + const inputValues: string[] = [] + input.on(InputRenderableEvents.INPUT, (value: string) => { + inputValues.push(value) + }) + + // Alt+Backspace is also bound to delete-word-backward + mockInput.pressBackspace({ meta: true }) + expect(input.value).toBe("foo bar ") + expect(inputValues).toEqual(["foo bar "]) + }) + it("should handle delete correctly", () => { const { input } = createInputRenderable({ value: "hello", From c1a3f9a51de4a270158c4994d15b82072c509313 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Tue, 10 Feb 2026 13:42:19 -0500 Subject: [PATCH 2/3] fix: emit INPUT event for all mutating edit actions in InputRenderable Override deleteWordBackward, deleteWordForward, deleteToLineStart, deleteToLineEnd, undo, and redo to emit INPUT after the super call, matching the existing pattern for deleteCharBackward and deleteChar. Fixes Ctrl+W / Alt+Backspace not updating search results in TUI dialog filters (e.g. model picker, skill picker). --- packages/core/src/renderables/Input.ts | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/packages/core/src/renderables/Input.ts b/packages/core/src/renderables/Input.ts index 9161308eb..6f89b2724 100644 --- a/packages/core/src/renderables/Input.ts +++ b/packages/core/src/renderables/Input.ts @@ -167,6 +167,42 @@ export class InputRenderable extends TextareaRenderable { return result } + public override deleteWordBackward(): boolean { + const result = super.deleteWordBackward() + this.emit(InputRenderableEvents.INPUT, this.plainText) + return result + } + + public override deleteWordForward(): boolean { + const result = super.deleteWordForward() + this.emit(InputRenderableEvents.INPUT, this.plainText) + return result + } + + public override deleteToLineStart(): boolean { + const result = super.deleteToLineStart() + this.emit(InputRenderableEvents.INPUT, this.plainText) + return result + } + + public override deleteToLineEnd(): boolean { + const result = super.deleteToLineEnd() + this.emit(InputRenderableEvents.INPUT, this.plainText) + return result + } + + public override undo(): boolean { + const result = super.undo() + this.emit(InputRenderableEvents.INPUT, this.plainText) + return result + } + + public override redo(): boolean { + const result = super.redo() + this.emit(InputRenderableEvents.INPUT, this.plainText) + return result + } + public deleteCharacter(direction: "backward" | "forward"): void { if (direction === "backward") { this.deleteCharBackward() From 7ef9699cf66b4e55cdf5a8f9d0ef2f55917e1db4 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Tue, 10 Feb 2026 14:51:47 -0500 Subject: [PATCH 3/3] add deleteLine override and test to InputRenderable Covers the Ctrl+Shift+D (delete-line) action that was also missing INPUT event emission. --- packages/core/src/renderables/Input.test.ts | 17 +++++++++++++++++ packages/core/src/renderables/Input.ts | 6 ++++++ 2 files changed, 23 insertions(+) diff --git a/packages/core/src/renderables/Input.test.ts b/packages/core/src/renderables/Input.test.ts index edcedf49c..5a969d839 100644 --- a/packages/core/src/renderables/Input.test.ts +++ b/packages/core/src/renderables/Input.test.ts @@ -209,6 +209,23 @@ describe("InputRenderable", () => { expect(inputValues).toEqual(["foo bar "]) }) + it("should emit INPUT event on deleteLine()", () => { + const { input } = createInputRenderable({ + value: "hello world", + }) + + input.focus() + + const inputValues: string[] = [] + input.on(InputRenderableEvents.INPUT, (value: string) => { + inputValues.push(value) + }) + + input.deleteLine() + expect(input.value).toBe("") + expect(inputValues).toEqual([""]) + }) + it("should handle delete correctly", () => { const { input } = createInputRenderable({ value: "hello", diff --git a/packages/core/src/renderables/Input.ts b/packages/core/src/renderables/Input.ts index 6f89b2724..099d3c16b 100644 --- a/packages/core/src/renderables/Input.ts +++ b/packages/core/src/renderables/Input.ts @@ -167,6 +167,12 @@ export class InputRenderable extends TextareaRenderable { return result } + public override deleteLine(): boolean { + const result = super.deleteLine() + this.emit(InputRenderableEvents.INPUT, this.plainText) + return result + } + public override deleteWordBackward(): boolean { const result = super.deleteWordBackward() this.emit(InputRenderableEvents.INPUT, this.plainText)