2209f022ee
* Bring libghostty-vt back behind a consumer-safe terminal_vt seam
- The framework module imports terminal_vt everywhere: a ghostty-vt wrapper where the dependency is safe to traverse, a stub (enabled=false) elsewhere
- Root builds resolve a lazy ghostty pin gated on being the build root, so consumers running this build script as a dependency never touch ghostty's graph
- Apps opt in via addAppArtifacts .ghostty_vt with their own pin; scaffolded builds keep the stub and stay free of harfbuzz/translate_c
* Live <terminal> sessions: the runtime-owned emulator behind a bound pty key
- terminal_session.zig: per-pty-key libghostty-vt sessions publishing resolved TerminalGrid snapshots, with the example tier's lossless outbound ring, query-answer write-back, key encoding, and theme-derived palette promoted into the runtime
- The effects engine grows a pty delivery tap (live drain and replay feed alike), so emulator state derives from exactly the journaled stream; outbound bytes ride the journaled ptyWrite verdict path
- The app loop installs the builder's terminal grid lookup, reconciles cols/rows from each element's laid-out frame into ptyResize, applies the scrollback source-wins echo, routes focused keys/IME text and wheel scrollback to the session, and dispatches on-terminal states
* Terminal session store tests: the element contract at the store seam
- Lookup-driven session creation, fed output as resolved cells, stdin-ordered write-back (keys, text, DSR answers), retained-ring flush, resize reconcile, scrollback source-wins, respawn reset, and wide-cell spacer backgrounds
- Fix the snapshot sizing multiply: @min against a comptime bound refines its result type, so the product needed explicit widening
* The terminal emulator is an app-owned pin, opted into per app
- Replace AppOptions.ghostty_vt with terminal_sessions: bool; the framework resolves the app's own lazy ghostty pin with the app module's target/optimize and the safe flags, so an app asks for live <terminal> sessions with one field instead of threading a module
- Drop the ghostty pin from this package's build.zig.zon: a pin here materializes into every consumer's package directory even when lazy and unused (measured 27 packages / 585 MB in a scaffolded app's package dir, now zero), so the toolkit's own builds always carry the terminal_vt stub
- Add src/terminal_session_tests_root.zig so an emulator-wired app build can run the session store's tests, which skip in this package's own suite
* examples/workbench: a live terminal beside a browser in one split
- Add the workbench example: a <split> with a <terminal pty={key}> pane and a browser pane (back, forward, reload, address bar), app-owned navigation history, and a hidden-inset titlebar with per-pane drag bands
- The app spawns a shell with fx.ptySpawn and binds the key in markup; no emulator wiring, key encoding, or grid plumbing lives in the app
- Register test-example-workbench, whose build also runs the runtime session store's tests since this build wires the emulator
* Document live terminal sessions and how a build opts in
- Terminal docs: the runtime owns the emulator behind a bound pty key, plus an "Enabling live sessions" section covering the app-owned lazy pin, terminal_sessions = true, and the terminal_sessions_enabled flag apps can gate tests on
- Add the changelog fragment for the live sessions, the opt-in emulator, and the workbench example
* Workbench: the shell owns the keyboard when the window opens
- Mark the terminal element autofocus, so typing lands in the live session without a click first — a terminal window's keyboard belongs to its shell
* Terminal runs measure the face they actually ink with
- Derive the cell width from a 16-glyph mono probe divided by its length — the advance a merged run walks — and leave it unrounded, so glyphs stay on the cells the painter's backgrounds, cursor, and selection draw from
- Carry the measurement seam on each run: a command's raster extent is its own declared bounds, and the estimator's 0.6 em mono pitch falls short of a wider host face (macOS resolves the mono id to the system monospaced face at 0.618 em when Geist Mono is absent), which sheared a full-width row's last cell to a two-pixel sliver
- Cover both with a painter test that paints a full-width row against a wide-pitch provider and asserts every run's bounds hold its ink
* Workbench README: what the divider drag does to focus
- `stty size` named as the visible proof that the pty re-grids with the pane
- the divider keeps the keyboard after a drag; clicking the terminal returns it
43 lines
2.0 KiB
Zig
43 lines
2.0 KiB
Zig
// This example owns its build for exactly one reason: live `<terminal>`
|
|
// sessions. `terminal_sessions = true` makes the framework resolve the
|
|
// lazy ghostty pin from THIS build.zig.zon (with the app module's
|
|
// target/optimize and the safe import flags) — the consumer-safe seam,
|
|
// so scaffolded apps never traverse ghostty's dependency graph.
|
|
// Everything else is the standard app build.
|
|
const std = @import("std");
|
|
const native_sdk = @import("native_sdk");
|
|
|
|
pub fn build(b: *std.Build) void {
|
|
const dep = b.dependency("native_sdk", .{});
|
|
const artifacts = native_sdk.addAppArtifacts(b, dep, .{
|
|
.name = "workbench",
|
|
.terminal_sessions = true,
|
|
});
|
|
|
|
// The toolkit's own terminal-session store tests run HERE, because
|
|
// this is a build that WIRES the emulator: the toolkit package pins
|
|
// none, so under its own `zig build test` those cases skip. Zig only
|
|
// runs tests from a compilation's root module, so they get their own
|
|
// test artifact — rooted at that file, carrying the framework
|
|
// module's imports (`terminal_vt` among them, the enabled half).
|
|
const sdk_mod = artifacts.tests.root_module.import_table.get("native_sdk").?;
|
|
const session_tests = b.addTest(.{
|
|
// Only the store's cases: a test root pulls in whatever its
|
|
// imports declare, and the rest of the toolkit's suite belongs to
|
|
// the toolkit's own `zig build test`.
|
|
.filters = &.{"terminal_session_tests"},
|
|
.root_module = b.createModule(.{
|
|
.root_source_file = dep.path("src/terminal_session_tests_root.zig"),
|
|
.target = sdk_mod.resolved_target,
|
|
.optimize = sdk_mod.optimize orelse .Debug,
|
|
}),
|
|
});
|
|
var imports = sdk_mod.import_table.iterator();
|
|
while (imports.next()) |entry| {
|
|
session_tests.root_module.addImport(entry.key_ptr.*, entry.value_ptr.*);
|
|
}
|
|
if (b.top_level_steps.get("test")) |test_step| {
|
|
test_step.step.dependOn(&b.addRunArtifact(session_tests).step);
|
|
}
|
|
}
|