MoonBit bindings for Ghostty VT with a shared API across native, js,
wasm, and wasm-gc.
This package exposes one public MoonBit API from lib.mbt, with different backend implementations selected by target.
| target | backend file | runtime |
|---|---|---|
native |
backend_native.mbt |
libghostty-vt loaded through dlopen |
js |
backend_js.mbt |
inline JS runtime that loads ghostty-vt.wasm |
wasm / wasm-gc |
backend_wasm.mbt |
host import bridge + ghostty-vt.wasm |
The public API stays the same:
load()loaded()backend()last_error()build_info()Terminal::new(...)Terminal::write(...)Terminal::write_bytes(...)Terminal::reset()Terminal::resize(...)Terminal::cols()Terminal::rows()Terminal::title()Terminal::pwd()Terminal::format(...)Terminal::format_plain(...)
native uses stub.c as a small dynamic loader in the same style as
mizchi/libgit2.
- It resolves
libghostty-vtsymbols at runtime. RuntimeConfig.native_library_pathcan override the shared library path.- The included native test uses mock_libghostty_vt.c and vendor/libghostty-vt.dylib so the common API can be exercised without building upstream Ghostty in this repository.
js loads ghostty-vt.wasm directly from JavaScript.
Resolution order:
RuntimeConfig.wasm_module_pathglobalThis.__mbt_ghostty_wasm_module_pathglobalThis.LIBGHOSTTY_WASM_PATHglobalThis.LIBGHOSTTY_WASM_URL./vendor/ghostty-vt.wasm
For this repository, the recommended local path is:
./.runtime/ghostty-vt.wasm
You can prepare it with:
bash scripts/fetch_ghostty_vt_wasm.shBehavior:
- If
../restty/reference/ghosttyexists andapple/containeris available, the script builds upstreamghostty-vt.wasminside a Linux container with Zig0.15.2and writes the result to.runtime/ghostty-vt.wasm. - If
LIBGHOSTTY_VT_WASM_SRCis set, that path is copied into.runtime/. - If a host-built
../restty/reference/ghostty/zig-out/bin/ghostty-vt.wasmexists, it is copied into.runtime/. - Otherwise, a mock-compatible fallback wasm is built from
tools/mock_ghostty_vt_wasm/and copied into.runtime/.
The fallback wasm is intentionally aligned with the checked-in native mock library so that native/js parity can still be tested when upstream Ghostty cannot be built locally.
wasm and wasm-gc use the same MoonBit API, but they cannot embed
extern "js" directly. Instead, they depend on two host import modules:
ghostty_bridgeghostty_host
This module carries strings and byte arrays between MoonBit and the host. The MoonBit side is implemented in wasm_bridge.mbt.
Required imports:
begin_create_stringstring_append_charfinish_create_stringbegin_read_stringstring_read_charfinish_read_stringbegin_create_byte_arraybyte_array_append_bytefinish_create_byte_array
This module is the actual Ghostty VT adapter that the host must provide.
Required imports:
loadloadedlast_errorterminal_newterminal_is_nullterminal_closeterminal_writeterminal_resetterminal_resizeterminal_colsterminal_rowsterminal_titleterminal_pwdterminal_formatbuild_info_boolbuild_info_string
The intended structure is:
- The host loads
ghostty-vt.wasm. - The host implements
ghostty_host.*in terms of that runtime. - The MoonBit
wasm/wasm-gcartifact calls those imports throughbackend_wasm.mbt.
This keeps the MoonBit contract stable even though the actual runtime boundary
differs between js and wasm.
Validated in this repository with:
moon test --target native
moon check --target js
moon check --target wasm
moon check --target wasm-gcTo verify native/js parity with the same scenario:
bash scripts/check_native_js_parity.shTo verify wasm / wasm-gc parity with the same scenario:
bash scripts/check_wasm_parity.shThis uses a Node-based host adapter in scripts/ghostty_runtime_adapter.mjs
to provide the ghostty_bridge and ghostty_host imports expected by the
MoonBit wasm targets.