Skip to content

Instantly share code, notes, and snippets.

@GGPrompts
Last active November 17, 2025 18:27
Show Gist options
  • Select an option

  • Save GGPrompts/7d40ea1070a45de120261db00f1d7e3a to your computer and use it in GitHub Desktop.

Select an option

Save GGPrompts/7d40ea1070a45de120261db00f1d7e3a to your computer and use it in GitHub Desktop.
Fixing xterm.js EOL Conversion for Tmux Splits #bug-fix #xterm.js #tmux #terminal #eol-conversion #split-panes

Fixing xterm.js EOL Conversion for Tmux Splits

Problem

When multiple xterm.js instances share a tmux session (e.g., React split terminals), enabling convertEol: true causes output corruption:

  • Text bleeding between split panes
  • Misaligned split divider (offset on first few rows only)
  • Cursor positioning errors

Root Cause

  • Tmux sends terminal sequences with proper line endings (\n)
  • xterm.js with convertEol: true converts \n\r\n (carriage return + line feed)
  • Each xterm instance in the split converts the same tmux output independently
  • Different cursor positioning = panes corrupt each other

Solution

Conditionally disable EOL conversion for tmux sessions:

// Terminal.tsx - xterm initialization
const isTmuxSession = !!agent.sessionName || shouldUseTmux;

const xtermOptions = {
  theme: theme.xterm,
  fontSize: savedFontSize,
  cursorBlink: true,
  allowProposedApi: true,
  cursorStyle: "block",
  scrollback: isTmuxSession ? 0 : 10000,

  // CRITICAL: Disable EOL conversion for tmux
  // Tmux manages its own terminal sequences - xterm should display raw output
  convertEol: !isTmuxSession,  // Only convert for regular shells

  // Ensure UNIX-style line endings
  windowsMode: false,
};

const xterm = new XTerm(xtermOptions);

Why This Works

  • Tmux sessions: convertEol: false → xterm displays raw PTY output without modification
  • Regular shells: convertEol: true → xterm converts for proper display (Windows compatibility)
  • Both xterm instances now handle tmux output identically → no corruption

Key Insights

  1. Tmux is a terminal multiplexer - it manages its own terminal protocol
  2. Multiple xterm instances sharing one tmux session must handle output identically
  3. EOL conversion must be disabled to prevent double-processing of tmux sequences
  4. windowsMode: false ensures UNIX-style line endings (\n only, not \r\n)

Testing

After applying the fix:

  1. Spawn a TUI terminal (e.g., TFE, htop)
  2. Split it horizontally or vertically
  3. Spawn a bash terminal in the new pane
  4. Type commands, check for corruption

Expected behavior:

  • Each pane displays independently
  • No text bleeding between panes
  • Line breaks work correctly in both panes
  • Split divider stays aligned

Related Issues

Font Normalization

When splitting tmux sessions, both panes must use the same font to calculate matching dimensions. Different fonts = different character heights = dimension mismatch.

Solution: Track reference dimensions per session and normalize fonts before xterm initialization.

// useTmuxSessionDimensions.ts
const tmuxSessionDimensions = new Map<string, {
  rows: number;
  cols: number;
  fontFamily: string;
  fontSize: number;
}>();

// Terminal.tsx - Before creating xterm
if (isTmuxSession && agent.sessionName) {
  const reference = getReference();
  if (reference) {
    // Use reference font to ensure matching dimensions
    savedFontFamily = reference.fontFamily;
    savedFontSize = reference.fontSize;
  }
}

Files

  • src/components/Terminal.tsx - xterm initialization with conditional EOL conversion
  • src/hooks/useTmuxSessionDimensions.ts - Font normalization for splits

References


Project: Tabz - Lightweight, tab-based terminal interface for the web Commit: cc05c4a - fix: disable EOL conversion for tmux splits to prevent corruption

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment