Skip to content

JabTerm React: resize dedup + accessibilitySupport option #21

@holiber

Description

@holiber

Problem

Two issues found while using JabTerm React component (<JabTerm>) in a dockview grid layout with multiple terminal panes:

1. Missing resize deduplication

handleResize() in JabTerm.js (lines 252-266) calls fitAddon.fit() and sends a resize WebSocket message on every window.resize and ResizeObserver callback without checking if cols/rows actually changed:

// Current behavior — sends resize even if dimensions are identical
const handleResize = () => {
  fitAddon.fit();
  if (ws.readyState === WebSocket.OPEN) {
    const cols = Math.max(term.cols || 80, 80);
    const rows = Math.max(term.rows || 24, 24);
    ws.send(JSON.stringify({ type: "resize", cols, rows })); // always sends
  }
};

In a grid layout with 3 terminal panes, each pane triggers 3-4 redundant resizes during initial layout, causing:

  • TUI apps like htop to flicker/redraw rapidly
  • mc (Midnight Commander) to show rendering artifacts
  • Unnecessary PTY SIGWINCH signals

Suggested fix: Track last sent cols/rows and skip if unchanged:

let lastCols = 0, lastRows = 0;
const handleResize = () => {
  fitAddon.fit();
  const cols = Math.max(term.cols || 80, 80);
  const rows = Math.max(term.rows || 24, 24);
  if (cols === lastCols && rows === lastRows) return;
  lastCols = cols;
  lastRows = rows;
  if (ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify({ type: "resize", cols, rows }));
  }
};

2. No accessibilitySupport option for xterm

JabTerm creates the xterm Terminal without exposing the accessibilitySupport option:

const term = new TerminalCtor({
  cursorBlink: true,
  fontFamily,
  fontSize,
  theme: { ... },
  // accessibilitySupport not configurable
});

In xterm v6 with the canvas/WebGL renderer, .xterm-rows text content is empty by default (accessibility tree not populated unless accessibilitySupport: "on"). This makes it impossible to read terminal text content via DOM queries like:

document.querySelector("[data-testid=\"my-terminal\"] .xterm-rows").textContent
// Returns empty string in xterm v6 canvas mode

Use case: browser2video uses waitForPrompt() and waitForText() to detect terminal state by reading .xterm-rows text. Without accessibility support, we had to add a workaround using JabTerm's readAll() capture buffer exposed via a DOM attribute.

Suggested fix: Accept accessibilitySupport in JabTerm props and pass it through to xterm:

<JabTerm wsUrl="..." accessibilitySupport="on" />

Reproduction

import { JabTerm } from "jabterm/react";

// In a dockview grid with 3 panes:
<JabTerm wsUrl="ws://localhost:9523/cmd:htop" />
<JabTerm wsUrl="ws://localhost:9523/cmd:mc" />
<JabTerm wsUrl="ws://localhost:9523/shell" />

// Observe: rapid pty_resize messages with identical cols/rows in jabterm server logs
// Observe: document.querySelector(".xterm-rows").textContent returns ""

Environment

  • jabterm: latest (commit e4ed68e)
  • @xterm/xterm: 6.0.0
  • @xterm/addon-fit: 0.11.0
  • React: 19.x
  • Electron: 40.x
  • macOS (Apple Silicon)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions