mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 12:39:42 +02:00
fix(terminal): only forward scroll to Claude while it tracks the mouse
Claude 2.1.280 renders inline by default: no alt screen, no mouse tracking, transcript in real scrollback. The version-only gate still sent every wheel tick and touch swipe as SGR reports, which Claude ignores, so scrolling a Claude session was dead while codex (routed locally) worked. Gate forwarding on the server-recorded cliMouseTracking flag, which fullscreen mode (CLAUDE_CODE_NO_FLICKER=1) sets.
This commit is contained in:
@@ -217,7 +217,7 @@ Further detail: the `<prefix>: <title>` form (`w3-myapp: fix the login redirect`
|
||||
|
||||
⚠️ **What the full strip removes, it must REMEMBER.** Stripping the mouse DECSETs means xterm's `modes.mouseTrackingMode` is permanently `'none'` for those modes, so the browser hand-encodes click reports to compensate (`_sendSyntheticSgrTap`). With no state to consult it did that on EVERY click, which delivered mouse reports to programs that never asked for them: the same pane runs a plain shell whenever the CLI has exited or a `shell` was started inside a claude-mode session, and a shell prints the report as literal text (`[<0;88;20M`), garbling the next line typed. `_recordStrippedMouseMode()` therefore records each stripped sequence as it goes and publishes `cliMouseTracking` through `toState()`, and `_shouldReportMouseToCli()` requires it. ⚠️ Only the TRACKING modes count (1000/1001/1002/1003): 1005/1006 select an ENCODING and 1007 is alt-scroll, and counting those would put the stray reports straight back. ⚠️ The change broadcasts IMMEDIATELY rather than through `broadcastSessionStateDebounced`, because the flag flips when a dialog opens and the user can click that dialog inside the 500ms debounce window. Measured on a live claude 2.x: the CLI holds a tracking mode on continuously (so clicks keep being reported exactly as before), while a bash prompt in the same stripped mode reports nothing. Fails toward silence: after a server restart the flag is false until the CLI re-emits, which tmux does at client attach.
|
||||
|
||||
**Only claude ≥ 2.1.187 forwards the wheel; every other mode scrolls local scrollback** (#227 follow-up, `terminal-ui.js:_shouldForwardWheelToApp`). Codex was in the forward list until a reporter hit a completely dead wheel in codex tabs while the scrollbar drag worked. Measured against codex-cli 0.147.0 in a bare tmux: it never enables mouse tracking (`mouse_any_flag=0`) and SGR wheel reports fed to its PTY change nothing on screen, because it runs an INLINE viewport (`alternate_on=0`) and pushes its transcript into the terminal's own scrollback (tmux `history_size` grows) instead of paging in-app. So for codex, local scrollback IS the transcript and forwarding swallowed every tick. ⚠️ "The TUI is a strip mode" is NOT evidence that it consumes wheel reports — verify with a real `\x1b[<64;c;rM` write into a live pane before adding a mode here. Hand-encoded SGR TAPS are gated by `_shouldReportMouseToCli()` (strip mode AND the server-observed `cliMouseTracking` flag, recorded by `_recordStrippedMouseMode` in session.ts as it strips): codex never enables mouse tracking, so since #325 no tap report is sent there at all — click-to-position was already a measured no-op in codex, and a pane that has fallen back to a shell no longer receives `[<0;88;20M` junk.
|
||||
**Only claude ≥ 2.1.187 with mouse tracking on forwards the wheel; everything else scrolls local scrollback** (#227 follow-up, `terminal-ui.js:_shouldForwardWheelToApp`). Codex was in the forward list until a reporter hit a completely dead wheel in codex tabs while the scrollbar drag worked. Measured against codex-cli 0.147.0 in a bare tmux: it never enables mouse tracking (`mouse_any_flag=0`) and SGR wheel reports fed to its PTY change nothing on screen, because it runs an INLINE viewport (`alternate_on=0`) and pushes its transcript into the terminal's own scrollback (tmux `history_size` grows) instead of paging in-app. So for codex, local scrollback IS the transcript and forwarding swallowed every tick. Claude repeats this exactly in its default INLINE renderer (measured on 2.1.280: `alternate_on=0`, `mouse_any_flag=0`, `history_size` grows), and swipes on iOS Safari were dead there while codex scrolled; only fullscreen claude (`CLAUDE_CODE_NO_FLICKER=1`: alt screen plus modes 1003/1006) pages its transcript on wheel reports. So claude forwards only while the server-observed `cliMouseTracking` flag is true; a stale-false flag after a server restart falls through to the PageUp/PageDown fallback, never a dead wheel. ⚠️ "The TUI is a strip mode" is NOT evidence that it consumes wheel reports — verify with a real `\x1b[<64;c;rM` write into a live pane before adding a mode here. Hand-encoded SGR TAPS are gated by `_shouldReportMouseToCli()` (strip mode AND the server-observed `cliMouseTracking` flag, recorded by `_recordStrippedMouseMode` in session.ts as it strips): codex never enables mouse tracking, so since #325 no tap report is sent there at all — click-to-position was already a measured no-op in codex, and a pane that has fallen back to a shell no longer receives `[<0;88;20M` junk.
|
||||
|
||||
**Wheel/touch forwarding is NOT gated on viewport-at-bottom** (#205, `terminal-ui.js:_shouldForwardWheelToApp`): for sessions verified to scroll their own transcript on SGR wheel reports (claude ≥ 2.1.187 — version via the local/docker/remote `--version` probes), the plain wheel AND touch drags forward as coalesced SGR reports (`_forwardScrollToApp` → `_sendSyntheticSgrWheel`, 40ms batches, 5-tick cap, 512-byte queue bound). It used to gate on the viewport being at the bottom so both scrollbacks stayed reachable, but a repaint-mode CLI keeps NO terminal scrollback of its own — xterm's buffer holds only replayed repaint frames, so local scrolling drags the CLI's pinned prompt box up the screen over stale frames; and `scrollToLastNonEmptyLine()` routinely parked the viewport off-bottom, silently pinning the wheel to local. Forwarding now snaps the viewport home first (SGR coordinates address the LIVE screen — a report computed from a scrolled-up viewport would hit-test the wrong row). Local scrollback remains on Shift+wheel and the `terminalWheelLocalScrollback` opt-out (both also cover touch via the shared gate; touch has no Shift, so the setting is its only local pin). `_wheelScrollLines()` normalizes `deltaMode` (Firefox fires LINE deltas ≈3/notch — read as pixels that rounded to 0 and fell to the ±1 fallback, ~4× too slow; PAGE deltas scale by `terminal.rows`) while keeping the #154 Shift-axis trap (macOS trackpads put Shift+scroll magnitude on deltaX). Tests: `test/terminal-touch-tap.test.ts`.
|
||||
|
||||
|
||||
@@ -153,8 +153,9 @@ Worth knowing:
|
||||
Shell sessions open from a bounded recent tail so a large transcript cannot stall tab
|
||||
switching; press **Load full history** to pull the rest explicitly. Ordinary Shell scrolling
|
||||
and automatic output recovery stay within the bounded browser buffer.
|
||||
- **Wheel and touch scrolling** are forwarded into Claude's own transcript on recent Claude
|
||||
versions, so the wheel scrolls the conversation rather than the terminal. `Shift+Wheel` is
|
||||
- **Wheel and touch scrolling** are forwarded into Claude's own transcript when a recent
|
||||
Claude runs fullscreen, so the wheel scrolls the conversation rather than the terminal.
|
||||
Claude's default inline view keeps its history in the terminal and scrolls locally. `Shift+Wheel` is
|
||||
always local scrollback. Other CLIs scroll locally.
|
||||
- **Selection copy.** `Ctrl+C` copies when text is selected and interrupts when it is not.
|
||||
`Ctrl+Shift+C` always copies.
|
||||
|
||||
@@ -164,8 +164,9 @@ Scrollback behaviour depends on the CLI, and Codeman adjusts what it strips per
|
||||
Things to try:
|
||||
|
||||
- `Shift+Wheel` always scrolls the local buffer, whatever else is going on.
|
||||
- On Claude sessions with a recent CLI, the wheel is forwarded into Claude's own transcript,
|
||||
so it scrolls the conversation rather than the terminal buffer. That is intended.
|
||||
- On Claude sessions running fullscreen (recent CLI with mouse tracking on), the wheel is
|
||||
forwarded into Claude's own transcript, so it scrolls the conversation rather than the
|
||||
terminal buffer. That is intended. Claude's default inline view scrolls locally.
|
||||
- Scrolling to the very top pulls the full tmux scrollback again on demand.
|
||||
|
||||
### The wheel does nothing in a Codex session
|
||||
|
||||
Reference in New Issue
Block a user