An embedded terminal never answered OSC 10 or OSC 11 color queries, so programs such as Neovim that query the background to choose a dark or light scheme had to guess. A child that set only one default color was also ignored: after `OSC 11 ; rgb:ff/00/00` the background stayed black until an OSC 10 set the foreground too. The terminal was created without default colors, so Ghostty left its dynamic foreground and background unset. An unset color has no query reply, and the render state copies the terminal colors only when both are set. Native now passes the colors the compositor already used when they were unset: a black background, a white foreground, no cursor color (the cursor follows the foreground), and the default palette. As in xterm and Ghostty, OSC 12 and Kitty OSC 21 color queries now reply too, and OSC 110/111 reset to white and black. Reverse video (`CSI ? 5 h`) now also takes effect without OSC 10/11; this makes nothing worse for shells, Neovim, less, or htop by default, but rows already on screen are not redrawn when it changes, which shows only with an opt-in pausing visual bell or a program that leaves reverse video on, and a follow-up fixes that. Tests: the PTY response table gains OSC 10 (BEL) and OSC 11 (ST) query rows; the transparent-background test sends OSC 11 alone and checks the exact opaque and transparent background; the cursor state test sends OSC 10 alone and compares the whole cursor, including its color. Leaving the background, foreground, or cursor default unset or seeded wrongly fails at least one of them. Fixes #1605
Native API
OpenTUI uses Context-owned scenes and Sessions for production rendering, including text, editors, custom paint hooks, images, detached surfaces, and split output. Standalone resources use the same checked ownership model without a terminal.
Read Resources and ownership for resource bindings, text copy units, and scoped framebuffer access. Frames and output defines mutation admission, painted drafts, and transport completion. How Core uses native explains TypeScript staging and resource ownership. Host I/O and time defines Context file operations, Session deadlines, and native diagnostic clocks.
API surfaces
src/opentui.zigis the public Zig module. It exports the checkedContextAPI and raw Zig primitives, includingCliRenderer,NativeRenderable,OptimizedBuffer, text buffers, and pools. Callers manage the lifetimes of raw primitives. Importing a raw primitive does not add Context checks to it.include/opentui.hdefines the checkedot_*C ABI for Contexts, Sessions, scenes, drawing, text, editors, styles, leases, clipboard, and diagnostics. The ABI is experimental and at version 1. Core renders through it.../core/src/zig.tssupplies TypeScript wrappers over that checked ABI. Its checked signatures, callbacks, constants, and record layouts come fromnative-abi.generated.ts.
ABI generation and builds
Use C headers and libraries from the same revision. Set each versioned record's
exact struct_size and abi_version, and leave unused flags and reserved fields
zero. Follow the output and failure contract of each operation in the header.
From packages/core:
bun run generate:abi
bun run check:abi
bun run test:abi
scripts/native-abi.ts uses Zig Translate-C and
scripts/native-abi.zig reflection to derive scalar
widths, signatures, callback types, constants, record sizes, alignment, and field
offsets from the header. Pointer nullability, retention, address fields, and portable
buffer/ptr policy live in
scripts/native-abi-pointers.ts, because C
types cannot prove lifetimes. Review that metadata when ownership contracts change.
Do not edit generated bindings. check:abi detects stale output. Use
bun run check:abi --all-targets to also compare the layouts of all supported targets.
An unsupported record shape or calling convention fails the script. It does not
produce partial metadata. C compiler assertions also check complete function and
callback prototypes, record layouts, field types, and constant values. Translate-C
alone is not enough, because it can drop callback calling-convention attributes and
ignore #pragma pack.
From packages/native, bun run build installs the header and libraries under
lib/<target>/ for the host target. Linux and macOS produce libopentui.a beside
the shared library. Windows produces opentui-static.lib, opentui.lib for DLL
imports, and opentui.dll. Static linkage also needs the platform and C++ runtime
libraries. zig build -Dall builds all supported targets.
zig build -Dlibrary-target=<target> builds one target.
zig build test-abi --summary all
This command runs the C fixture against the static and shared libraries on the host.
On a glibc Linux host, the fixture targets glibc 2.17. check:abi --all-targets
checks layouts for all eight supported targets. It does not test macOS or Windows
runtime linkage or terminal behavior.
The external examples/hello package imports the public Zig module
without JavaScript.