diff --git a/packages/tui/CHANGELOG.md b/packages/tui/CHANGELOG.md index b654906cd..713c29ce8 100644 --- a/packages/tui/CHANGELOG.md +++ b/packages/tui/CHANGELOG.md @@ -17,6 +17,7 @@ ### Fixed - Fixed overlay compositing dropping `CURSOR_MARKER` when an overlay ends directly left of the cursor, which lost the hardware cursor position +- Fixed focused fullscreen overlays blocking mouse-wheel scrolling of the transcript. Wheel events that no component handles now scroll the transcript instead of reaching the focused overlay's `handleInput` as raw input; overlays that scroll with the wheel must implement `handleMouse` ([#10769](https://github.com/earendil-works/pi/issues/10769)) - Fixed fragmented terminal color replies leaking hex values into the prompt and triggering the external editor via their BEL terminator (related reports: [#10256](https://github.com/earendil-works/pi/issues/10256), [#10657](https://github.com/earendil-works/pi/issues/10657)) ## [1.1.0] - 2026-10-07 diff --git a/packages/tui/src/tui-alt-screen.ts b/packages/tui/src/tui-alt-screen.ts index a0ffeb89a..e92f5c953 100644 --- a/packages/tui/src/tui-alt-screen.ts +++ b/packages/tui/src/tui-alt-screen.ts @@ -715,7 +715,6 @@ export class TuiAltScreen extends TuiBase implements ViewportTUI { if (this.applyMouseDispatchResult(event, result)) this.requestRender(); return { consume: true }; } - if (this.shouldDeferViewportInputToOverlay()) return undefined; this.routeWheel(wheelEvent, wheelDelta); return { consume: true }; } diff --git a/packages/tui/test/tui-alt-screen.test.ts b/packages/tui/test/tui-alt-screen.test.ts index 34a9b2e0b..25772b54c 100644 --- a/packages/tui/test/tui-alt-screen.test.ts +++ b/packages/tui/test/tui-alt-screen.test.ts @@ -1862,6 +1862,26 @@ describe("TuiAltScreen", () => { tui.stop(); }); + // #10769 + it("scrolls the transcript on wheel input a focused overlay does not handle", async () => { + const terminal = new VirtualTerminal(20, 6); + const tui = new TuiAltScreen(terminal); + tui.addChild(new Text(Array.from({ length: 12 }, (_, index) => `line ${index + 1}`).join("\n"), 0, 0)); + const overlay = new InputOverlay(); + tui.start(); + await terminal.waitForRender(); + const topBefore = tui.viewportTop; + tui.showOverlay(overlay, { anchor: "top-left", width: 10 }); + await terminal.waitForRender(); + assert.strictEqual(overlay.focused, true); + + terminal.sendInput("\x1b[<64;3;1M"); + await terminal.waitForRender(); + assert.ok(tui.viewportTop < topBefore); + assert.deepStrictEqual(overlay.inputs, []); + tui.stop(); + }); + it("focuses and captures drag gestures for mouse-aware components", async () => { const terminal = new VirtualTerminal(20, 2); const tui = new TuiAltScreen(terminal); @@ -2001,7 +2021,7 @@ describe("TuiAltScreen", () => { } }); - it("gives wheel and viewport keys to a focused overlay", async () => { + it("gives viewport keys to a focused overlay", async () => { const terminal = new VirtualTerminal(20, 6); const tui = new TuiAltScreen(terminal); tui.addChild(new Text(Array.from({ length: 12 }, (_, index) => `line ${index + 1}`).join("\n"), 0, 0)); @@ -2013,8 +2033,7 @@ describe("TuiAltScreen", () => { await terminal.waitForRender(); assert.strictEqual(overlay.focused, true); - const wheel = "\x1b[<64;10;3M"; - const keys = ["\x1b[5~", "\x1b[6~", "\x1bOH", "\x1bOF", wheel]; + const keys = ["\x1b[5~", "\x1b[6~", "\x1bOH", "\x1bOF"]; for (const key of keys) terminal.sendInput(key); await terminal.waitForRender();