diff --git a/src/web/public/terminal-ui.js b/src/web/public/terminal-ui.js index cd6ef453..afc03555 100644 --- a/src/web/public/terminal-ui.js +++ b/src/web/public/terminal-ui.js @@ -6082,19 +6082,24 @@ Object.assign(CodemanApp.prototype, { const session = this.sessions?.get(sessionId); const optOut = !!this.loadAppSettingsFromStorage?.()?.terminalWheelLocalScrollback; const tracking = this.terminal?.modes?.mouseTrackingMode || 'none'; + // The encoding xterm would report in (DEFAULT, SGR, SGR_PIXELS): touch + // forwards to xterm's own tracking only when it is SGR + // (_shouldForwardTouchToApp). + const encoding = this.terminal?._core?.coreMouseService?.activeEncoding || 'unknown'; // xterm's own mode above stays 'none' for a strip mode (the server removes // the DECSETs), so the CLI's real tracking state is reported separately. const cliTracking = session?.cliMouseTracking === true; const baseY = this.terminal?.buffer?.active?.baseY ?? -1; const signature = - `${decision}|${session?.mode}|${session?.cliVersion}|${optOut}|${tracking}|${cliTracking}|${baseY > 0}`; + `${decision}|${session?.mode}|${session?.cliVersion}|${optOut}|` + + `${tracking}|${encoding}|${cliTracking}|${baseY > 0}`; if (!this._scrollRoutingLogged) this._scrollRoutingLogged = new Map(); if (this._scrollRoutingLogged.get(sessionId) === signature) return; this._scrollRoutingLogged.set(sessionId, signature); console.log( `[scroll] ${sessionId} → ${decision} (mode=${session?.mode || '?'}, cliVersion=${session?.cliVersion || 'unknown'}, ` + - `localScrollbackOptOut=${optOut}, mouseTracking=${tracking}, cliMouseTracking=${cliTracking}, ` + - `localScrollbackRows=${baseY})` + `localScrollbackOptOut=${optOut}, mouseTracking=${tracking}, mouseEncoding=${encoding}, ` + + `cliMouseTracking=${cliTracking}, localScrollbackRows=${baseY})` ); }, diff --git a/test/terminal-scroll-routing.test.ts b/test/terminal-scroll-routing.test.ts index 6293a21c..7fd26e64 100644 --- a/test/terminal-scroll-routing.test.ts +++ b/test/terminal-scroll-routing.test.ts @@ -876,6 +876,20 @@ describe('scroll routing diagnostic (issue #205 round 2)', () => { app._logScrollRouting('page-keys'); expect(logs[0]).toContain('cliVersion=unknown'); }); + + it("reports xterm's mouse encoding, the input touch forwarding to native tracking reads", () => { + const { app, logs } = hollowApp({ mode: 'shell' }); + app._logScrollRouting('local-scrollback'); // no readable xterm private + expect(logs[0]).toContain('mouseEncoding=unknown'); + + // An app switching to SGR changes what touch does, so it prints again. + app.terminal.modes.mouseTrackingMode = 'any'; + app.terminal._core = { coreMouseService: { activeEncoding: 'SGR' } }; + app._logScrollRouting('local-scrollback'); + expect(logs).toHaveLength(2); + expect(logs[1]).toContain('mouseTracking=any'); + expect(logs[1]).toContain('mouseEncoding=SGR'); + }); }); describe('touch scrolling when xterm owns native mouse tracking', () => { @@ -898,6 +912,13 @@ describe('touch scrolling when xterm owns native mouse tracking', () => { expect(app._shouldForwardTouchToApp()).toBe(false); }); + it('does not send cell-based SGR reports to an app that asked for SGR pixel coordinates', () => { + const { app } = hollowApp({ mode: 'shell' }); + app.terminal.modes.mouseTrackingMode = 'any'; + app.terminal._core = { coreMouseService: { activeEncoding: 'SGR_PIXELS' } }; + expect(app._shouldForwardTouchToApp()).toBe(false); + }); + it('keeps the explicit local-history preference for touch', () => { const { app } = hollowApp({ mode: 'shell' }); app.terminal.modes.mouseTrackingMode = 'any';