fix(terminal): merge-time fixes for #498

- _logScrollRouting() reports cliMouseTracking, the gate's new input, in both
  the de-dup signature and the console line (xterm's own mouseTracking stays
  'none' for Claude, so it gave no reason for a no).
- Restore two guard tests the new gate made vacuous: the local-scrollback
  opt-out footgun test and the codex/gemini "no version rescues it" fixtures
  now set cliMouseTracking: true, so removing the opt-out or re-adding codex to
  the gate fails again.
- Update the comments and architecture-invariants lines that still described
  the version-only rule (wheel handler header, gate doc, the false paths of
  _maybePageCliTranscript, "holds a tracking mode on continuously").
- Name both fullscreen switches (CLAUDE_CODE_NO_FLICKER=1 and "tui":
  "fullscreen" in ~/.claude/settings.json) in the code comment, the invariants
  and the two wiki pages.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer
2026-09-28 16:30:40 +02:00
parent 714050fe8a
commit 1f4c390e12
6 changed files with 54 additions and 27 deletions
+15 -3
View File
@@ -47,11 +47,13 @@ function loadTerminalUiHarness() {
}
/** A Claude session whose local buffer holds exactly one screen (baseY 0). */
function hollowClaudeApp(overrides: { cliVersion?: string; rows?: number } = {}) {
function hollowClaudeApp(overrides: { cliVersion?: string; rows?: number; cliMouseTracking?: boolean } = {}) {
const { app, logs } = loadTerminalUiHarness();
const sent: Array<{ id: string; data: string }> = [];
app.activeSessionId = 'sess-1';
app.sessions = new Map([['sess-1', { mode: 'claude', cliVersion: overrides.cliVersion }]]);
app.sessions = new Map([
['sess-1', { mode: 'claude', cliVersion: overrides.cliVersion, cliMouseTracking: overrides.cliMouseTracking }],
]);
app._sendInputEphemeral = (id: string, data: string) => sent.push({ id, data });
app.terminal = {
cols: 80,
@@ -194,7 +196,8 @@ describe('PageUp/PageDown fallback for a hollow local buffer (issue #205 round 2
// "Wheel scrolls local history" ON pins the wheel to a buffer that, for a
// repaint-mode CLI, is empty — a user who flipped it while hunting for a fix
// on 1.11.x would have ended up with a completely dead wheel on 1.12.0.
const { app, sent } = hollowClaudeApp({ cliVersion: '2.1.223' }); // gate would forward…
// Version and tracking both qualify, so the opt-out is the only thing saying no.
const { app, sent } = hollowClaudeApp({ cliVersion: '2.1.223', cliMouseTracking: true }); // gate would forward…
app.loadAppSettingsFromStorage = () => ({ terminalWheelLocalScrollback: true });
expect(app._shouldForwardWheelToApp({ shiftKey: false })).toBe(false); // …but the opt-out wins
@@ -236,10 +239,19 @@ describe('scroll routing diagnostic (issue #205 round 2)', () => {
expect(logs[0]).toContain('cliVersion=2.1.100');
expect(logs[0]).toContain('localScrollbackOptOut=false');
expect(logs[0]).toContain('mouseTracking=none');
// The gate's real tracking input: xterm's own mode above is always 'none'
// for Claude, since the server strips the DECSETs.
expect(logs[0]).toContain('cliMouseTracking=false');
app._logScrollRouting('page-keys'); // a changed route still prints
expect(logs).toHaveLength(2);
expect(logs[1]).toContain('page-keys');
// The CLI turning tracking on changes the gate, so it prints again.
app.sessions.get('sess-1').cliMouseTracking = true;
app._logScrollRouting('page-keys');
expect(logs).toHaveLength(3);
expect(logs[2]).toContain('cliMouseTracking=true');
});
it('reports an unknown CLI version, the false-path that disables forwarding', () => {
+4 -2
View File
@@ -683,10 +683,12 @@ describe('terminal touch tap mouse guard', () => {
// (the codex transcript lives there — inline viewport, no in-app pager) sat unused.
app.sessions = new Map([['sess-1', { mode: 'codex' }]]);
expect(app._shouldForwardWheelToApp({ shiftKey: false })).toBe(false);
app.sessions = new Map([['sess-1', { mode: 'codex', cliVersion: '9.9.9' }]]); // no version rescues it
// Tracking on and a high version, so only the mode check can say no: without
// them the gate is false for claude too and this would pin nothing.
app.sessions = new Map([['sess-1', { mode: 'codex', cliVersion: '9.9.9', cliMouseTracking: true }]]); // no version rescues it
expect(app._shouldForwardWheelToApp({ shiftKey: false })).toBe(false);
app.sessions = new Map([['sess-1', { mode: 'gemini', cliVersion: '9.9.9' }]]); // unverified TUI
app.sessions = new Map([['sess-1', { mode: 'gemini', cliVersion: '9.9.9', cliMouseTracking: true }]]); // unverified TUI
expect(app._shouldForwardWheelToApp({ shiftKey: false })).toBe(false);
});