xtermjs-skill
Expert guidance for building, configuring, and integrating xterm.js terminal emulators in web and Electron applications. Use this skill whenever the user mentions xterm, xterm.js, @xterm/xterm, terminal emulator in the browser, web terminal, WebSSH, in-browser shell, or asks about addons like FitAddon, WebglAddon, SearchAddon, AttachAddon, or integration with node-pty. Also trigger for questions about ANSI/VT sequences, terminal theming, PTY over WebSocket, custom key handlers, parser hooks, or embedding a terminal in React/Vue/Angular/Electron apps. TRIGGER WHEN: the user mentions xterm, xterm.js, @xterm/xterm, terminal emulator in the browser, web terminal, WebSSH, in-browser shell, or asks about addons like FitAddon, WebglAddon, SearchAddon, AttachAddon, or integration with node-pty. DO NOT TRIGGER WHEN: the user wants a native OS terminal (not browser-based) or a non-xterm terminal library (e.g. VT100 emulator in Python).
What this skill does
# xterm.js Development Skill
xterm.js (`@xterm/xterm`) is a full-featured terminal emulator that runs in the browser or
Electron. It is NOT a shell -- it must be connected to a backend process (e.g. via node-pty +
WebSocket) to execute commands.
---
## 1. Installation
```bash
npm install @xterm/xterm
# Required addons (install only what you need):
npm install @xterm/addon-fit @xterm/addon-attach @xterm/addon-web-links \
@xterm/addon-search @xterm/addon-webgl @xterm/addon-clipboard \
@xterm/addon-web-fonts @xterm/addon-unicode11
```
Always import the CSS:
```html
<link rel="stylesheet" href="node_modules/@xterm/xterm/css/xterm.css" />
```
Or in JS/TS:
```ts
import '@xterm/xterm/css/xterm.css';
```
---
## 2. Basic Setup
```ts
import { Terminal } from '@xterm/xterm';
const term = new Terminal({
cols: 80,
rows: 24,
cursorBlink: true,
scrollback: 5000,
fontFamily: '"Cascadia Code", Menlo, monospace',
fontSize: 14,
theme: {
background: '#1e1e1e',
foreground: '#d4d4d4',
cursor: '#d4d4d4',
},
});
term.open(document.getElementById('terminal')!);
term.write('Hello from \x1B[1;32mxterm.js\x1B[0m\r\n$ ');
```
**Critical**: Always call `term.open(element)` AFTER the element is in the DOM.
Use `\r\n` (not just `\n`) for newlines when writing directly.
---
## 3. Official Addons
| Addon | Package | Purpose |
|---|---|---|
| FitAddon | `@xterm/addon-fit` | Resize terminal to fill its container |
| AttachAddon | `@xterm/addon-attach` | Connect to a WebSocket backend |
| SearchAddon | `@xterm/addon-search` | In-terminal text search |
| WebglAddon | `@xterm/addon-webgl` | GPU-accelerated WebGL2 renderer |
| WebLinksAddon | `@xterm/addon-web-links` | Clickable URLs |
| ClipboardAddon | `@xterm/addon-clipboard` | Browser clipboard integration |
| WebFontsAddon | `@xterm/addon-web-fonts` | Wait for web fonts before rendering |
| Unicode11Addon | `@xterm/addon-unicode11` | Unicode 11 character width support |
| LigaturesAddon | `@xterm/addon-ligatures` | Font ligature support (DEPRECATED with Canvas renderer removal; prefer WebGL + font features) |
### Loading addons
```ts
import { FitAddon } from '@xterm/addon-fit';
import { WebglAddon } from '@xterm/addon-webgl';
import { WebLinksAddon } from '@xterm/addon-web-links';
import { SearchAddon } from '@xterm/addon-search';
const fitAddon = new FitAddon();
const searchAddon = new SearchAddon();
term.loadAddon(fitAddon);
term.loadAddon(searchAddon);
term.loadAddon(new WebLinksAddon());
term.open(document.getElementById('terminal')!);
// WebGL: load AFTER open(), handle fallback
try {
const webgl = new WebglAddon();
webgl.onContextLoss(() => webgl.dispose()); // fallback on context loss
term.loadAddon(webgl);
} catch {
console.warn('WebGL2 not available, falling back to canvas renderer');
}
fitAddon.fit();
```
---
## 4. FitAddon: Responsive Resizing
FitAddon resizes cols/rows to fit the container's pixel dimensions.
```ts
// Resize on window resize
const ro = new ResizeObserver(() => fitAddon.fit());
ro.observe(document.getElementById('terminal')!);
// Or with window resize event (less precise):
window.addEventListener('resize', () => fitAddon.fit());
```
**Gotcha**: Container must have explicit dimensions (CSS `width`/`height`).
FitAddon returns `undefined` if the container has zero size.
---
## 5. Backend Integration: node-pty + WebSocket
### Frontend (AttachAddon)
```ts
import { AttachAddon } from '@xterm/addon-attach';
const socket = new WebSocket('ws://localhost:3000/ws');
const attachAddon = new AttachAddon(socket);
term.loadAddon(attachAddon);
// Sync terminal resize with PTY on the server
term.onResize(({ cols, rows }) => {
socket.send(JSON.stringify({ type: 'resize', cols, rows }));
});
```
### Backend (Node.js + node-pty)
```ts
import * as pty from 'node-pty';
import { WebSocketServer } from 'ws';
const wss = new WebSocketServer({ port: 3000 });
wss.on('connection', (ws) => {
const shell = process.platform === 'win32' ? 'powershell.exe' : 'bash';
const ptyProcess = pty.spawn(shell, [], {
name: 'xterm-color',
cols: 80,
rows: 24,
env: process.env,
});
ptyProcess.onData((data) => ws.send(data));
ws.on('message', (msg) => {
const parsed = JSON.parse(msg.toString());
if (parsed.type === 'resize') {
ptyProcess.resize(parsed.cols, parsed.rows);
} else {
ptyProcess.write(msg.toString());
}
});
ws.on('close', () => ptyProcess.kill());
});
```
For simple bidirectional use (no resize), AttachAddon handles piping automatically;
manual `term.onData` / `pty.onData` wiring is only needed for custom protocols.
---
## 6. Manual Data Wiring (without AttachAddon)
```ts
// PTY → terminal
ptyProcess.onData((data) => term.write(data));
// Terminal → PTY (user keystrokes)
term.onData((data) => ptyProcess.write(data));
// Binary events (e.g., certain mouse reports)
term.onBinary((data) => ptyProcess.write(data));
```
---
## 7. Theming
Pass an `ITheme` object in options or use `term.options.theme = {...}` at runtime:
```ts
const darkTheme = {
background: '#0d1117',
foreground: '#c9d1d9',
cursor: '#58a6ff',
cursorAccent: '#0d1117',
selectionBackground: '#264f78',
black: '#484f58', red: '#ff7b72', green: '#3fb950', yellow: '#d29922',
blue: '#58a6ff', magenta: '#bc8cff', cyan: '#39c5cf', white: '#b1bac4',
brightBlack: '#6e7681', brightRed: '#ffa198', brightGreen: '#56d364',
brightYellow: '#e3b341', brightBlue: '#79c0ff', brightMagenta:'#d2a8ff',
brightCyan: '#56d4dd', brightWhite: '#f0f6fc',
};
// Apply at construction
const term = new Terminal({ theme: darkTheme });
// Or update at runtime
term.options.theme = darkTheme;
```
---
## 8. Key Event Handling
```ts
// Intercept keys before terminal processes them
// Return false to suppress, true to allow
term.attachCustomKeyEventHandler((ev: KeyboardEvent) => {
// Example: Ctrl+Shift+C → copy
if (ev.ctrlKey && ev.shiftKey && ev.key === 'C') {
// Modern clipboard API (navigator.clipboard requires HTTPS/localhost)
const selection = term.getSelection();
if (selection) navigator.clipboard.writeText(selection);
return false; // don't pass to terminal
}
return true;
});
```
---
## 9. Search (SearchAddon)
```ts
// Find next/previous
searchAddon.findNext('search term', {
regex: false,
wholeWord: false,
caseSensitive: false,
incremental: false, // true = highlight while typing
decorations: {
matchBackground: '#ffff0040',
matchBorder: '#ffff00',
matchOverviewRuler: '#ffff00',
activeMatchBackground: '#ff000080',
activeMatchBorder: '#ff0000',
activeMatchColorOverviewRuler: '#ff0000',
},
});
searchAddon.findPrevious('search term');
```
---
## 10. Decorations and Markers
```ts
// Add a marker on the current row
const marker = term.registerMarker(0); // 0 = current row offset
// Add a decoration (e.g. highlight a line)
const decoration = term.registerDecoration({
marker,
overviewRulerOptions: { color: '#ff0000' },
});
decoration?.onRender((element) => {
element.style.backgroundColor = 'rgba(255,0,0,0.2)';
});
```
---
## 11. Parser Hooks (Custom Sequences)
```ts
// Register a custom OSC sequence handler (e.g. OSC 1337)
term.parser.registerOscHandler(1337, (data: string) => {
console.log('Custom OSC 1337 payload:', data);
return true; // handled
});
// Register a custom CSI sequence handler
term.parser.registerCsiHandler({ final: 'z' }, (params) => {
console.log('Custom CSI z params:', params);
return true;
});
```
---
## 12. React Integration Pattern
```tsx
import { useEffect, useRef } from 'react';
import { Terminal } from '@xterm/xterm';
import { FitAddon } from '@xterm/addon-fit';
import '@xterm/xterm/css/xterm.css';
export function XTerminal() {
const containerRef = useRef<HTMLDivElement>(null);
const termRef = useRef<Terminal | null>(null);
useEffect(() => {
const term = new Terminal({ cuRelated in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.