RPQA · Terminal Engineering · 2026-06-12

What a web terminal unlocks

Short version: a custom terminal built on xterm.js + node-pty (likely in Electron) is a completely normal terminal as far as Claude Code is concerned — and it adds five capability classes that no native macOS terminal offers. The deepest unlock isn't visual: it's that the terminal stops being a character stream and becomes structured data.

Other version: examples edition (plain English + challenge/solution)

The Research
In Plain English

The architecture everyone converges on

xterm.js + node-pty VS Code's exact stack

xterm.js renders the terminal in a DOM; node-pty spawns a real zsh behind it; a thin pipe connects them. Microsoft maintains both, and this is literally how VS Code's integrated terminal works — so "will Claude Code / vim / tmux run in it" is a settled question. Other production users: Wave, Tabby, Hyper, Azure Cloud Shell, JupyterLab, Codespaces, Replit.

github.com/xtermjs/xterm.js  ·  github.com/microsoft/node-pty
In plain English

It's three Lego pieces: a part that draws the terminal on screen, a part that runs a real shell behind the scenes, and a wire between them. These are the exact same parts VS Code's built-in terminal is made of — and Microsoft maintains them — so there's no mystery about whether it works. You used Claude Code inside VS Code for months; that was this.

The five web-only capabilities

1 · Real UI beside or over the terminal

Sidebars, buttons, lists, web content — with a live JavaScript bridge to terminal state. Every native terminal tops out at styled text in a character grid. The lone partial exception is iTerm2's toolbelt webview, which can dock a panel but only "loads a URL," with no real bridge.

In plain English

You can put normal app stuff — a sidebar, buttons, lists, even a small web page — right next to the terminal, and it can see and react to what's happening inside the terminal. Regular terminals can only ever show you text in a grid; they have no place to put a real button.

2 · Chrome that is entirely yours

Tabs, headers, layout, proportional type — and one design system spanning the UI and the terminal (Coda tokens could style both). Natives offer config options, never new widgets.

In plain English

Every pixel of the window is yours to design — the tabs, the title area, the layout, the fonts. The terminal and the rest of the app can share one look (your Coda styling could cover both). With normal terminals you can change colors and fonts, but the window furniture is whatever the vendor decided.

3 · Rich elements pinned to output lines

The Decorations API anchors a real DOM element to any buffer line — it scrolls with the text and disposes when the line scrolls off. That's how VS Code draws its exit-code dots. A "rerun" button or "ask Claude about this error" affordance on any line is the same mechanism.

xtermjs.org · Terminal API
In plain English

You can glue a button or badge to a specific line of terminal output, and it stays glued — scrolls with the text, disappears when the line does. Those little green/red dots next to commands in VS Code? That's this. Imagine an "ask Claude about this error" button appearing right on the error line.

4 · A private app↔shell protocol

Register custom escape-sequence handlers (registerOscHandler) and any CLI tool or shell function can talk straight to your UI — printf a message, a sidebar panel updates. No native terminal lets you define new sequences without forking.

xtermjs.org · parser hooks guide
In plain English

Scripts and command-line tools can send invisible messages to your app — nothing appears on screen, but a sidebar panel updates. It's a private walkie-talkie channel between the shell and your UI. No off-the-shelf terminal lets you invent new messages like this.

5 · Sessions as data

Serialize the whole buffer to HTML or escape codes: save/restore sessions across restarts, export styled output, share a command + its output as a permalink (Warp's party trick), or serve the same app to a phone — Tabby ships a web build of itself; Codespaces runs this stack entirely in-browser.

In plain English

Everything in the terminal — every command, all its output, with colors — can be saved like a document. Restore a session after a restart, export output as a styled web page, share a link to one command and its result, or open the same terminal on your phone.

The deeper unlock — the command model

Shell integration is the highest-leverage capability and it's invisible. A small script injected into zsh emits markers (OSC 133/633 — the public standard VS Code's terminal uses), and the app then knows, for every command: its text, where its output starts and ends, its exit code, its working directory, its duration.

Everything "smart" hangs off this: exit-code decorations, jump-between-commands navigation, sticky scroll, quick fixes, collapsible output, Warp-style blocks, a sidebar that reacts to failures. The terminal becomes data you can build product on.

code.visualstudio.com · shell integration spec

In plain English

Normally a terminal is a firehose of characters — it has no idea where one command ends and the next begins. A tiny tweak to the shell adds invisible bookmarks, so the app knows exactly: here's a command, here's its output, it succeeded (or failed), it ran in this folder, it took this long.

Once the app knows that, the terminal stops being a wall of text and becomes a list of things that happened — which you can navigate, collapse, decorate, and build features on. This is the single most valuable idea in the whole report.

The control group — what natives already do fine

CapabilityBest native answerVerdict
Scriptable panes, send-textWezTerm CLI, kitty remote control, iTerm2 PythonNatives fine
Styled status / tab bars, pickers, popupsWezTerm Lua, kitty kittens, tmux popupsNatives fine
Inline images, GLSL eye-candykitty graphics, OSC 1337, Ghostty shadersNatives fine
Output-reactive triggersiTerm2 regex triggersMostly fine
Interactive GUI sidebar / paneliTerm2 toolbelt webview (~20% of the way)Web only
Restyle the chrome itselfNone — config options at bestWeb only
DOM decoration of output linesNone — hyperlinks/images at bestWeb only
Custom escape-sequence protocolNone without forkingWeb only
One design system, UI + terminalNoneWeb only
In plain English

