libghostty

pub package GitHub Actions Sponsor

Dart bindings to libghostty-vt, the terminal emulator library from Ghostty.

Android iOS macOS Linux Windows Web
✅ ✅ ✅ ✅ ✅ ✅

Getting started

# pubspec.yaml
dependencies:
  libghostty: ^0.0.13

On web, initialize the Wasm module once before using any libghostty API:

await initializeForWeb(Uri.parse('assets/libghostty.wasm'));

Usage

Terminal

Terminal emulator with screen state, scrollback, cursor, styles, modes, and VT stream processing. Register effect callbacks for PTY writes, bell, title changes, and more.

Effects run synchronously during write and callback-emitting resize calls. If one or more effects throw, that call finishes processing, notifies its listeners, and then rethrows its first failure with the original stack. Catch the initiating call when the host needs to recover.

import 'dart:typed_data';
import 'package:libghostty/libghostty.dart';

void main() {
  final terminal = Terminal(cols: 80, rows: 24);

  // Register effects (callbacks invoked synchronously during write).
  terminal.onWritePty = (data) => pty.write(data);
  terminal.onBell = () => playSound();
  terminal.onTitleChanged = () => print('Title: ${terminal.title}');

  // Write VT data and resize.
  terminal.write(Uint8List.fromList(vtData));
  terminal.resize(cols: 120, rows: 40, cellWidthPx: 8, cellHeightPx: 16);

  // Read a single cell via grid reference (for ad-hoc lookups).
  final ref = GridRef.at(terminal, const Position(row: 0, col: 0));
  print(ref.content); // the character at (0, 0)

  // Read screen via render state and reusable iterators.
  final renderState = RenderState();
  final rows = RowIterator();
  final cells = CellIterator();

  renderState.update(terminal);
  rows.reset(renderState);
  while (rows.next()) {
    cells.reset(rows);
    while (cells.next()) {
      if (cells.hasText) print(cells.content);
    }
    rows.dirty = false;
  }
  renderState.dirty = DirtyState.clean;

  cells.dispose();
  rows.dispose();
  renderState.dispose();
  terminal.dispose();
}

Key encoding

Encode key events into terminal escape sequences, supporting legacy and Kitty keyboard protocol.

final encoder = KeyEncoder();
final event = KeyEvent()
  ..mods = const .ctrl()
  ..action = .press
  ..key = .c;

encoder.sync(terminal); // pick up mode changes before encoding
final encoded = encoder.encode(event);
if (encoded.isNotEmpty) pty.write(utf8.encode(encoded));

event.dispose();
encoder.dispose();

Mouse encoding

Encode mouse events into escape sequences, supporting X10, UTF-8, SGR, URxvt, and SGR-Pixels protocols.

final encoder = MouseEncoder()
  ..setSize(const MouseEncoderSize(
    screenWidth: 640,
    screenHeight: 384,
    cellWidth: 8,
    cellHeight: 16,
  ));

final event = MouseEvent()
  ..action = .press
  ..button = .left
  ..setPosition(x: 10.0, y: 5.0);

encoder.sync(terminal); // pick up tracking-mode changes before encoding
final encoded = encoder.encode(event);
if (encoded.isNotEmpty) pty.write(utf8.encode(encoded));

event.dispose();
encoder.dispose();

Formatting

Format terminal content as plain text, VT sequences, or HTML.

final formatter = Formatter(terminal: terminal, format: .plain);
print(formatter.format());
formatter.dispose();

Resource lifetime and errors

Dispose owned resources such as Terminal, RenderState, iterators, encoders, events, parsers, and formatters when they are no longer needed. Disposal is idempotent. Borrowed values such as GridRef, KittyGraphics, and KittyImage must not be used after a mutating terminal operation; reacquire them instead.

Failures reported by libghostty use typed exceptions, including OutOfMemoryException, InvalidValueException, OutOfSpaceException, NoValueException, IoException, and LimitExceededException. Invalid Dart resource lifecycle use throws StateError.

SGR and OSC parsing

Parse SGR (Select Graphic Rendition) parameters into typed attributes and OSC (Operating System Command) sequences from a byte stream.

// SGR: parse Select Graphic Rendition parameters.
final sgr = SgrParser();
for (final attr in sgr.parse([1, 38, 2, 255, 0, 0])) {
  switch (attr.tag) {
    case .bold:
      print('bold');
    case .directColorFg:
      print('fg: ${attr.color}');
    default:
      break;
  }
}
sgr.dispose();

// OSC: parse Operating System Command sequences.
final osc = OscParser();
osc.feedBytes(utf8.encode('0;My Title'));
final command = osc.end(0x07);
print(command.type);        // OscCommandType.changeWindowTitle
print(command.windowTitle); // My Title
osc.dispose();

Paste validation

Check paste data for unsafe sequences (newlines, bracketed paste escapes) before writing to the terminal.

pasteIsSafe('hello');           // true
pasteIsSafe('rm -rf /\n');      // false
pasteIsSafe('\x1b[201~inject'); // false

Terminal paste

Set onWritePty before calling Terminal.paste. The method applies the terminal's paste policy and returns whether it wrote text or a clipboard event.

terminal.paste(PasteRequest.text('direct text'));

final clipboardBytes = utf8.encode('clipboard text');
terminal.paste(PasteRequest.mime(
  source: PasteSource.clipboard,
  location: ClipboardLocation.standard,
  mimes: const ['text/plain'],
  read: (mime, sink) => sink.write(clipboardBytes),
));

MIME readers run synchronously and their sinks expire when they return. Clipboard events use onClipboardRead for later reads. The text fallback does not decode image or HTML data.

On Wasm, clipboard paste events require a secure random source configured with LibGhostty.setRandomSecure.

Libraries

libghostty
Framework-independent terminal emulation powered by libghostty.