gespenst API - v0.1.0
    Preparing search index...

    Module @gespenst/core

    @gespenst/core

    A high-performance browser terminal and headless VT runtime powered by the official nightly ghostty-vt.wasm.

    The native API is byte-oriented and built around Web Streams. Browser terminals use worker isolation when available, select WebGPU, WebGL2, or Canvas 2D at runtime, and keep application policy outside the renderer.

    Ghostty's tip artifact is a nightly build. Each published package version pins and checksums an exact artifact. Pin this package version in production and test upgrades before deployment.

    pnpm add @gespenst/core
    

    Import the stylesheet and give the host an explicit height:

    <div id="terminal"></div>

    <style>
    #terminal {
    width: 100%;
    height: 32rem;
    }
    </style>
    import { createTerminal } from '@gespenst/core';
    import '@gespenst/core/style.css';

    const terminal = await createTerminal({
    container: document.querySelector<HTMLElement>('#terminal')!,
    worker: 'dedicated',
    renderer: 'auto',
    });

    terminal.write('\x1b[1;32mGhostty is ready\x1b[0m\r\n');
    terminal.focus();

    The default worker policy chooses a dedicated worker when Worker and OffscreenCanvas are available, with a main-thread fallback. The automatic renderer tries WebGPU, then WebGL2, then Canvas 2D for cell backgrounds. Browser Canvas 2D shapes text on every renderer path. Releases run the complete browser suite in Chromium and focused API compatibility tests in Firefox and WebKit; hardware WebGPU availability is not required because the renderer falls back automatically.

    The core accepts bidirectional byte streams and does not impose a server protocol:

    const connection = terminal.connect({
    readable: ptyOutput, // ReadableStream<Uint8Array>
    writable: ptyInput, // WritableStream<Uint8Array>
    });

    terminal.on('resize', ({ cols, rows }) => {
    sendPtyResize({ cols, rows });
    });

    await connection.closed;

    Keep PTY bytes as Uint8Array data from the socket to Ghostty. Use @gespenst/websocket for a ready-made binary WebSocket transport with resize messages, backpressure, and optional reconnect behavior.

    The terminal fills its host, so the host must have a nonzero width and height. A ResizeObserver fits the grid to container changes. Call fit() after revealing a terminal created in a hidden tab or dialog.

    The default font stack uses installed system monospace fonts. Custom fonts are flexible and do not come from a predefined list:

    await terminal.loadFont({
    family: 'JetBrains Mono',
    source: 'url(/fonts/jetbrains-mono.woff2)',
    descriptors: { weight: '400 700' },
    });

    await terminal.setFont({
    family: 'JetBrains Mono, ui-monospace, monospace',
    sizePx: 14,
    lineHeight: 1.25,
    });

    loadFont() installs the face in both the document and rendering worker before the grid is remeasured.

    Themes use portable CSS strings or RGB(A) objects and include xterm-compatible named ANSI, selection, and cursor-accent colors. Install @gespenst/themes for audited, tree-shakable presets:

    import { catppuccinMocha } from '@gespenst/themes/catppuccin-mocha';

    await terminal.setTheme(catppuccinMocha); // replaces the current theme
    await terminal.updateTheme({ cursor: '#ffffff' }); // patches it

    Enable allowTransparency at creation when non-selection colors use alpha. Set minimumContrastRatio when automatic text contrast is required. Missing extended ANSI colors are generated once by Ghostty rather than calculated in the cell-rendering loop.

    const terminal = await createTerminal({
    container,
    worker: 'dedicated',
    renderer: 'auto',
    scrollbackLines: 10_000,
    accessibility: 'basic',
    minimumContrastRatio: 1,
    });
    • Use worker: 'shared' when many terminals should share one worker event loop.
    • Use worker: false for deterministic main-thread execution or unsupported environments.
    • Use accessibility: 'full' when the product needs a live viewport text mirror.
    • Read terminal.renderer.backend to see which renderer was actually selected.
    • Lower scrollback for many concurrent terminals or memory-constrained devices.

    Terminal escape sequences can request clipboard writes and desktop notifications. The core emits typed clipboardWrite and notification events but does not perform those browser actions automatically. Version 0.1.0 denies remote clipboard writes; use clipboardWrite only as an audit record. Validate session trust, user intent, and browser permission before showing notifications.

    For user-initiated text and MIME paste, install @gespenst/clipboard. It keeps clipboard support disabled until the addon is loaded, supports Kitty mode-5522 paste events, denies remote writes, and requires an application confirmation hook before unsafe text can bypass normal paste safety. See the clipboard guide.

    Every on() call returns a disposable subscription. Calling terminal.dispose() closes active connections, disposes addons, removes listeners and loaded fonts, releases rendering resources, and terminates owned workers.

    Use the paged buffer API for scrollbars, search, links, transcript views, and compatibility layers:

    const visible = await terminal.readBuffer();
    const history = await terminal.readBuffer({
    start: 0,
    end: visible.state.totalRows,
    });

    terminal.on('viewportChange', ({ state: { viewportY, totalRows, viewportLength } }) => {
    updateScrollbar({ viewportY, totalRows, viewportLength });
    });

    Rows are read directly from Ghostty's active grid; Gespenst does not run a second VT parser. Ranges are half-open and clamped. viewportY and the scroll event use an absolute zero-based offset from the oldest retained row. Page only the rows a feature needs on hot paths.

    import { createCoreRuntime } from '@gespenst/core/headless';

    const runtime = await createCoreRuntime();
    const terminal = runtime.createTerminal({ cols: 100, rows: 30 });

    terminal.write('\x1b[34mParsed by Ghostty VT\x1b[0m\r\n');
    const text = terminal
    .viewport()
    .viewportRows.map((row) => row.text)
    .join('\n');

    console.log(text);
    runtime.dispose();

    Reuse one CoreRuntime for multiple terminals so they share the compiled Ghostty module. The headless runtime works without a DOM and exposes resolved cells, dirty rows, cursor state, effects, selection, snapshots, and input encoding.

    • Keep output byte-native and avoid decode-encode cycles.
    • Use writeAsync() when a producer needs a parse and render boundary.
    • Preserve meaningful transport chunks instead of writing one byte at a time.
    • Bound scrollback and avoid frequent viewport or snapshot reads on the output hot path.
    • Prefer paged readBuffer() ranges over rebuilding all retained scrollback.
    • Measure dedicated and shared workers with your real terminal count and output corpus.

    The workspace benchmark is intended for controlled comparisons, not a universal throughput claim:

    pnpm bench:throughput
    

    The core guide covers PTY architecture, every configuration group, fonts and themes, event policy, performance measurement, headless use, xterm.js migration, and troubleshooting.

    MIT. The vendored Ghostty WASM is MIT licensed; see THIRD_PARTY_NOTICES.md.

    core
    index