diff --git a/packages/tui/src/tui-alt-screen.ts b/packages/tui/src/tui-alt-screen.ts index 08dd88218..28a9e37a4 100644 --- a/packages/tui/src/tui-alt-screen.ts +++ b/packages/tui/src/tui-alt-screen.ts @@ -72,6 +72,7 @@ const END_SYNCHRONIZED_OUTPUT = "\x1b[?2026l"; const OSC133_ZONE_PREFIX = /^(?:\x1b\]133;[ABC](?:\x07|\x1b\\))+/; const OSC133_PROMPT_START = /^\x1b\]133;A(?:\x07|\x1b\\)/; const PAGE_SCROLL_OVERLAP = 4; +const ALT_WHEEL_SCROLL_MULTIPLIER = 5; const MAX_CACHED_OFFSCREEN_KITTY_IMAGES = 16; const MAX_CACHED_OFFSCREEN_KITTY_TRANSMISSION_BYTES = 32 * 1024 * 1024; const MAX_CACHED_OFFSCREEN_KITTY_DECODED_BYTES = 64 * 1024 * 1024; @@ -680,7 +681,7 @@ export class TuiAltScreen extends TuiBase implements ViewportTUI { const wheelEvent = this.parseWheelEvent(data); if (wheelEvent) { const event = this.createMouseEvent("wheel", wheelEvent.button, wheelEvent.x, wheelEvent.y, { - wheelDelta: wheelEvent.direction * this.wheelScrollLines, + wheelDelta: wheelEvent.direction * this.getWheelScrollLines(wheelEvent.button), }); const overlay = this.dispatchMouseToOverlay(event); const result = overlay.result ?? (overlay.hit ? undefined : this.dispatchMouseToLayout(event)); @@ -964,8 +965,13 @@ export class TuiAltScreen extends TuiBase implements ViewportTUI { return undefined; } + private getWheelScrollLines(button: number): number { + // SGR mouse button codes use bit 3 (value 8) for the Alt modifier. + return (button & 8) !== 0 ? this.wheelScrollLines * ALT_WHEEL_SCROLL_MULTIPLIER : this.wheelScrollLines; + } + private routeWheel(event: WheelEvent): void { - let remaining = event.direction * this.wheelScrollLines; + let remaining = event.direction * this.getWheelScrollLines(event.button); const seen = new Set(); for (const scrollView of this.currentLayout ? getScrollViewsAt(this.currentLayout, event.x, event.y) : []) { seen.add(scrollView); diff --git a/packages/tui/test/tui-alt-screen.test.ts b/packages/tui/test/tui-alt-screen.test.ts index 3e4d72bc5..feb2ff137 100644 --- a/packages/tui/test/tui-alt-screen.test.ts +++ b/packages/tui/test/tui-alt-screen.test.ts @@ -273,6 +273,22 @@ describe("TuiAltScreen", () => { tui.stop(); }); + it("scrolls faster while Alt is held during wheel input", async () => { + const terminal = new VirtualTerminal(20, 4); + const tui = new TuiAltScreen(terminal); + const text = new Text(Array.from({ length: 12 }, (_, index) => `line ${index + 1}`).join("\n"), 0, 0); + tui.addChild(text); + tui.start(); + await terminal.waitForRender(); + assert.strictEqual(tui.viewportTop, 8); + + // Alt modifier sets bit 8 on the wheel button (72 = 64 + 8). + terminal.sendInput("\x1b[<72;1;1M"); + await terminal.waitForRender(); + assert.strictEqual(tui.viewportTop, 3); + tui.stop(); + }); + it("does not vertically redispatch misses through horizontal layout containers", async () => { const terminal = new VirtualTerminal(20, 2); const tui = new TuiAltScreen(terminal);