fix: disable WebGL renderer to fix black-on-black text in tmux
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
This commit is contained in:
@@ -8,7 +8,6 @@ import React, {
|
|||||||
import { Terminal } from "xterm";
|
import { Terminal } from "xterm";
|
||||||
import { FitAddon } from "xterm-addon-fit";
|
import { FitAddon } from "xterm-addon-fit";
|
||||||
import { WebLinksAddon } from "xterm-addon-web-links";
|
import { WebLinksAddon } from "xterm-addon-web-links";
|
||||||
import { WebglAddon } from "xterm-addon-webgl";
|
|
||||||
import "xterm/css/xterm.css";
|
import "xterm/css/xterm.css";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -310,25 +309,13 @@ export const TerminalComponent = React.forwardRef<TerminalRef, TerminalProps>(
|
|||||||
term.loadAddon(fitAddon);
|
term.loadAddon(fitAddon);
|
||||||
term.loadAddon(new WebLinksAddon());
|
term.loadAddon(new WebLinksAddon());
|
||||||
|
|
||||||
// Load WebGL renderer for GPU acceleration, fall back to DOM
|
// NOTE: WebGL renderer disabled.
|
||||||
let webglAddon: WebglAddon | null = null;
|
// The WebGL addon causes black-on-black rendering artifacts with
|
||||||
try {
|
// tmux/vim reverse-video (inverse color) sequences on desktop.
|
||||||
webglAddon = new WebglAddon();
|
// Mobile already uses the DOM renderer (WebGL fails there), which
|
||||||
term.loadAddon(webglAddon);
|
// handles these color attributes correctly. The DOM renderer is
|
||||||
webglAddon.onContextLoss(() => {
|
// fast enough for typical terminal workloads.
|
||||||
console.warn("WebGL context lost, falling back to DOM renderer");
|
// See: xterm.js WebGL known issues with reverse video / minimumContrastRatio
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
const container = terminalRef.current;
|
const container = terminalRef.current;
|
||||||
|
|
||||||
@@ -585,16 +572,6 @@ export const TerminalComponent = React.forwardRef<TerminalRef, TerminalProps>(
|
|||||||
window.clearInterval(heartbeatCheckRef.current);
|
window.clearInterval(heartbeatCheckRef.current);
|
||||||
heartbeatCheckRef.current = null;
|
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 {
|
try {
|
||||||
term.dispose();
|
term.dispose();
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
Reference in New Issue
Block a user