To be fair to the terminals you already use: they're good at a lot. Controlling panes from scripts, fancy status bars, pop-up pickers, inline images, visual effects — all covered. That's the green half of the table.

The red half is the point: none of them can host a real sidebar, let you redesign the window itself, attach buttons to output, accept custom invisible messages, or share one design across UI and terminal. That's not "harder on native" — it's impossible without forking the terminal's source code.

Native-side findings that matter

WezTerm has quietly stalled — no stable release since Feb 2024, nightlies only; the sidebar request is open and unimplemented, and a user asking for "xterm.js-style UI buttons, AI panels" got zero replies. Ghostty will never do this by design — Mitchell Hashimoto explicitly rejected a do-anything extension API. kitty's kittens and panels always terminate at another character grid. Warp open-sourced its client (AGPL, Apr 2026) — proof that block/AI UX requires owning the whole app.

wezterm #5171 (sidebar request)  ·  ghostty #2353  ·  warp.dev · open source
In plain English

Some news from the research worth knowing: WezTerm — your current terminal — has gone quiet. No proper release in over two years, and requests for exactly the kind of UI you want sit unanswered. Ghostty's creator has said flat-out he'll never add a plugin system. And Warp — the closest commercial product to this idea — had to build an entire terminal from scratch to do it, which rather proves the point.

The honest column — what you give up

Raw speed: VS Code-terminal-class, not Ghostty-class. The xterm.js maintainers themselves acknowledge the JS parser ceiling (they're exploring adopting libghostty's WASM core). Fine for Claude Code workloads; a purist daily-driver concern only.

Edge cases: compound-emoji width (experimental addon), no RTL, weaker screen-reader support than native, occasional IME quirks.

Ownership: it's an Electron app — auto-update, crash handling, node-pty rebuilds on Electron upgrades. Mitigated by a ~150-line core and two Electron apps already maintained.

xterm.js #5686 · "Explore adopting libghostty"
In plain English

The trade-offs, honestly: it's a touch slower than the fastest native terminals — about as fast as VS Code's terminal, which you used happily, so you almost certainly won't notice. A few rare quirks with unusual emoji and non-Western text. And it's an app you own — updates and maintenance are on you, though the core is tiny and you already maintain two Electron apps.

Fork something or build from scratch?

Wave Terminal Apache-2.0 · active

The closest shipped product to "terminal + sidebar blocks + AI" — terminal blocks beside AI chat (Vercel AI SDK), web panes, Monaco editors, and charts, on Electron + xterm.js 6 + React 19. Best used as a source-code reference; fork only if its block/layout model is exactly the desired UX.

github.com/wavetermdev/waveterm
In plain English

Someone already built almost exactly this idea — terminal panels next to AI chat, web pages, and editors — and gave the code away free. It proves the concept works. We'd read their code for answers rather than build on top of it, because their layout isn't quite what you want.

Hyper read, don't fork

The cleanest "all chrome is React" architecture — its plugin API proves the whole concept — but effectively unmaintained since January 2023.

github.com/vercel/hyper
In plain English

Another existing project with a beautiful architecture worth learning from — but it's been abandoned for three years, so we wouldn't build on it.

Scratch build Recommended

~150 lines to a first working shell; every capability above is incremental from there. Maximum ownership, zero inherited architecture, and existing Electron patterns (window-state persistence, automation port, deep links) port directly.

In plain English

The recommendation: start fresh. A working terminal is about 150 lines of code, and everything else gets added piece by piece. You own all of it, inherit nobody's decisions, and reuse patterns from your existing apps.

Questions, answered

  1. Is it really a normal terminal to Claude Code? Yes — Claude Code sees a pty and TERM=xterm-256color; VS Code's terminal is the same stack and runs it daily.
  2. What can it do that natives can't? The five classes above — real adjacent UI, custom chrome, line-anchored decorations, a private protocol, sessions-as-data — all resting on the command model.
  3. What's the cost? Some rendering speed, some Unicode/a11y edge cases, owning an Electron app. None blocking for a Claude Code cockpit.
  4. Fork or build? Build from scratch; read Wave's source for answers.
  5. Strategic question still open: this would be the natural successor to the WezTabs + WezTerm send-text bridge — one window, one design language. No build decision made yet.
In plain English

Bottom line: Claude Code will run in it exactly as it does in WezTerm — it can't tell the difference. The payoff is a terminal that's also an app: your design, your sidebar, buttons on error lines, scripts that talk to panels, sessions you can save and share.

The costs are real but small for your use. The smart path is building it fresh (it starts tiny) while cribbing from the open-source project that already proved the idea.

The one decision deliberately not made yet: whether this becomes the successor to WezTabs + WezTerm as your daily Claude Code home. That's yours to sit with.