@cplieger/web-terminal-engine
v2.5.0
Published
Browser terminal engine: VT500 screen buffer, DOM renderer, and binary wire protocol
Readme
@cplieger/web-terminal-engine
Browser virtual terminal renderer for the
cplieger/web-terminal-engineGo module: DOM-based VT500 screen with OSC 8 hyperlink support, scrollback, keyboard mapper, mouse encoder, and binary wire decoder. Zero runtime dependencies.
The browser half of the web-terminal-engine cross-language terminal library. Pairs with the Go server-side packages (vt, terminal) over a binary WebSocket protocol; see the project README for the full story.
Install
npx jsr add @cplieger/web-terminal-engine # JSR (preferred)
npm i @cplieger/web-terminal-engine # NPMUsage
import {
render,
keyboard,
mouse,
scroll,
modes,
decodeWireBinary,
} from "@cplieger/web-terminal-engine";
const wrap = document.getElementById("term") as HTMLElement;
const out = document.getElementById("term-output") as HTMLElement;
render.init({ output: out, termWrap: wrap });
scroll.init({ scrollEl: wrap });
mouse.init({
send: (data) => ws.send(data),
cellSize: () => ({ width: cellW, height: cellH }),
termElement: () => wrap,
});
ws.binaryType = "arraybuffer";
ws.addEventListener("message", (ev) => {
const msg = decodeWireBinary(ev.data);
if (!msg) return;
switch (msg.type) {
case "screen":
render.handleScreen(msg);
break;
case "scroll":
render.handleScroll(msg);
break;
case "modes":
modes.setModes(
msg.bracketedPaste,
msg.applicationCursor,
msg.mouseSGR,
msg.focusReporting,
msg.mouseMode,
msg.applicationKeypad,
msg.reverseVideo,
);
break;
case "title":
document.title = msg.title;
break;
}
});
document.addEventListener("keydown", (ev) => {
const r = keyboard.mapKeyboardEvent(ev);
if (r.kind === "send") {
ws.send(r.bytes);
ev.preventDefault();
}
if (r.kind === "scroll-up" || r.kind === "scroll-down") ev.preventDefault();
});API
render— DOM renderer driven byScreenMessage/ScrollMessageframes.init,handleScreen,handleScroll,updateFontMetrics,computeSize,getCursorPx,setPredictedCursor,resetScreen,resetScrollback,getHighestIndex,noteResumeBounds,updateReverseVideo.keyboard— TranslatesKeyboardEventto terminal byte sequences.mapKeyboardEvent,bracketTextForPaste,prepareTextForTerminal,ctrlByteFor. HonorsapplicationCursor,applicationKeypad,bracketedPaste. For touch / mobile UIs,bindMobileToolbar({toolbar, send, ids?})wirespointerdownhandlers for an on-screen Ctrl/arrows/Tab/Enter/Esc toolbar (with sticky-Ctrl semantics and DECCKM-aware arrows), returning aMobileToolbarControllerexposingapplyStickyCtrl,setCtrlArmed,isCtrlArmed, anddispose.mouse— SGR 1006 mouse + focus reporting encoder.init,encodeSGR,MouseInputHandler. Auto-gates onmouseMode > 0.scroll— Auto-follow tracker for the scroll container.init,stickToBottom,scrollToBottom,isUserScrolledUp.modes— DEC private mode state (synced from server'sModesMessage).setModes,isBracketedPaste,isApplicationCursor,getMouseMode,isMouseSGR,isFocusReporting,isApplicationKeypad,isReverseVideo.decodeWireBinary(buf)— Top-level decoder for the binary WebSocket frames. Returns aServerMessageornullfor invalid/truncated frames.connection— Client → server WebSocket lifecycle: owns the socket, exponential-backoff reconnect, and the resume/inputAck reliability layer (outbox + server-restart detection).init(callbacks),connect,sendBinary(bytes),sendResize,reconnectNow. The callbacks exposeonMessage(ServerMessage),onOpen/onClose/onConnecting/onOutboxFull/onServerRestart, acomputeSize()provider, and an optionalwsPath(defaults to"/ws"). It decodes frames internally and appliesmodes.setModesfor you, so a consumer only needs to dispatch screen/scroll torender. Prefer this over wiringWebSocket+decodeWireBinaryby hand unless you need full control.controlFrame(msg)/wsURL(proto, host, path?)— Low-level helpers for the client → server protocol (0x00-prefixed JSON control frames, WebSocket URL building). Used internally byconnection; exported for advanced consumers.
Wire types (WireRun, ScreenMessage, ScrollMessage, ModesMessage, TitleMessage, ResumeAckMessage, ServerMessage, ControlMessage) are re-exported from the package root and match the Go server's wire format byte-for-byte.
Browser-only
This package depends on document, HTMLElement, MessageChannel, and other DOM APIs, so it only runs in browser-like environments. The companion Go server runs anywhere Go does.
License
GPL-3.0 — see LICENSE.
