@mks2508/restty
v0.1.37
Published
Browser terminal rendering library powered by WASM, WebGPU/WebGL2, and TypeScript text shaping.
Maintainers
Readme
restty
Powerful, lightweight browser terminal. Batteries included.
Live demo: https://restty.pages.dev/
Powered by:
libghostty-vt(WASM terminal core)WebGPU(with WebGL2 fallback)text-shaper(shaping + raster)
Release Status
restty is in an early release stage.
- Known issue: kitty image protocol handling can still fail in some edge cases.
- API note: high-level APIs are usable now, but some APIs may still change to improve DX.
If you hit an issue, please open one on GitHub with repro steps.
Install
npm i resttyQuick Start
<div id="terminal"></div>import { Restty } from "restty";
const restty = new Restty({
root: document.getElementById("terminal") as HTMLElement,
});
restty.connectPty("ws://localhost:8787/pty");That is the primary API: new Restty(...).
restty creates pane DOM, canvas, and hidden IME input for you.
Common Tasks
Apply a built-in theme
import { getBuiltinTheme } from "restty";
const theme = getBuiltinTheme("Aizen Dark");
if (theme) restty.applyTheme(theme);Parse and apply a Ghostty theme file
import { parseGhosttyTheme } from "restty";
const theme = parseGhosttyTheme(`
foreground = #c0caf5
background = #1a1b26
cursor-color = #c0caf5
`);
restty.applyTheme(theme, "inline");Split panes and operate per pane
restty.splitActivePane("vertical");
restty.splitActivePane("horizontal");
for (const pane of restty.panes()) {
pane.connectPty("ws://localhost:8787/pty");
}Use active-pane convenience methods
restty.setFontSize(15);
restty.sendInput("ls -la\n");
restty.copySelectionToClipboard();Provide custom fonts
By default, restty uses a local-first font preset with CDN fallback. To fully control fonts, disable the preset and pass fontSources.
const restty = new Restty({
root: document.getElementById("terminal") as HTMLElement,
appOptions: {
fontPreset: "none",
},
fontSources: [
{
type: "url",
url: "https://cdn.jsdelivr.net/gh/JetBrains/[email protected]/fonts/ttf/JetBrainsMono-Regular.ttf",
label: "JetBrains Mono",
},
{
type: "local",
matchers: ["jetbrains mono nerd font", "fira code nerd font"],
label: "Local fallback",
},
],
});Update fonts at runtime (all panes):
await restty.setFontSources([
{ type: "local", matchers: ["sf mono"], required: true },
{
type: "url",
url: "https://cdn.jsdelivr.net/gh/ryanoasis/[email protected]/patched-fonts/NerdFontsSymbolsOnly/SymbolsNerdFontMono-Regular.ttf",
},
]);Touch behavior (pan-first by default)
On touch devices, restty defaults to pan-first scrolling with long-press selection.
const restty = new Restty({
root: document.getElementById("terminal") as HTMLElement,
appOptions: {
// "long-press" (default) | "drag" | "off"
touchSelectionMode: "long-press",
// Optional tuning knobs:
touchSelectionLongPressMs: 450,
touchSelectionMoveThresholdPx: 10,
},
});Plugin system (native)
Use plugins when you want to extend restty behavior without patching core.
import type { ResttyPlugin } from "restty";
const metricsPlugin: ResttyPlugin = {
id: "example/metrics",
apiVersion: 1,
activate(ctx) {
const paneCreated = ctx.on("pane:created", ({ paneId }) => {
console.log("pane created", paneId);
});
const outgoing = ctx.addInputInterceptor(({ text }) => text.replace(/\t/g, " "));
const lifecycle = ctx.addLifecycleHook(({ phase, action }) => {
console.log("lifecycle", phase, action);
});
return () => {
paneCreated.dispose();
outgoing.dispose();
lifecycle.dispose();
};
},
};
await restty.use(metricsPlugin, { sampleRate: 1 });
console.log(restty.pluginInfo("example/metrics"));
restty.unuse("example/metrics");Declarative loading (manifest + registry):
await restty.loadPlugins(
[{ id: "example/metrics", options: { sampleRate: 1 } }],
{
"example/metrics": () => metricsPlugin,
},
);See docs/plugins.md for full plugin authoring details.
xterm compatibility layer
For migration from xterm.js-style app code, use restty/xterm:
import { Terminal } from "restty/xterm";
const term = new Terminal({ cols: 100, rows: 30 });
term.open(document.getElementById("terminal") as HTMLElement);
term.onData((data) => console.log("input", data));
term.onResize(({ cols, rows }) => console.log("resize", cols, rows));
term.write("hello");
term.writeln(" world");
term.resize(120, 40);
term.loadAddon({
activate() {},
dispose() {},
});Compatibility scope:
- Good for common embed/migration flows.
- Not full xterm internals parity (buffer/parser/marker ecosystem APIs are not all implemented).
- Prefer native
ResttyAPI for long-term integrations.
API Snapshot
Primary class:
new Restty({ root, ...options })createRestty(options)
Xterm compatibility:
import { Terminal } from "restty/xterm"- Supports
open,write,writeln,resize,focus,blur,clear,reset,onData,onResize,options,loadAddon,dispose
Pane access:
panes()/pane(id)/activePane()/focusedPane()/forEachPane(visitor)splitActivePane("vertical" | "horizontal")/splitPane(id, direction)/closePane(id)
Active-pane convenience:
connectPty(url)/disconnectPty()/isPtyConnected()setRenderer("auto" | "webgpu" | "webgl2")setFontSize(number)/setFontSources([...])applyTheme(theme)/resetTheme()setMouseMode("auto" | "on" | "off")sendInput(text)/sendKeyInput(text)copySelectionToClipboard()/pasteFromClipboard()resize(cols, rows)/focus()/blur()updateSize(force?)destroy()
Plugin host:
use(plugin, options?)/loadPlugins(manifest, registry)/unuse(pluginId)/plugins()/pluginInfo(pluginId?)- plugin context supports
on(...),addInputInterceptor(...),addOutputInterceptor(...),addLifecycleHook(...),addRenderHook(...)
Advanced / Internal Modules
Use these only when you need lower-level control:
restty/internal: full internal barrel (unstable; includes low-level modules like WASM/input/pty helpers)
Local Development
git clone https://github.com/wiedymi/restty.git
cd restty
git submodule update --init --recursive
bun install
bun run build:themes
bun run playgroundOpen http://localhost:5173.
Repository Commands
bun run build # build package output
bun run test # full tests
bun run test:ci # CI-safe test target
bun run lint # lint
bun run format:check # formatting check
bun run build:assets # static playground bundle (playground/public/playground.js)
bun run playground # one-command local dev (PTY + playground dev server)
bun run pty # PTY websocket server onlyDocumentation
docs/README.md- docs indexdocs/usage.md- practical integration guidedocs/xterm-compat.md- xterm migration shimdocs/how-it-works.md- runtime flowdocs/internals/- implementation notes and architectureTHIRD_PARTY_NOTICES.md- third-party credits and notices
