xterm.js as Renderer
In MonoTerm’s Atomic Architecture, xterm.js is demoted from a full terminal emulator to a specialized display component.What is xterm.js?
xterm.js is a complete terminal emulator written in TypeScript.Copy
╔═══════════════════════════════════════════════════════════════════════╗
║ ║
║ xterm.js - COMPLETE TERMINAL EMULATOR ║
║ ║
║ ║
║ ┌──────────────────────────────────────────────────────────┐ ║
║ │ │ ║
║ │ xterm.js │ ║
║ │ │ ║
║ │ ┌──────────────────────────────────────────────────┐ │ ║
║ │ │ VTE Parser Layer │ │ ║
║ │ │ │ │ ║
║ │ │ * EscapeSequenceParser │ │ ║
║ │ │ * InputHandler │ │ ║
║ │ │ * OSC/CSI/DCS handlers │ │ ║
║ │ │ * UTF-8 decoder │ │ ║
║ │ │ │ │ ║
║ │ └──────────────────────────────────────────────────┘ │ ║
║ │ │ │ ║
║ │ v │ ║
║ │ ┌──────────────────────────────────────────────────┐ │ ║
║ │ │ Storage Layer │ │ ║
║ │ │ │ │ ║
║ │ │ * Primary/Alternate buffers │ │ ║
║ │ │ * Line storage │ │ ║
║ │ │ * Circular scrollback │ │ ║
║ │ │ * Cursor state │ │ ║
║ │ │ │ │ ║
║ │ └──────────────────────────────────────────────────┘ │ ║
║ │ │ │ ║
║ │ v │ ║
║ │ ┌──────────────────────────────────────────────────┐ │ ║
║ │ │ Renderer Layer │ │ ║
║ │ │ │ │ ║
║ │ │ * WebGL Renderer (fast) │ │ ║
║ │ │ * Canvas Renderer (fallback) │ │ ║
║ │ │ * DOM Renderer (accessible) │ │ ║
║ │ │ │ │ ║
║ │ └──────────────────────────────────────────────────┘ │ ║
║ │ │ │ ║
║ │ v │ ║
║ │ ┌──────────────────────────────────────────────────┐ │ ║
║ │ │ Interaction Layer │ │ ║
║ │ │ │ │ ║
║ │ │ * Selection │ │ ║
║ │ │ * Mouse handling │ │ ║
║ │ │ * Keyboard handling │ │ ║
║ │ │ * Clipboard │ │ ║
║ │ │ │ │ ║
║ │ └──────────────────────────────────────────────────┘ │ ║
║ │ │ ║
║ └──────────────────────────────────────────────────────────┘ ║
║ ║
║ ║
║ xterm.js can work standalone - no backend needed. ║
║ term.write("\x1b[32mHello\x1b[0m") displays green text. ║
║ ║
╚═══════════════════════════════════════════════════════════════════════╝
The Demotion
In Atomic Architecture, we use only parts of xterm.js.Copy
╔═══════════════════════════════════════════════════════════════════════╗
║ ║
║ xterm.js IN ATOMIC ARCHITECTURE ║
║ ║
║ ║
║ ┌──────────────────────────────────────────────────────────┐ ║
║ │ │ ║
║ │ xterm.js │ ║
║ │ │ ║
║ │ ┌──────────────────────────────────────────────────┐ │ ║
║ │ │ VTE Parser Layer │ │ ║
║ │ │ │ │ ║
║ │ │ ╔═════════════════════╗ │ │ ║
║ │ │ ║ ║ │ │ ║
║ │ │ ║ BYPASSED ║ │ │ ║
║ │ │ ║ ║ │ │ ║
║ │ │ ║ Not used at ║ │ │ ║
║ │ │ ║ all in Atomic ║ │ │ ║
║ │ │ ║ Architecture ║ │ │ ║
║ │ │ ║ ║ │ │ ║
║ │ │ ╚═════════════════════╝ │ │ ║
║ │ │ │ │ ║
║ │ └──────────────────────────────────────────────────┘ │ ║
║ │ │ ║
║ │ ┌──────────────────────┐ │ ║
║ │ │ Direct injection │ │ ║
║ │ │ from Rust backend │ │ ║
║ │ │ │ │ ║
║ │ │ Pre-parsed cells │ │ ║
║ │ │ injected directly │ │ ║
║ │ └──────────┬───────────┘ │ ║
║ │ │ │ ║
║ │ v │ ║
║ │ ┌──────────────────────────────────────────────────┐ │ ║
║ │ │ Storage Layer │ │ ║
║ │ │ │ │ ║
║ │ │ ┌──────────────────────────────────────────┐ │ │ ║
║ │ │ │ │ │ │ ║
║ │ │ │ USED AS STORAGE │ │ │ ║
║ │ │ │ │ │ │ ║
║ │ │ │ Receives pre-parsed cells │ │ │ ║
║ │ │ │ from Grid Injector │ │ │ ║
║ │ │ │ │ │ │ ║
║ │ │ └──────────────────────────────────────────┘ │ │ ║
║ │ │ │ │ ║
║ │ └──────────────────────────────────────────────────┘ │ ║
║ │ │ │ ║
║ │ v │ ║
║ │ ┌──────────────────────────────────────────────────┐ │ ║
║ │ │ Renderer Layer │ │ ║
║ │ │ │ │ ║
║ │ │ ┌──────────────────────────────────────────┐ │ │ ║
║ │ │ │ │ │ │ ║
║ │ │ │ USED FOR DISPLAY │ │ │ ║
║ │ │ │ │ │ │ ║
║ │ │ │ WebGL renders stored cells │ │ │ ║
║ │ │ │ GPU-accelerated display │ │ │ ║
║ │ │ │ │ │ │ ║
║ │ │ └──────────────────────────────────────────┘ │ │ ║
║ │ │ │ │ ║
║ │ └──────────────────────────────────────────────────┘ │ ║
║ │ │ │ ║
║ │ v │ ║
║ │ ┌──────────────────────────────────────────────────┐ │ ║
║ │ │ Interaction Layer │ │ ║
║ │ │ │ │ ║
║ │ │ ┌──────────────────────────────────────────┐ │ │ ║
║ │ │ │ │ │ │ ║
║ │ │ │ USED AS-IS │ │ │ ║
║ │ │ │ │ │ │ ║
║ │ │ │ Selection, scrollbar, │ │ │ ║
║ │ │ │ clipboard all work normally │ │ │ ║
║ │ │ │ │ │ │ ║
║ │ │ └──────────────────────────────────────────┘ │ │ ║
║ │ │ │ │ ║
║ │ └──────────────────────────────────────────────────┘ │ ║
║ │ │ ║
║ └──────────────────────────────────────────────────────────┘ ║
║ ║
╚═══════════════════════════════════════════════════════════════════════╝
Feature-by-Feature Breakdown
What we use and what we bypass.Copy
╔═══════════════════════════════════════════════════════════════════════╗
║ ║
║ xterm.js FEATURE USAGE IN ATOMIC ARCHITECTURE ║
║ ║
║ ║
║ ┌─────────────────────────────────────────────────────────────┐ ║
║ │ │ ║
║ │ BYPASSED (VTE Parsing) - Replaced by Alacritty │ ║
║ │ │ ║
║ │ These components are NEVER called in Atomic Architecture: │ ║
║ │ │ ║
║ │ * EscapeSequenceParser │ ║
║ │ State machine for escape sequences │ ║
║ │ │ ║
║ │ * InputHandler │ ║
║ │ 50+ CSI handlers (cursorUp, eraseInDisplay, etc) │ ║
║ │ │ ║
║ │ * OscParser, DcsParser │ ║
║ │ Operating system commands, device control strings │ ║
║ │ │ ║
║ │ * term.write() │ ║
║ │ We NEVER call this method in Atomic Mode │ ║
║ │ │ ║
║ └─────────────────────────────────────────────────────────────┘ ║
║ ║
║ ┌─────────────────────────────────────────────────────────────┐ ║
║ │ │ ║
║ │ USED (Storage) │ ║
║ │ │ ║
║ │ These components store the data we inject: │ ║
║ │ │ ║
║ │ * Primary and alternate screen buffers │ ║
║ │ │ ║
║ │ * Line storage with internal data arrays │ ║
║ │ │ ║
║ │ * Cursor position (we set these directly) │ ║
║ │ │ ║
║ │ * Scrollback offset │ ║
║ │ │ ║
║ └─────────────────────────────────────────────────────────────┘ ║
║ ║
║ ┌─────────────────────────────────────────────────────────────┐ ║
║ │ │ ║
║ │ USED (Rendering) │ ║
║ │ │ ║
║ │ These components display the stored cells: │ ║
║ │ │ ║
║ │ * WebGL Renderer │ ║
║ │ GPU-accelerated rendering (via addon) │ ║
║ │ │ ║
║ │ * Canvas Renderer │ ║
║ │ Fallback for non-WebGL browsers │ ║
║ │ │ ║
║ │ * term.refresh() │ ║
║ │ Triggers re-render after direct injection │ ║
║ │ │ ║
║ └─────────────────────────────────────────────────────────────┘ ║
║ ║
║ ┌─────────────────────────────────────────────────────────────┐ ║
║ │ │ ║
║ │ USED (Interaction) │ ║
║ │ │ ║
║ │ User interaction features work unchanged: │ ║
║ │ │ ║
║ │ * SelectionService │ ║
║ │ Text selection with mouse/keyboard │ ║
║ │ │ ║
║ │ * Viewport (scrollbar) │ ║
║ │ Scrolling through history │ ║
║ │ │ ║
║ │ * Clipboard integration │ ║
║ │ Copy/paste functionality │ ║
║ │ │ ║
║ │ * FitAddon (auto-resize) │ ║
║ │ │ ║
║ │ * WebLinksAddon (clickable URLs) │ ║
║ │ │ ║
║ │ * SearchAddon (find in scrollback) │ ║
║ │ │ ║
║ └─────────────────────────────────────────────────────────────┘ ║
║ ║
╚═══════════════════════════════════════════════════════════════════════╝
The Data Path Comparison
Visual comparison of how data flows.Copy
╔═══════════════════════════════════════════════════════════════════════╗
║ ║
║ TRADITIONAL xterm.js DATA PATH ║
║ ║
║ ║
║ Raw PTY bytes: "\x1b[32mHello\x1b[0m World" ║
║ │ ║
║ v ║
║ ┌──────────────────────────────────────────────────────────┐ ║
║ │ term.write(bytes) │ ║
║ └──────────────────────────────────────────────────────────┘ ║
║ │ ║
║ v ║
║ ┌──────────────────────────────────────────────────────────┐ ║
║ │ EscapeSequenceParser │ ║
║ │ │ ║
║ │ State machine processes byte by byte: │ ║
║ │ \x1b ──▶ ESCAPE state │ ║
║ │ [ ──▶ CSI state │ ║
║ │ 3 ──▶ param = 3 │ ║
║ │ 2 ──▶ param = 32 │ ║
║ │ m ──▶ SGR action (set color 32 = green) │ ║
║ │ H ──▶ GROUND state, print 'H' │ ║
║ │ ... │ ║
║ └──────────────────────────────────────────────────────────┘ ║
║ │ ║
║ v ║
║ ┌──────────────────────────────────────────────────────────┐ ║
║ │ Storage Layer │ ║
║ │ Cells populated one at a time │ ║
║ └──────────────────────────────────────────────────────────┘ ║
║ │ ║
║ v ║
║ ┌──────────────────────────────────────────────────────────┐ ║
║ │ WebGL Renderer │ ║
║ └──────────────────────────────────────────────────────────┘ ║
║ ║
║ ║
║ SLOW: JavaScript processes every byte individually ║
║ ║
╚═══════════════════════════════════════════════════════════════════════╝
╔═══════════════════════════════════════════════════════════════════════╗
║ ║
║ ATOMIC ARCHITECTURE DATA PATH ║
║ ║
║ ║
║ Raw PTY bytes: "\x1b[32mHello\x1b[0m World" ║
║ │ ║
║ v ║
║ ┌──────────────────────────────────────────────────────────┐ ║
║ │ Rust Alacritty VTE Parser │ ║
║ │ │ ║
║ │ Parses everything at native speed │ ║
║ │ Updates internal grid state │ ║
║ └──────────────────────────────────────────────────────────┘ ║
║ │ ║
║ │ GridUpdate { rows: [...], cursor: {...} } ║
║ v ║
║ ┌──────────────────────────────────────────────────────────┐ ║
║ │ Grid Injector │ ║
║ │ │ ║
║ │ Receives pre-parsed cell data │ ║
║ └──────────────────────────────────────────────────────────┘ ║
║ │ ║
║ │ (BYPASSES term.write, EscapeSequenceParser, ║
║ │ InputHandler - all skipped entirely) ║
║ v ║
║ ┌──────────────────────────────────────────────────────────┐ ║
║ │ Storage Layer (Direct Write) │ ║
║ │ │ ║
║ │ Cells written directly to internal arrays │ ║
║ └──────────────────────────────────────────────────────────┘ ║
║ │ ║
║ v ║
║ ┌──────────────────────────────────────────────────────────┐ ║
║ │ WebGL Renderer │ ║
║ └──────────────────────────────────────────────────────────┘ ║
║ ║
║ ║
║ FAST: Rust does the heavy lifting ║
║ ║
╚═══════════════════════════════════════════════════════════════════════╝
Why Not Replace xterm.js Entirely?
If we bypass the parser, why keep xterm.js at all?Copy
╔═══════════════════════════════════════════════════════════════════════╗
║ ║
║ WHY WE STILL NEED xterm.js ║
║ ║
║ ║
║ We could theoretically replace xterm.js with a custom ║
║ renderer, but xterm.js provides many features for free: ║
║ ║
║ ║
║ ┌──────────────────────────────────────────────────────────┐ ║
║ │ │ ║
║ │ FEATURES WE GET FOR FREE │ ║
║ │ │ ║
║ │ ┌────────────────────────────────────────────────────┐ │ ║
║ │ │ │ │ ║
║ │ │ WebGL Renderer │ │ ║
║ │ │ * GPU-accelerated text rendering │ │ ║
║ │ │ * Glyph atlas management │ │ ║
║ │ │ * Efficient dirty-rectangle updates │ │ ║
║ │ │ │ │ ║
║ │ └────────────────────────────────────────────────────┘ │ ║
║ │ │ ║
║ │ ┌────────────────────────────────────────────────────┐ │ ║
║ │ │ │ │ ║
║ │ │ Text Selection │ │ ║
║ │ │ * Mouse drag selection │ │ ║
║ │ │ * Double-click word select │ │ ║
║ │ │ * Triple-click line select │ │ ║
║ │ │ * Rectangle select (Alt+drag) │ │ ║
║ │ │ │ │ ║
║ │ └────────────────────────────────────────────────────┘ │ ║
║ │ │ ║
║ │ ┌────────────────────────────────────────────────────┐ │ ║
║ │ │ │ │ ║
║ │ │ Scrolling │ │ ║
║ │ │ * Smooth scrolling │ │ ║
║ │ │ * Scrollbar with drag support │ │ ║
║ │ │ * Wheel scroll with momentum │ │ ║
║ │ │ │ │ ║
║ │ └────────────────────────────────────────────────────┘ │ ║
║ │ │ ║
║ │ ┌────────────────────────────────────────────────────┐ │ ║
║ │ │ │ │ ║
║ │ │ Addons │ │ ║
║ │ │ * FitAddon (auto-resize) │ │ ║
║ │ │ * WebLinksAddon (clickable URLs) │ │ ║
║ │ │ * SearchAddon (find in scrollback) │ │ ║
║ │ │ * Unicode11Addon (wide character support) │ │ ║
║ │ │ │ │ ║
║ │ └────────────────────────────────────────────────────┘ │ ║
║ │ │ ║
║ └──────────────────────────────────────────────────────────┘ ║
║ ║
║ ║
║ Reimplementing all of this would take months. ║
║ Better to reuse what works and bypass only the slow parts. ║
║ ║
╚═══════════════════════════════════════════════════════════════════════╝
The “Dumb Renderer” Concept
A summary of xterm.js’s new role.Copy
╔═══════════════════════════════════════════════════════════════════════╗
║ ║
║ xterm.js: FROM "TERMINAL EMULATOR" TO "TERMINAL RENDERER" ║
║ ║
║ ║
║ ║
║ ┌──────────────────────────────────────────────────────────┐ ║
║ │ │ ║
║ │ BEFORE (Traditional Usage) │ ║
║ │ │ ║
║ │ │ ║
║ │ xterm.js │ ║
║ │ ┌──────────────────────────────────────────────────┐ │ ║
║ │ │ │ │ ║
║ │ │ "I am a complete terminal emulator. │ │ ║
║ │ │ Give me raw bytes, I will parse them, │ │ ║
║ │ │ update my storage, and render them." │ │ ║
║ │ │ │ │ ║
║ │ │ Input: Raw PTY bytes │ │ ║
║ │ │ Output: Rendered terminal │ │ ║
║ │ │ │ │ ║
║ │ └──────────────────────────────────────────────────┘ │ ║
║ │ │ ║
║ └──────────────────────────────────────────────────────────┘ ║
║ ║
║ ║
║ ┌──────────────────────────────────────────────────────────┐ ║
║ │ │ ║
║ │ AFTER (Atomic Architecture) │ ║
║ │ │ ║
║ │ │ ║
║ │ xterm.js │ ║
║ │ ┌──────────────────────────────────────────────────┐ │ ║
║ │ │ │ │ ║
║ │ │ "I am now a display component. │ │ ║
║ │ │ Give me cell data, I will store it │ │ ║
║ │ │ and render it. No parsing needed." │ │ ║
║ │ │ │ │ ║
║ │ │ Input: Pre-parsed cell grid │ │ ║
║ │ │ Output: Rendered terminal │ │ ║
║ │ │ │ │ ║
║ │ └──────────────────────────────────────────────────┘ │ ║
║ │ │ ║
║ └──────────────────────────────────────────────────────────┘ ║
║ ║
║ ║
║ ║
║ The "intelligence" (VTE parsing) moved to Rust. ║
║ xterm.js is now a "dumb" but capable display. ║
║ ║
║ This is NOT a criticism of xterm.js. ║
║ It's an optimization for our specific use case. ║
║ ║
╚═══════════════════════════════════════════════════════════════════════╝
Summary
| Feature | Traditional xterm.js | MonoTerm Atomic Architecture |
|---|---|---|
| VTE Parsing | JavaScript | Rust (Alacritty) - 10x faster |
| term.write() | Used | Bypassed |
| Storage | Used | Used (direct injection) |
| WebGL Rendering | Used | Used |
| Selection | Used | Used |
| Scrolling | Used | Used |
| Addons | Used | Used |