# xterm.js in Argus — Technical Specification ## What xterm.js is xterm.js is a browser-based terminal emulator written in TypeScript. It renders a VT100/ANSI terminal in a `` element, handles keyboard input, and connects to a backend via WebSocket. In Argus it is bundled inside **ttyd** — Argus does not ship or version-control xterm.js directly. Whatever version ttyd was compiled with is what runs. --- ## How it gets into the pane 1. User opens a TERMINAL pane → Argus loads the ttyd URL in a WebView 2. ttyd serves its own `index.html` which includes its bundled xterm.js (UMD build) 3. xterm.js boots, connects back to ttyd over WebSocket, attaches to the shell process The WebSocket carries binary frames: - `[0x00][keystrokes]` — user input, browser → server - `[0x01][cols 4B][rows 4B]` — resize signal, browser → server - raw VT100/ANSI bytes — shell output, server → browser --- ## Globals xterm.js exposes on `window` | Global | Type | What it is | |---|---|---| | `window.term` | `Terminal` | The xterm.js Terminal instance | | `window.socket` | `WebSocket` | The raw WS connection to ttyd | | `window.fitAddon` | `FitAddon` \| undefined | Fit-to-container addon (if ttyd bundles it) | Everything Argus does to xterm at runtime goes through `window.term`. --- ## What you can control on `window.term` ### Options (read/write at any time) ```js window.term.options.theme = { ... }; // full color theme object window.term.options.fontSize = 15; window.term.options.fontFamily = '"JetBrains Mono", monospace'; window.term.options.cursorStyle = 'block'; // 'block' | 'underline' | 'bar' window.term.options.cursorBlink = true; window.term.options.lineHeight = 1.2; window.term.options.scrollback = 10000; // lines kept in scrollback buffer ``` ### Theme object (full 16-color palette) ```js window.term.options.theme = { background: '#0D0D0D', foreground: '#C8C8C0', cursor: '#C15F3C', cursorAccent: '#0D0D0D', selection: 'rgba(193,95,60,0.25)', black: '#1E1E1E', red: '#C0392B', green: '#3FB950', yellow: '#D29922', blue: '#4D9EDA', magenta: '#B06EC0', cyan: '#4DB8B0', white: '#C8C8C0', brightBlack: '#4E4E4E', brightRed: '#D9534F', brightGreen: '#56D364', brightYellow: '#E8BB3F', brightBlue: '#79B8F8', brightMagenta: '#C678DD', brightCyan: '#6ACFCA', brightWhite: '#F0F0E8', }; ``` ### Methods ```js window.term.fit() // resize terminal to fill its container window.term.refresh(startRow, endRow) // force repaint of a row range window.term.clear() // clear scrollback + screen window.term.reset() // full terminal state reset window.term.write(data) // write raw VT100 data to the terminal window.term.writeln(text) // write text + newline window.term.focus() // focus the terminal (keyboard captures) window.term.blur() // unfocus window.term.scrollToBottom() window.term.scrollToTop() window.term.scrollLines(n) // positive = down, negative = up ``` ### Buffer (read-only, for scraping content) ```js var buf = window.term.buffer.active; var line = buf.getLine(rowIndex); // returns BufferLine | null line.translateToString(trimRight) // get text of a line buf.cursorX // cursor column buf.cursorY // cursor row buf.baseY // scroll position (viewportRow + baseY = bufferRow) buf.length // total rows in buffer incl. scrollback ``` ### Events (subscribe via `.onXxx(callback)`) ```js window.term.onData(function(data) { ... }) // user typed something window.term.onKey(function(e) { ... }) // key event {key, domEvent} window.term.onLineFeed(function() { ... }) // newline written window.term.onScroll(function(y) { ... }) // viewport scrolled window.term.onSelectionChange(function() { ... }) window.term.onResize(function({cols, rows}) { ... }) window.term.onTitleChange(function(title) { ... }) // terminal title set via ESC]2;...\x07 ``` ### Addons (loaded via `term.loadAddon(addon)`) The addon API is how you extend xterm without modifying it. Argus currently loads one: **`@xterm/addon-web-links` v0.11.0** — detects URLs in terminal output and makes them clickable. Argus inlines the full UMD bundle as a string constant and loads it after page load: ```js window.term.loadAddon(new WebLinksAddon.WebLinksAddon(function(ev, uri) { ev.preventDefault(); if (window.ArgusNative) window.ArgusNative.openUrl(uri); })); ``` Other official addons that could be loaded the same way: - `@xterm/addon-fit` — auto-resize terminal to container dimensions - `@xterm/addon-search` — find in terminal (Ctrl+F style) - `@xterm/addon-serialize` — serialize buffer to HTML or plain text - `@xterm/addon-unicode11` — better Unicode width handling - `@xterm/addon-image` — render inline images (sixel / iTerm2 protocol) - `@xterm/addon-canvas` — explicit canvas renderer (vs default DOM) --- ## What we can build with it Since Argus already injects JavaScript post-load via `evaluateJavascript()`, anything that touches `window.term` is possible without changing the server or ttyd. ### Things that need zero server changes | Feature | How | |---|---| | Change font / size / colors at runtime | Write to `window.term.options.*` from Kotlin | | Add font size +/- buttons in the header | Kotlin buttons call `evaluateJavascript("window.term.options.fontSize+=1;window.term.fit()",null)` | | Find/search in terminal | Load `@xterm/addon-search`, call `addon.findNext(query)` | | Copy selected text | `window.term.getSelection()` → pass to Android clipboard via `ArgusNative` bridge | | Clear terminal | `window.term.clear()` from Kotlin | | Scrollback line count setting | `window.term.options.scrollback = N` | | Inline image rendering | Load `@xterm/addon-image`, server sends sixel/iTerm2 escape sequences | | Session title capture | `window.term.onTitleChange(fn)` → report to Kotlin via `ArgusNative` bridge → update pane header label | | "Watch for pattern" (notify when command finishes) | `window.term.onLineFeed(fn)` + regex → trigger native notification | ### Things that need small server changes (ttyd/shell side) | Feature | How | |---|---| | Shell prompt with path/git in status bar | Shell writes to `\x1b]2;title\x07` (OSC 2) → `onTitleChange` picks it up | | Command output capture | `window.term.buffer.active` lines → serialize → send to Kotlin | --- ## Current Argus xterm theme (for reference) ``` Background: #0D0D0D Foreground: #C8C8C0 Cursor: #C15F3C (Argus brand orange) Font: JetBrains Mono, 15px, lineHeight 1.2 Cursor: block, blinking Scrollback: 10,000 lines Renderer: canvas-2D (WebGL force-disabled) ```