mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-11 01:39:41 +02:00
Merge #555: opencode drags select text and the wheel pages its transcript
This commit is contained in:
+54
-16
@@ -277,10 +277,11 @@ function cliExportsTruecolor(mode: SessionMode): boolean {
|
||||
* Codex, Claude Code, and Gemini are known, controlled (Ink/React) TUIs that
|
||||
* repaint via cursor positioning, so dropping the alt-screen switch is safe —
|
||||
* content stays in the normal buffer. Excluded: `shell` (arbitrary programs like
|
||||
* vim/less/htop legitimately need the alt screen), `opencode` (renders its own
|
||||
* TUI that may rely on it), `pi` (below) and `grok` (a fullscreen alt-screen TUI
|
||||
* with mouse support, i.e. the opencode case, not the Ink case). Keep parity
|
||||
* with the replay-side strip in session-routes.ts.
|
||||
* vim/less/htop legitimately need the alt screen), `opencode` (its own MIDDLE strip,
|
||||
* isMuxMouseStripMode), `pi` (below) and `grok` (a fullscreen alt-screen TUI with
|
||||
* mouse support). Keep parity with the replay-side strip (`stripReplayBuffer` in
|
||||
* session-routes.ts); the table in `CliCapabilities.altScreen` is pinned for every
|
||||
* stock CLI in test/claude-scrollback-strip.test.ts.
|
||||
*
|
||||
* ⚠️ Being excluded here does NOT preserve the alt screen. Every excluded mode
|
||||
* falls through to isMuxAltScreenOnlyStripMode(), which strips the alt-screen
|
||||
@@ -300,8 +301,10 @@ export function isAltScreenStripMode(mode: SessionMode): boolean {
|
||||
|
||||
/**
|
||||
* Modes that need the NARROW strip: alt-screen toggles only, leaving `\x1b[3J`
|
||||
* and the mouse-tracking DECSETs alone. Applies to every mode `isAltScreenStripMode`
|
||||
* excludes, but ONLY when the session is tmux-backed (`useMux`).
|
||||
* and the mouse-tracking DECSETs alone. Applies to every mode that is neither
|
||||
* `strip-full` (isAltScreenStripMode) nor `strip-mux-and-mouse` (isMuxMouseStripMode),
|
||||
* so `strip-mux-only` and `preserve` alike, but ONLY when the session is tmux-backed
|
||||
* (`useMux`).
|
||||
*
|
||||
* The bug (issue #205): the tmux CLIENT emits `smcup` (`\x1b[?1049h`) as its first
|
||||
* bytes on attach, before any program has run. Unstripped, xterm.js parks in the
|
||||
@@ -326,7 +329,31 @@ export function isAltScreenStripMode(mode: SessionMode): boolean {
|
||||
* `\x1b[3J` from a user's own `clear` is a deliberate "wipe my scrollback".
|
||||
*/
|
||||
export function isMuxAltScreenOnlyStripMode(mode: SessionMode, useMux: boolean): boolean {
|
||||
return useMux && !isAltScreenStripMode(mode);
|
||||
if (!useMux) return false;
|
||||
const altScreen = getCli(mode)?.capabilities.altScreen;
|
||||
return altScreen !== 'strip-full' && altScreen !== 'strip-mux-and-mouse';
|
||||
}
|
||||
|
||||
/**
|
||||
* Modes whose mouse-tracking DECSETs must be stripped, leaving `3J` alone:
|
||||
* `altScreen: 'strip-mux-and-mouse'`, i.e. a mouse-capable full-screen TUI.
|
||||
*
|
||||
* Why this exists (opencode, measured 2026-09-16): the TUI enables tracking
|
||||
* DECSETs, tmux runs with `mouse off` and therefore passes the PANE's DECSETs
|
||||
* straight through to the tmux client, and the browser's xterm obeyed them —
|
||||
* `mouseTrackingMode` flipped to `'any'` and xterm then reported DRAGS to the TUI
|
||||
* instead of selecting locally. "Mark text, copy on select" silently did nothing
|
||||
* (measured 62 `none` / 18 `any` over 16s, and 5/5 dead drags while `any`), and
|
||||
* the obvious fallback — Ctrl+C — is opencode's `app_exit`, so the failure also
|
||||
* ended sessions. Stripping at the source keeps xterm in selection mode; clicks
|
||||
* still reach the CLI through the browser's hand-encoded tap, which this strip
|
||||
* publishes as `cliMouseTracking` (`_recordStrippedMouseMode`).
|
||||
*
|
||||
* Gated on `useMux` for the same reason as the narrow strip: on the direct-PTY
|
||||
* fallback the program's own DECSETs really do reach xterm and must be honoured.
|
||||
*/
|
||||
export function isMuxMouseStripMode(mode: SessionMode, useMux: boolean): boolean {
|
||||
return useMux && getCli(mode)?.capabilities.altScreen === 'strip-mux-and-mouse';
|
||||
}
|
||||
|
||||
// Note: Claude CLI PATH resolution moved to session-cli-builder.ts (buildClaudeEnv)
|
||||
@@ -2529,14 +2556,21 @@ export class Session extends EventEmitter {
|
||||
// redraws overwrite only the cells they target, so non-erased rows keep
|
||||
// their content. Gated to Codex/Claude/Gemini (isAltScreenStripMode).
|
||||
//
|
||||
// Every OTHER mode (shell/opencode/antigravity) gets the NARROW strip when it
|
||||
// is tmux-backed: alt-screen toggles only, because the sequence that breaks
|
||||
// Every OTHER mode (shell/antigravity/pi/grok/deepseek/omp) gets the NARROW strip
|
||||
// when it is tmux-backed: alt-screen toggles only, because the sequence that breaks
|
||||
// scrollback there is tmux's own client-side smcup at attach, not anything the
|
||||
// program in the pane emitted (issue #205, see isMuxAltScreenOnlyStripMode).
|
||||
// 3J and the mouse DECSETs stay, so `clear` and mouse-aware TUIs keep working.
|
||||
//
|
||||
// The MIDDLE case (isMuxMouseStripMode) is a mouse-capable full-screen TUI:
|
||||
// it needs smcup AND the mouse DECSETs gone — otherwise the pane's tracking
|
||||
// reaches xterm and every drag becomes a mouse report instead of a text
|
||||
// selection, which is what killed mark-and-copy in opencode — while 3J stays,
|
||||
// because a TUI is not a `clear` consumer.
|
||||
const fullStrip = isAltScreenStripMode(this.mode);
|
||||
const altOnlyStrip = !fullStrip && isMuxAltScreenOnlyStripMode(this.mode, this._useMux);
|
||||
if (fullStrip || altOnlyStrip) {
|
||||
const mouseStrip = isMuxMouseStripMode(this.mode, this._useMux);
|
||||
const altOnlyStrip = !fullStrip && !mouseStrip && isMuxAltScreenOnlyStripMode(this.mode, this._useMux);
|
||||
if (fullStrip || mouseStrip || altOnlyStrip) {
|
||||
// Reassemble sequences split across PTY chunk boundaries first: a chunk
|
||||
// ending mid-sequence ('\x1b[?104' now, '9h' next) would slip past the
|
||||
// strip below and leave xterm stuck in the scrollback-less alt buffer
|
||||
@@ -2555,14 +2589,18 @@ export class Session extends EventEmitter {
|
||||
// eslint-disable-next-line no-control-regex
|
||||
data = data.replace(/\x1b\[\?(?:47|1047|1049)[hl]/g, '');
|
||||
if (fullStrip) {
|
||||
data = data
|
||||
// eslint-disable-next-line no-control-regex
|
||||
data = data.replace(/\x1b\[3J/g, '');
|
||||
}
|
||||
if (fullStrip || mouseStrip) {
|
||||
data = data.replace(
|
||||
// eslint-disable-next-line no-control-regex
|
||||
.replace(/\x1b\[3J/g, '')
|
||||
// eslint-disable-next-line no-control-regex
|
||||
.replace(/\x1b\[\?(?:1000|1001|1002|1003|1005|1006|1007)[hl]/g, (seq) => {
|
||||
/\x1b\[\?(?:1000|1001|1002|1003|1005|1006|1007)[hl]/g,
|
||||
(seq) => {
|
||||
this._recordStrippedMouseMode(seq);
|
||||
return '';
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user