diff --git a/src/frontend/terminal/Terminal.tsx b/src/frontend/terminal/Terminal.tsx index 5f2150f..4cf00b6 100644 --- a/src/frontend/terminal/Terminal.tsx +++ b/src/frontend/terminal/Terminal.tsx @@ -3095,7 +3095,12 @@ const TerminalInner = forwardRef( } return false; } - if (wheelHasNothingToScroll(terminal.buffer.active)) { + if ( + wheelHasNothingToScroll( + terminal.buffer.active, + terminal.modes.mouseTrackingMode, + ) + ) { ev.preventDefault(); return false; } diff --git a/src/frontend/terminal/terminal-wheel.ts b/src/frontend/terminal/terminal-wheel.ts index e23fd72..15f9f67 100644 --- a/src/frontend/terminal/terminal-wheel.ts +++ b/src/frontend/terminal/terminal-wheel.ts @@ -25,9 +25,18 @@ type WheelBuffer = { type: "normal" | "alternate"; baseY: number }; * A plain wheel on a normal buffer with nothing scrolled off yet. xterm turns * that into an arrow key, which walks shell history, so it is dropped. The * alternate screen keeps the arrow keys: that is how less and vim scroll. + * When the app has mouse reporting on, xterm sends the wheel to the app + * instead (tmux attached on the normal screen does this), so it is kept. */ -export function wheelHasNothingToScroll(buffer: WheelBuffer): boolean { - return buffer.type === "normal" && buffer.baseY === 0; +export function wheelHasNothingToScroll( + buffer: WheelBuffer, + mouseTrackingMode: string, +): boolean { + return ( + mouseTrackingMode === "none" && + buffer.type === "normal" && + buffer.baseY === 0 + ); } /** Pixels per scrolled line, the same step xterm uses for one wheel line. */ diff --git a/tests/frontend/terminal/terminal-wheel.test.ts b/tests/frontend/terminal/terminal-wheel.test.ts index f542b37..77d4754 100644 --- a/tests/frontend/terminal/terminal-wheel.test.ts +++ b/tests/frontend/terminal/terminal-wheel.test.ts @@ -42,14 +42,26 @@ describe("terminalWheelAction", () => { describe("wheelHasNothingToScroll", () => { it("drops the wheel on a normal buffer with no scrollback yet", () => { - expect(wheelHasNothingToScroll({ type: "normal", baseY: 0 })).toBe(true); - expect(wheelHasNothingToScroll({ type: "normal", baseY: 12 })).toBe(false); + expect(wheelHasNothingToScroll({ type: "normal", baseY: 0 }, "none")).toBe( + true, + ); + expect(wheelHasNothingToScroll({ type: "normal", baseY: 12 }, "none")).toBe( + false, + ); }); it("leaves the alternate screen alone so less and vim still scroll", () => { - expect(wheelHasNothingToScroll({ type: "alternate", baseY: 0 })).toBe( - false, - ); + expect( + wheelHasNothingToScroll({ type: "alternate", baseY: 0 }, "none"), + ).toBe(false); + }); + + it("keeps the wheel when the app reports the mouse (tmux on the normal screen)", () => { + for (const mode of ["x10", "vt200", "drag", "any"]) { + expect(wheelHasNothingToScroll({ type: "normal", baseY: 0 }, mode)).toBe( + false, + ); + } }); });