Web Package - rahulpandita/react-term GitHub Wiki
The @next_term/web package provides the browser-side terminal runtime: the WebTerminal orchestrator, Web Worker communication bridges, WebGL2 and Canvas 2D renderers, input handling, accessibility, and the addon system.
WebTerminal
WebTerminal is the main entry point. It owns all subsystems and exposes a unified API to framework layers.
const term = new WebTerminal({
cols: 80,
rows: 24,
fontSize: 14,
fontFamily: 'monospace',
fontWeight: 400,
fontWeightBold: 700,
theme: myTheme,
useWorker: true,
onData: (data) => socket.send(data),
onResize: (cols, rows) => pty.resize(cols, rows),
onTitleChange: (title) => (document.title = title),
});
term.attach(containerElement);
term.write(data);
term.loadAddon(new FitAddon());
term.dispose();
WebTerminal maintains two Buffer instances (normal + alternate screen), passes both SABs to WorkerBridge on init, and manages a scrollback viewport and a scrollbar overlay (1.5 s auto-hide).
Write Data Flow
The following sequence shows how data written to WebTerminal.write() reaches the canvas.
sequenceDiagram
participant App
participant WT as WebTerminal
participant WB as WorkerBridge
participant PW as Parser Worker
participant SAB as SharedArrayBuffer
participant RW as Render Worker
App->>WT: write(data)
WT->>WB: write(data)
WB->>WB: check watermark
WB->>PW: postMessage(chunk)
PW->>PW: VTParser.parse()
PW->>SAB: Atomics.store (cells + dirty)
PW-->>WB: FlushMessage
WB-->>WT: onFlush()
WT->>RW: update(cursor, selection)
RW->>RW: rAF → drawFrame
WorkerBridge
WorkerBridge manages the parser worker lifecycle and implements write-side flow control.
- High watermark (2 MB pending bytes): incoming
write()calls are queued once the parser worker is busy. - Low watermark (512 KB): the write queue drains when the worker signals readiness via
FlushMessage. - SAB mode: the worker writes directly to the
SharedArrayBuffer; only aFlushMessage(with anisAlternateflag) is sent back to trigger a render. - Non-SAB fallback: cell data and dirty rows are transferred as
Transferableobjects in the flush message and applied to the main-thread grid viaapplyFlush.
RenderBridge
RenderBridge manages the OffscreenCanvas render worker. It is used when both SharedArrayBuffer and OffscreenCanvas are available (Full Worker strategy). canUseOffscreenCanvas() performs the feature detection.
Messages sent to the render worker: init, update (cursor + selection), resize, theme, font, dispose. The render worker posts frame (FPS stats) and error messages back.
Canvas2DRenderer
Canvas2DRenderer implements IRenderer using the CanvasRenderingContext2D API. It is the universal fallback and runs on the main thread in Parser Worker strategy.
- Renders only dirty rows per frame.
- Wide characters:
isSpacerCell()skips the right-half cell;isWide()doubles the render width viacellWidth * 2. - Supports
fontWeight(normal) andfontWeightBoldfor fine-grained weight control.
WebGLRenderer
WebGLRenderer is the high-performance renderer using WebGL2 instanced rendering.
- 2 draw calls per frame: one for background rectangles, one for foreground glyphs.
- Alpha-only glyph atlas: glyphs are rasterized lazily into an
OffscreenCanvas-backed texture, keyed by(codepoint, bold, italic). - Wide character support: spacer cells are skipped; the background instance for a wide character spans
2 × cellWidth; the glyph instance is rendered at double width. hexToFloat4: accepts any CSS color expression (#rrggbb,#rgb,rgb(),rgba(),hsl(),oklch(), named colors) via a 1×1OffscreenCanvascolor-parsing trick that works inside workers.createRenderer('auto' | 'webgl' | 'canvas2d'): factory that returns aWebGLRendererorCanvas2DRenderer.
SharedWebGLContext
SharedWebGLContext enables multiple terminal panes to share a single WebGL2 context, working around Chrome's hard limit of 16 simultaneous contexts.
preserveDrawingBuffer: truekeeps each pane's canvas content after compositing.- Per-terminal dirty-row caches prevent redundant redraws; at 16 panes this allows ~120 fps.
- Wide character support mirrors the standalone
WebGLRenderer: spacer cells skipped, background instances at 2× width. - Software renderer detection: if WebGL2 is software-backed,
SharedWebGLContextthrows to trigger automatic Canvas 2D fallback.
InputHandler
InputHandler translates DOM keyboard and pointer events into VT sequences.
- Kitty keyboard protocol (modes 1/2/4/8/16): CSI u format, event types, alt/shifted base keys, all-key reporting, associated text.
- Mouse reporting: X10, Normal, Button, Any event modes.
- Touch input: touch-to-pointer translation.
- Paste: bracketed paste wrapping.
AccessibilityManager
AccessibilityManager maintains a parallel DOM that screen readers can traverse. See Accessibility for details.
Addons
Addons are loaded via WebTerminal.loadAddon(addon).
SearchAddon
Provides regex text search across the terminal viewport and scrollback.
import { SearchAddon } from '`@next_term/web`';
const search = new SearchAddon();
term.loadAddon(search);
search.findNext('error', { regex: true, caseSensitive: false });
search.findPrevious('error');
search.clearDecorations();
WebLinksAddon
Detects URLs in terminal output and makes them clickable.
import { WebLinksAddon } from '`@next_term/web`';
term.loadAddon(new WebLinksAddon((event, uri) => {
window.open(uri, '_blank');
}));
FitAddon
Automatically resizes the terminal columns and rows to fill its container.
import { FitAddon } from '`@next_term/web`';
const fit = new FitAddon();
term.loadAddon(fit);
// Call after the container is mounted or the window is resized
fit.fit();