From 6553a8845b1ddac55b8c126f8ce91545ac2be613 Mon Sep 17 00:00:00 2001 From: Developer Date: Fri, 5 Jun 2026 09:04:20 +0000 Subject: [PATCH] fix: disable WebGL renderer to fix black-on-black text in tmux MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The xterm.js WebGL addon has known rendering bugs with reverse-video (inverse color) ANSI sequences — exactly what tmux uses for its status bar, pane borders, and selected text. On desktop the WebGL addon loaded successfully, causing characters to render as black-on-black and appear to 'disappear'. On mobile WebGL typically fails to initialize, so the terminal silently fell back to the DOM renderer which handles these color attributes correctly. - Remove WebGL addon loading and its cleanup logic - Remove unused xterm-addon-webgl import and dependency - DOM renderer is the default and correctly handles all ANSI color attributes including reverse video Quality gates: tsc --noEmit (pass), build (pass), bundle -100KB Refs: xterm.js WebGL reverse-video / minimumContrastRatio issues --- .../components/features/terminal/terminal.tsx | 37 ++++--------------- 1 file changed, 7 insertions(+), 30 deletions(-) diff --git a/apps/web/src/components/features/terminal/terminal.tsx b/apps/web/src/components/features/terminal/terminal.tsx index 289ef59..02aca77 100644 --- a/apps/web/src/components/features/terminal/terminal.tsx +++ b/apps/web/src/components/features/terminal/terminal.tsx @@ -8,7 +8,6 @@ import React, { import { Terminal } from "xterm"; import { FitAddon } from "xterm-addon-fit"; import { WebLinksAddon } from "xterm-addon-web-links"; -import { WebglAddon } from "xterm-addon-webgl"; import "xterm/css/xterm.css"; import { @@ -310,25 +309,13 @@ export const TerminalComponent = React.forwardRef( term.loadAddon(fitAddon); term.loadAddon(new WebLinksAddon()); - // Load WebGL renderer for GPU acceleration, fall back to DOM - let webglAddon: WebglAddon | null = null; - try { - webglAddon = new WebglAddon(); - term.loadAddon(webglAddon); - webglAddon.onContextLoss(() => { - console.warn("WebGL context lost, falling back to DOM renderer"); - try { - webglAddon?.dispose(); - } catch { - // ignore - } - webglAddon = null; - // Trigger a refit since cell dimensions may differ - requestAnimationFrame(() => fitTerminal()); - }); - } catch (e) { - console.warn("WebGL renderer failed to load, using DOM renderer", e); - } + // NOTE: WebGL renderer disabled. + // The WebGL addon causes black-on-black rendering artifacts with + // tmux/vim reverse-video (inverse color) sequences on desktop. + // Mobile already uses the DOM renderer (WebGL fails there), which + // handles these color attributes correctly. The DOM renderer is + // fast enough for typical terminal workloads. + // See: xterm.js WebGL known issues with reverse video / minimumContrastRatio const container = terminalRef.current; @@ -585,16 +572,6 @@ export const TerminalComponent = React.forwardRef( window.clearInterval(heartbeatCheckRef.current); heartbeatCheckRef.current = null; } - // Dispose WebGL addon BEFORE the terminal to avoid race with - // RenderService.setRenderer accessing a disposed renderer - if (webglAddon) { - try { - webglAddon.dispose(); - } catch { - // Ignore disposal errors from partially torn-down terminal - } - webglAddon = null; - } try { term.dispose(); } catch {