harden(tui): also disable fast-echo for tmux-flavored TERM (SSH-from-tmux)

TMUX is not forwarded over SSH, so a TUI launched on a remote host from
inside local tmux only sees TERM=tmux/tmux-256color with no TMUX var --
the cursor-drift bug still applies there. Extend supportsFastEchoTerminal()
to also fall back when TERM is tmux-flavored.

Deliberately scoped to tmux* only, NOT screen*: GNU screen sets the same
screen/screen-256color TERM and has no reported drift, so widening to
screen would disable the optimization for those users with no evidence of
a bug (matching the original PR's stated out-of-scope note).

Adds tests for tmux-flavored TERM (disabled) and screen/xterm TERM
(stays enabled) to guard against accidental widening.
This commit is contained in:
kshitijk4poor 2026-06-19 16:09:33 +05:30
parent ab8f063814
commit e52fffb607
2 changed files with 27 additions and 1 deletions

View file

@ -198,6 +198,23 @@ describe('supportsFastEchoTerminal', () => {
expect(supportsFastEchoTerminal({ TERM_PROGRAM: 'vscode' } as NodeJS.ProcessEnv)).toBe(true)
})
it('disables fast-echo when only a tmux-flavored TERM is present (SSH from tmux, no TMUX forwarded)', () => {
// OpenSSH forwards TERM but not TMUX, so a TUI on a remote host launched
// from inside local tmux sees TERM=tmux-256color with no TMUX var. The
// cursor-drift bug still applies, so fast-echo must stay off.
expect(supportsFastEchoTerminal({ TERM: 'tmux' } as NodeJS.ProcessEnv)).toBe(false)
expect(supportsFastEchoTerminal({ TERM: 'tmux-256color' } as NodeJS.ProcessEnv)).toBe(false)
})
it('does NOT disable fast-echo for screen-flavored TERM (GNU screen out of scope, no reported drift)', () => {
// GNU screen sets TERM=screen/screen-256color and has no reported drift.
// We must not widen the tmux guard to screen* and regress its perf.
expect(supportsFastEchoTerminal({ TERM: 'screen' } as NodeJS.ProcessEnv)).toBe(true)
expect(supportsFastEchoTerminal({ TERM: 'screen-256color' } as NodeJS.ProcessEnv)).toBe(true)
// And an unrelated 256color TERM must stay enabled.
expect(supportsFastEchoTerminal({ TERM: 'xterm-256color' } as NodeJS.ProcessEnv)).toBe(true)
})
it('disables fast-echo by default in Termux mode', () => {
expect(
supportsFastEchoTerminal({ TERMUX_VERSION: '0.118.0', PREFIX: '/data/data/com.termux/files/usr' } as NodeJS.ProcessEnv)

View file

@ -362,7 +362,16 @@ export function supportsFastEchoTerminal(env: NodeJS.ProcessEnv = process.env):
// tmux adds a PTY multiplexing layer that desyncs stdout.write() cursor
// advances from its internal cursor model, causing cursor drift and ghost
// whitespace under the fast-echo bypass path.
if ((env.TMUX ?? '').trim().length > 0) {
//
// `TMUX` catches the local case. It is NOT forwarded over SSH, so when the
// TUI runs on a remote host launched from inside local tmux we only see a
// tmux-flavored `TERM` (tmux sets `tmux`/`tmux-256color`); match that too so
// remote-over-tmux sessions still fall back to the safe render path. We
// deliberately do NOT match `screen*`: GNU screen sets the same TERM and has
// no reported drift, so widening to screen would disable the optimization for
// those users with no evidence of a bug.
const term = (env.TERM ?? '').trim().toLowerCase()
if ((env.TMUX ?? '').trim().length > 0 || term === 'tmux' || term.startsWith('tmux-')) {
return false
}