a73x

9a21f071

refactor: one term component — wire contract, engine, delta and replay core behind a single seam

a73x   2026-08-30 12:59

Commit message
refactor: one term component — wire contract, engine, delta and replay core behind a single seam

build.zig
Old New
@@ -113,8 +113,17 @@ const ModSpec = struct {
113 113
114 const mod_table = [_]ModSpec{ 114 const mod_table = [_]ModSpec{
115 // ---- layer 0: imports nothing internal in production ---- 115 // ---- layer 0: imports nothing internal in production ----
116 .{ .name = "protocol", .path = "src/engine/protocol.zig", .layer = 0, .wasm = true }, 116 // The terminal component, one row over four files: the wire contract,
117 .{ .name = "engine", .path = "src/engine/engine.zig", .layer = 0, .wasm = true }, 117 // the authoritative engine, the daemon-side delta minting and the one
118 // replay core, reached as `term.protocol`, `.engine`, `.delta`,
119 // `.replica`. They are one owner — protocol.zig owns the bytes a delta
120 // payload is made of and delta.zig owns which rows go into one — and
121 // only this root is a seam, so a second module claiming any of the four
122 // is a file-in-multiple-modules compile error. It imports nothing
123 // internal, which is what lets the tracker and the replay core be
124 // driven by a test holding an engine and no socket; and the whole
125 // component is platform-free, so `mux_core.wasm` compiles it.
126 .{ .name = "term", .path = "src/engine/term.zig", .layer = 0, .wasm = true },
118 .{ .name = "pty", .path = "src/server/pty.zig", .layer = 0, .link_libc = true }, 127 .{ .name = "pty", .path = "src/server/pty.zig", .layer = 0, .link_libc = true },
119 // The QUIC vocabulary both ends share: the one @cImport of the vendored 128 // The QUIC vocabulary both ends share: the one @cImport of the vendored
120 // stack, the key, the wire constants, the egress ring. It has to be ONE 129 // stack, the key, the wire constants, the egress ring. It has to be ONE
@@ -155,20 +164,11 @@ const mod_table = [_]ModSpec{
155 // asks the OS about the process it is in — a question no headless 164 // asks the OS about the process it is in — a question no headless
156 // client may spell. 165 // client may spell.
157 .{ .name = "spawn", .path = "src/cli/spawn.zig", .layer = 1, .link_libc = true }, 166 .{ .name = "spawn", .path = "src/cli/spawn.zig", .layer = 1, .link_libc = true },
158 // Row-level change tracking behind the delta stream. Engine plus
159 // protocol and nothing else — no daemon, no clients — so the tracker's
160 // own tests drive it with an engine and no socket in sight.
161 .{ .name = "delta", .path = "src/engine/delta.zig", .layer = 1, .imports = &.{ "engine", "protocol" } },
162 // The replay core: snapshot/delta application and the resume
163 // coordinates, shared by the CLI client, the wasm core, and the
164 // server's test fixtures. Engine plus protocol and nothing else, and
165 // deliberately platform-free — it must compile for wasm32.
166 .{ .name = "replica", .path = "src/engine/replica.zig", .layer = 1, .wasm = true, .imports = &.{ "engine", "protocol" } },
167 // Replays a captured client stdout stream and prints the final grid in 167 // Replays a captured client stdout stream and prints the final grid in
168 // `muxd dump`'s formats — the client half of the M11 render-vs-dump 168 // `muxd dump`'s formats — the client half of the M11 render-vs-dump
169 // convergence check. Imports the engine module so both sides of the 169 // convergence check. Imports term so both sides of the diff go through
170 // diff go through the same ghostty-vt and the same formatter. 170 // the same ghostty-vt and the same formatter.
171 .{ .name = "render", .path = "test/render.zig", .layer = 1, .imports = &.{"engine"} }, 171 .{ .name = "render", .path = "test/render.zig", .layer = 1, .imports = &.{"term"} },
172 // The pty-driving e2e fixture: real client on a pty slave, scripted 172 // The pty-driving e2e fixture: real client on a pty slave, scripted
173 // from stdin (M12). Imports pty so the product's own module is the one 173 // from stdin (M12). Imports pty so the product's own module is the one
174 // under it. 174 // under it.
@@ -185,15 +185,17 @@ const mod_table = [_]ModSpec{
185 // daemon itself when nobody handed it a --key — and for the shim 185 // daemon itself when nobody handed it a --key — and for the shim
186 // directory shell integration writes under the same 0700 policy. 186 // directory shell integration writes under the same 0700 policy.
187 // `pty` stays a row of its own: the ptyclient fixture consumes it. 187 // `pty` stays a row of its own: the ptyclient fixture consumes it.
188 .{ .name = "daemon", .path = "src/server/server.zig", .layer = 2, .link_libc = true, .imports = &.{ "engine", "pty", "protocol", "delta", "sockpath", "quic", "xdg", "proxy" }, .test_imports = &.{ "replica", "testtmp" }, .quic_tests = true }, 188 .{ .name = "daemon", .path = "src/server/server.zig", .layer = 2, .link_libc = true, .imports = &.{ "term", "pty", "sockpath", "quic", "xdg", "proxy" }, .test_imports = &.{"testtmp"}, .quic_tests = true },
189 // The agent-facing client. It speaks frames and owns no terminal, which 189 // The agent-facing client. It speaks frames and owns no terminal, which
190 // is the whole point — it attaches at 0x0 and never claims the grid. 190 // is the whole point — it attaches at 0x0 and never claims the grid.
191 // The transport modules are the CLI client's, minus everything that 191 // The transport modules are the CLI client's, minus everything that
192 // renders: `quic` for the remote arm and `xdg` for the one 192 // renders: `quic` for the remote arm and `xdg` for the one
193 // key-resolution rule all three binaries obey. Deliberately still no 193 // key-resolution rule all three binaries obey. Of `term` it spells the
194 // engine and no replica — muxa has nothing to draw. 194 // wire contract and nothing else — no engine and no replica, muxa having
195 .{ .name = "agent", .path = "src/cli/muxa.zig", .layer = 2, .link_libc = true, .imports = &.{ "protocol", "sockpath", "quic", "xdg", "cliflags" }, .quic_tests = true }, 195 // nothing to draw: a fact of muxa.zig itself, which the one-row component
196 .{ .name = "wsclient", .path = "test/wsclient.zig", .layer = 2, .link_libc = true, .imports = &.{ "engine", "replica", "protocol", "script" } }, 196 // no longer refuses on its behalf.
197 .{ .name = "agent", .path = "src/cli/muxa.zig", .layer = 2, .link_libc = true, .imports = &.{ "term", "sockpath", "quic", "xdg", "cliflags" }, .quic_tests = true },
198 .{ .name = "wsclient", .path = "test/wsclient.zig", .layer = 2, .link_libc = true, .imports = &.{ "term", "script" } },
197 // ---- layer 3 ---- 199 // ---- layer 3 ----
198 // Dialling, and what a chord means. The client is the only thing that 200 // Dialling, and what a chord means. The client is the only thing that
199 // predicts — the overlay is a local display decision and never becomes 201 // predicts — the overlay is a local display decision and never becomes
@@ -208,7 +210,7 @@ const mod_table = [_]ModSpec{
208 // sits here rather than in either front so the CLI wall and the browser 210 // sits here rather than in either front so the CLI wall and the browser
209 // hub resolve a host line the same way. Nothing here WRITES that file — 211 // hub resolve a host line the same way. Nothing here WRITES that file —
210 // `wall_host.recordHost` and `webhub_main` do. 212 // `wall_host.recordHost` and `webhub_main` do.
211 .{ .name = "client", .path = "src/client/client.zig", .layer = 3, .link_libc = true, .imports = &.{ "protocol", "replica", "quic", "xdg", "sockpath" }, .test_imports = &.{"testtmp"}, .quic_tests = true }, 213 .{ .name = "client", .path = "src/client/client.zig", .layer = 3, .link_libc = true, .imports = &.{ "term", "quic", "xdg", "sockpath" }, .test_imports = &.{"testtmp"}, .quic_tests = true },
212 // ---- layer 4 ---- 214 // ---- layer 4 ----
213 // The muxweb hub's HTTP/WebSocket decisions: Origin gate, route table, 215 // The muxweb hub's HTTP/WebSocket decisions: Origin gate, route table,
214 // WS endpoint naming. Assets are injected (the exe root @embedFiles 216 // WS endpoint naming. Assets are injected (the exe root @embedFiles
@@ -217,7 +219,7 @@ const mod_table = [_]ModSpec{
217 // module's root directory is the dirname of its root file, and 219 // module's root directory is the dirname of its root file, and
218 // `@import("../client/webhub.zig")` from src/cli is "import of file 220 // `@import("../client/webhub.zig")` from src/cli is "import of file
219 // outside module path" — a compiler rule, not a table choice. 221 // outside module path" — a compiler rule, not a table choice.
220 .{ .name = "webhub", .path = "src/client/webhub.zig", .layer = 4, .imports = &.{ "protocol", "client" }, .quic_tests = true }, 222 .{ .name = "webhub", .path = "src/client/webhub.zig", .layer = 4, .imports = &.{ "term", "client" }, .quic_tests = true },
221 // The CLI wall (`mux wall`): multiattach stripes in one terminal, one of 223 // The CLI wall (`mux wall`): multiattach stripes in one terminal, one of
222 // which can be ZOOMED — promoted to the terminal's size and typed 224 // which can be ZOOMED — promoted to the terminal's size and typed
223 // through. Same layer as webhub for the same reason — both sit on 225 // through. Same layer as webhub for the same reason — both sit on
@@ -226,11 +228,11 @@ const mod_table = [_]ModSpec{
226 // The interaction loop, the painter, the selector and the prediction 228 // The interaction loop, the painter, the selector and the prediction
227 // overlay are CHILD FILES of this root rather than modules of their own, 229 // overlay are CHILD FILES of this root rather than modules of their own,
228 // so nothing outside src/tui/ can name one: a second module claiming any 230 // so nothing outside src/tui/ can name one: a second module claiming any
229 // of those files is a file-in-multiple-modules compile error. `replica` 231 // of those files is a file-in-multiple-modules compile error. `term` is
230 // is what a painter and a keyboard loop want and the wall itself never 232 // the children's as much as the root's: `term.replica` is what a painter
231 // spells; the decoder and the key table they also want reach them 233 // and a keyboard loop want and the wall itself never spells; the decoder
232 // through `client`'s seams. 234 // and the key table they also want reach them through `client`'s seams.
233 .{ .name = "wall", .path = "src/tui/wallview.zig", .layer = 4, .link_libc = true, .imports = &.{ "protocol", "client", "proxy", "engine", "replica", "spawn" }, .test_imports = &.{"testtmp"}, .quic_tests = true }, 235 .{ .name = "wall", .path = "src/tui/wallview.zig", .layer = 4, .link_libc = true, .imports = &.{ "term", "client", "proxy", "spawn" }, .test_imports = &.{"testtmp"}, .quic_tests = true },
234 // ---- layer 6: the one binary ---- 236 // ---- layer 6: the one binary ----
235 // Four words, one image — and one row: the daemon's entrypoint, the 237 // Four words, one image — and one row: the daemon's entrypoint, the
236 // client's and the hub's are CHILD FILES of the dispatcher, so a second 238 // client's and the hub's are CHILD FILES of the dispatcher, so a second
@@ -244,12 +246,12 @@ const mod_table = [_]ModSpec{
244 // two spell it differently — and for `client.hosts`, which owns the host 246 // two spell it differently — and for `client.hosts`, which owns the host
245 // grammar and the state file, so `mux web`'s argv is parsed by the SAME 247 // grammar and the state file, so `mux web`'s argv is parsed by the SAME
246 // rules `mux hosts add` is: one grammar, not two. Then the wall the 248 // rules `mux hosts add` is: one grammar, not two. Then the wall the
247 // no-arg client opens, `protocol` for validSessionName (a bad --session 249 // no-arg client opens, `term` for `term.protocol`'s SessionName.parseCLI
248 // is a usage error at parse, not bytes a daemon downstream must refuse) 250 // (a bad --session is a usage error at parse, not bytes a daemon
249 // and `sockpath` for the sun_path bound every verb checks before acting 251 // downstream must refuse) and `sockpath` for the sun_path bound every verb
250 // on a path. `testtmp` is the keygen round-trip's: it needs a directory 252 // checks before acting on a path. `testtmp` is the keygen round-trip's:
251 // to generate into, which the daemon itself never touches. 253 // it needs a directory to generate into, which the daemon never touches.
252 .{ .name = "mux", .path = "src/cli/mux.zig", .layer = 6, .link_libc = true, .imports = &.{ "daemon", "client", "wall", "agent", "webhub", "protocol", "proxy", "quic", "xdg", "spawn", "sockpath", "cliflags" }, .test_imports = &.{"testtmp"}, .quic_tests = true }, 254 .{ .name = "mux", .path = "src/cli/mux.zig", .layer = 6, .link_libc = true, .imports = &.{ "daemon", "client", "wall", "agent", "webhub", "term", "proxy", "quic", "xdg", "spawn", "sockpath", "cliflags" }, .test_imports = &.{"testtmp"}, .quic_tests = true },
253 }; 255 };
254 256
255 /// Comptime row lookup. Every hand-written module name in this file goes 257 /// Comptime row lookup. Every hand-written module name in this file goes
@@ -295,7 +297,7 @@ comptime {
295 } 297 }
296 298
297 /// The domain folders under `src/`. A module lives in the folder of the thing 299 /// The domain folders under `src/`. A module lives in the folder of the thing
298 /// that owns it, and this is what "owns" costs: an app that links `engine` and 300 /// that owns it, and this is what "owns" costs: an app that links `term` and
299 /// `client` to paint its own way must be able to do so without dragging a 301 /// `client` to paint its own way must be able to do so without dragging a
300 /// terminal in. `mux_core.wasm` already links that set with no tty anywhere, 302 /// terminal in. `mux_core.wasm` already links that set with no tty anywhere,
301 /// which is the proof the rule generalises past this repo's own binaries. 303 /// which is the proof the rule generalises past this repo's own binaries.
@@ -782,7 +784,7 @@ fn docGate(b: *std.Build, target: std.Build.ResolvedTarget, check_step: *std.Bui
782 } 784 }
783 785
784 /// Test registration order. Doctrine-laden and deliberately NOT derived 786 /// Test registration order. Doctrine-laden and deliberately NOT derived
785 /// from the layers: delta and sockpath run BEFORE daemon because their 787 /// from the layers: term and sockpath run BEFORE daemon because their
786 /// tests are seconds-long and socket-free, while a regression in either 788 /// tests are seconds-long and socket-free, while a regression in either
787 /// can wedge a daemon test that waits on a client forever — and a wedged 789 /// can wedge a daemon test that waits on a client forever — and a wedged
788 /// step prints nothing at all. Failing first is what makes the catch 790 /// step prints nothing at all. Failing first is what makes the catch
@@ -791,11 +793,11 @@ fn docGate(b: *std.Build, target: std.Build.ResolvedTarget, check_step: *std.Bui
791 /// every argument parser but muxa's, its mains being child files — a test 793 /// every argument parser but muxa's, its mains being child files — a test
792 /// that is never built is not a test (decisions.md). 794 /// that is never built is not a test (decisions.md).
793 const test_order = [_][]const u8{ 795 const test_order = [_][]const u8{
794 "script", "protocol", "engine", "pty", "delta", 796 "script", "term", "pty", "webhub", "wall",
795 "replica", "webhub", "wall", "sockpath", "agent", 797 "sockpath", "agent", "daemon", "client", "proxy",
796 "daemon", "client", "proxy", "quic", "testtmp", 798 "quic", "testtmp", "rawmode", "delaypipe", "xdg",
797 "rawmode", "delaypipe", "xdg", "spawn", "render", 799 "spawn", "render", "ptyclient", "wsclient", "cliflags",
798 "ptyclient", "wsclient", "cliflags", "mux", 800 "mux",
799 }; 801 };
800 802
801 comptime { 803 comptime {
@@ -891,7 +893,7 @@ pub fn build(b: *std.Build) void {
891 // that wiring actually uses (an unused local is a compile error). 893 // that wiring actually uses (an unused local is a compile error).
892 // Dep edges (ghostty) and build_options stay outside the table's 894 // Dep edges (ghostty) and build_options stay outside the table's
893 // jurisdiction, explicit. 895 // jurisdiction, explicit.
894 const engine_mod = mods[comptime idxOf("engine")]; 896 const term_mod = mods[comptime idxOf("term")];
895 const mux_mod = mods[comptime idxOf("mux")]; 897 const mux_mod = mods[comptime idxOf("mux")];
896 const agent_mod = mods[comptime idxOf("agent")]; 898 const agent_mod = mods[comptime idxOf("agent")];
897 const rawmode_mod = mods[comptime idxOf("rawmode")]; 899 const rawmode_mod = mods[comptime idxOf("rawmode")];
@@ -900,8 +902,11 @@ pub fn build(b: *std.Build) void {
900 const ptyclient_mod = mods[comptime idxOf("ptyclient")]; 902 const ptyclient_mod = mods[comptime idxOf("ptyclient")];
901 const wsclient_mod = mods[comptime idxOf("wsclient")]; 903 const wsclient_mod = mods[comptime idxOf("wsclient")];
902 904
905 // The dep is named by engine.zig, a file of the term module: an import
906 // name is resolved in the module that owns the spelling file, so the
907 // handle belongs on the component's root and nowhere else.
903 if (ghostty_dep) |dep| { 908 if (ghostty_dep) |dep| {
904 engine_mod.addImport("ghostty-vt", dep.module("ghostty-vt")); 909 term_mod.addImport("ghostty-vt", dep.module("ghostty-vt"));
905 } 910 }
906 // ONE module object, shared: the two rows link into one binary, and a 911 // ONE module object, shared: the two rows link into one binary, and a
907 // second instance of the same root file in one compilation is "file 912 // second instance of the same root file in one compilation is "file
@@ -950,9 +955,9 @@ pub fn build(b: *std.Build) void {
950 b.installArtifact(ptyclient_exe); 955 b.installArtifact(ptyclient_exe);
951 956
952 // ---- The wasm core (M-web Task 4) ---- 957 // ---- The wasm core (M-web Task 4) ----
953 // A SECOND resolved target: modules are target-bound, so the three 958 // A SECOND resolved target: modules are target-bound, so the wasm-clean
954 // wasm-clean modules (engine, protocol, replica) and the 959 // row — the term component — and the ghostty dependency are
955 // ghostty dependency are instantiated again against wasm32. Always 960 // instantiated again against wasm32. Always
956 // ReleaseSmall — the artifact is @embedFile'd into muxweb, and its 961 // ReleaseSmall — the artifact is @embedFile'd into muxweb, and its
957 // Debug build is 3.7MB against ReleaseSmall's 345KB (spike-measured). 962 // Debug build is 3.7MB against ReleaseSmall's 345KB (spike-measured).
958 // Safety checks are the price; the native test suite runs the same 963 // Safety checks are the price; the native test suite runs the same
@@ -980,22 +985,20 @@ pub fn build(b: *std.Build) void {
980 } 985 }
981 } 986 }
982 } 987 }
983 const engine_wasm_mod = wasm_mods[comptime idxOf("engine")].?; 988 const term_wasm_mod = wasm_mods[comptime idxOf("term")].?;
984 if (ghostty_wasm_dep) |dep| { 989 if (ghostty_wasm_dep) |dep| {
985 engine_wasm_mod.addImport("ghostty-vt", dep.module("ghostty-vt")); 990 term_wasm_mod.addImport("ghostty-vt", dep.module("ghostty-vt"));
986 } 991 }
987 const wasm_core_mod = wasmMod(b, wasm_target, "src/client/wasm_core.zig"); 992 const wasm_core_mod = wasmMod(b, wasm_target, "src/client/wasm_core.zig");
988 wasm_core_mod.addImport("engine", engine_wasm_mod); 993 wasm_core_mod.addImport("term", term_wasm_mod);
989 wasm_core_mod.addImport("protocol", wasm_mods[comptime idxOf("protocol")].?);
990 wasm_core_mod.addImport("replica", wasm_mods[comptime idxOf("replica")].?);
991 // Compile a tiny, never-embedded canary that calls the semantic decoder 994 // Compile a tiny, never-embedded canary that calls the semantic decoder
992 // through typed mode and clipboard result paths. This build-only object 995 // through typed mode and clipboard result paths. This build-only object
993 // forces the production API and its validation helpers through wasm 996 // forces the production API and its validation helpers through wasm
994 // semantic analysis without adding runtime exports or behavior. It 997 // semantic analysis without adding runtime exports or behavior. It
995 // names `protocol` because client_core.zig, its relative child now, 998 // names `term` because client_core.zig, its relative child now,
996 // imports that module by name. 999 // imports that module by name.
997 const client_core_wasm_check_mod = wasmMod(b, wasm_target, "src/client/client_core_wasm_check.zig"); 1000 const client_core_wasm_check_mod = wasmMod(b, wasm_target, "src/client/client_core_wasm_check.zig");
998 client_core_wasm_check_mod.addImport("protocol", wasm_mods[comptime idxOf("protocol")].?); 1001 client_core_wasm_check_mod.addImport("term", term_wasm_mod);
999 const client_core_wasm_guard = b.addObject(.{ 1002 const client_core_wasm_guard = b.addObject(.{
1000 .name = "client_core_wasm_check", 1003 .name = "client_core_wasm_check",
1001 .root_module = client_core_wasm_check_mod, 1004 .root_module = client_core_wasm_check_mod,
docscheck.budget
Old New
@@ -59,3 +59,4 @@ wall_test_layout.zig 0
59 wall_test_wall.zig 0 59 wall_test_wall.zig 0
60 mux.zig 0 60 mux.zig 0
61 askpass.zig 0 61 askpass.zig 0
62 term.zig 0
src/cli/main.zig
Old New
@@ -6,7 +6,7 @@
6 6
7 const std = @import("std"); 7 const std = @import("std");
8 const Server = @import("daemon").Server; 8 const Server = @import("daemon").Server;
9 const proto = @import("protocol"); 9 const proto = @import("term").protocol;
10 const proxy = @import("proxy"); 10 const proxy = @import("proxy");
11 const quic = @import("quic"); 11 const quic = @import("quic");
12 const quic_server = @import("daemon").quic_server; 12 const quic_server = @import("daemon").quic_server;
src/cli/mux_main.zig
Old New
@@ -15,7 +15,7 @@
15 //! about a session. 15 //! about a session.
16 const std = @import("std"); 16 const std = @import("std");
17 const client = @import("client"); 17 const client = @import("client");
18 const proto = @import("protocol"); 18 const proto = @import("term").protocol;
19 const build_options = @import("build_options"); 19 const build_options = @import("build_options");
20 const xdg = @import("xdg"); 20 const xdg = @import("xdg");
21 const spawn = @import("spawn"); 21 const spawn = @import("spawn");
src/cli/muxa.zig
Old New
@@ -15,7 +15,7 @@
15 //! and a `run` whose command failed still exits 0 because the question was 15 //! and a `run` whose command failed still exits 0 because the question was
16 //! answered. 16 //! answered.
17 const std = @import("std"); 17 const std = @import("std");
18 const proto = @import("protocol"); 18 const proto = @import("term").protocol;
19 const sockpath = @import("sockpath"); 19 const sockpath = @import("sockpath");
20 const quic = @import("quic"); 20 const quic = @import("quic");
21 const xdg = @import("xdg"); 21 const xdg = @import("xdg");
src/client/client.zig
Old New
@@ -13,8 +13,8 @@
13 //! `adopt`ed by the thread that will own it — plus the pure helpers the pump 13 //! `adopt`ed by the thread that will own it — plus the pure helpers the pump
14 //! asks for by name. Everything at the terminal is interact.zig's. 14 //! asks for by name. Everything at the terminal is interact.zig's.
15 const std = @import("std"); 15 const std = @import("std");
16 const Replica = @import("replica").Replica; 16 const Replica = @import("term").replica.Replica;
17 const proto = @import("protocol"); 17 const proto = @import("term").protocol;
18 const TmpDir = @import("testtmp").TmpDir; 18 const TmpDir = @import("testtmp").TmpDir;
19 const quic = @import("quic"); 19 const quic = @import("quic");
20 const xdg = @import("xdg"); 20 const xdg = @import("xdg");
src/client/client_core.zig
Old New
@@ -1,5 +1,5 @@
1 const std = @import("std"); 1 const std = @import("std");
2 const proto = @import("protocol"); 2 const proto = @import("term").protocol;
3 3
4 /// The bytes BORROW the frame payload; copy before reusing it. 4 /// The bytes BORROW the frame payload; copy before reusing it.
5 pub const ClipboardSet = struct { 5 pub const ClipboardSet = struct {
src/client/wasm_core.zig
Old New
@@ -18,10 +18,10 @@
18 18
19 const std = @import("std"); 19 const std = @import("std");
20 const builtin = @import("builtin"); 20 const builtin = @import("builtin");
21 const Engine = @import("engine").Engine; 21 const Engine = @import("term").engine.Engine;
22 const Replica = @import("replica").Replica; 22 const Replica = @import("term").replica.Replica;
23 const keymap = @import("keymap.zig"); 23 const keymap = @import("keymap.zig");
24 const proto = @import("protocol"); 24 const proto = @import("term").protocol;
25 const client_core = @import("client_core.zig"); 25 const client_core = @import("client_core.zig");
26 26
27 /// std.heap.wasm_allocator grows linear memory with @wasmMemoryGrow and 27 /// std.heap.wasm_allocator grows linear memory with @wasmMemoryGrow and
src/client/webhub.zig
Old New
@@ -14,7 +14,7 @@
14 //! `/tiles`. A browser is not somewhere to type one. 14 //! `/tiles`. A browser is not somewhere to type one.
15 15
16 const std = @import("std"); 16 const std = @import("std");
17 const proto = @import("protocol"); 17 const proto = @import("term").protocol;
18 const client = @import("client"); 18 const client = @import("client");
19 19
20 pub const default_port: u16 = 7681; 20 pub const default_port: u16 = 7681;
src/engine/delta.zig
Old New
@@ -8,8 +8,8 @@
8 //! no sockets — which is what lets the tracker be driven directly by a test 8 //! no sockets — which is what lets the tracker be driven directly by a test
9 //! holding nothing but an engine. 9 //! holding nothing but an engine.
10 const std = @import("std"); 10 const std = @import("std");
11 const Engine = @import("engine").Engine; 11 const Engine = @import("engine.zig").Engine;
12 const proto = @import("protocol"); 12 const proto = @import("protocol.zig");
13 13
14 const Wyhash = std.hash.Wyhash; 14 const Wyhash = std.hash.Wyhash;
15 15
src/engine/replica.zig
Old New
@@ -14,8 +14,8 @@
14 //! bookkeeping around it, not a second owner. 14 //! bookkeeping around it, not a second owner.
15 15
16 const std = @import("std"); 16 const std = @import("std");
17 const Engine = @import("engine").Engine; 17 const Engine = @import("engine.zig").Engine;
18 const proto = @import("protocol"); 18 const proto = @import("protocol.zig");
19 19
20 pub const Replica = struct { 20 pub const Replica = struct {
21 alloc: std.mem.Allocator, 21 alloc: std.mem.Allocator,
src/engine/term.zig
Old New
@@ -0,0 +1,16 @@
1 //! The terminal component: the wire contract, the authoritative engine,
2 //! the delta minting that feeds replicas, and the one replay core. One
3 //! table row — the four files are one owner, and only this root is a seam,
4 //! so a second module claiming any of them is a file-in-multiple-modules
5 //! compile error.
6 pub const protocol = @import("protocol.zig");
7 pub const engine = @import("engine.zig");
8 pub const delta = @import("delta.zig");
9 pub const replica = @import("replica.zig");
10
11 test {
12 _ = protocol;
13 _ = engine;
14 _ = delta;
15 _ = replica;
16 }
src/server/cmd.zig
Old New
@@ -6,8 +6,8 @@
6 //! wholesale — latest wins, no attempt to detect the nesting — and a stray 6 //! wholesale — latest wins, no attempt to detect the nesting — and a stray
7 //! A at idle is a no-op. 7 //! A at idle is a no-op.
8 const std = @import("std"); 8 const std = @import("std");
9 const proto = @import("protocol"); 9 const proto = @import("term").protocol;
10 const Engine = @import("engine").Engine; 10 const Engine = @import("term").engine.Engine;
11 11
12 pub const Tracker = struct { 12 pub const Tracker = struct {
13 phase: proto.CmdPhase = .at_prompt, 13 phase: proto.CmdPhase = .at_prompt,
src/server/server.zig
Old New
@@ -9,15 +9,15 @@
9 //! Single-threaded; pumpOnce is one poll iteration so tests can drive the 9 //! Single-threaded; pumpOnce is one poll iteration so tests can drive the
10 //! loop. 10 //! loop.
11 const std = @import("std"); 11 const std = @import("std");
12 const Engine = @import("engine").Engine; 12 const Engine = @import("term").engine.Engine;
13 const Pty = @import("pty").Pty; 13 const Pty = @import("pty").Pty;
14 const proto = @import("protocol"); 14 const proto = @import("term").protocol;
15 const DeltaTracker = @import("delta").DeltaTracker; 15 const DeltaTracker = @import("term").delta.DeltaTracker;
16 const cmdmod = @import("cmd.zig"); 16 const cmdmod = @import("cmd.zig");
17 const shellint = @import("shellint.zig"); 17 const shellint = @import("shellint.zig");
18 // Test-only consumer (applyFrame): the tests replay daemon frames through 18 // Test-only consumer (applyFrame): the tests replay daemon frames through
19 // the production client's replay core rather than a hand-rolled twin. 19 // the production client's replay core rather than a hand-rolled twin.
20 const replica_mod = @import("replica"); 20 const replica_mod = @import("term").replica;
21 const sockpath = @import("sockpath"); 21 const sockpath = @import("sockpath");
22 const quic = @import("quic"); 22 const quic = @import("quic");
23 const xdg = @import("xdg"); 23 const xdg = @import("xdg");
src/server/server_agent.zig
Old New
@@ -17,7 +17,7 @@
17 //! four calls. 17 //! four calls.
18 18
19 const std = @import("std"); 19 const std = @import("std");
20 const proto = @import("protocol"); 20 const proto = @import("term").protocol;
21 const xdg = @import("xdg"); 21 const xdg = @import("xdg");
22 const srv_mod = @import("server.zig"); 22 const srv_mod = @import("server.zig");
23 const Server = srv_mod.Server; 23 const Server = srv_mod.Server;
src/server/server_sessions.zig
Old New
@@ -18,8 +18,8 @@
18 //! after the first to the last one's bytes. 18 //! after the first to the last one's bytes.
19 19
20 const std = @import("std"); 20 const std = @import("std");
21 const proto = @import("protocol"); 21 const proto = @import("term").protocol;
22 const Engine = @import("engine").Engine; 22 const Engine = @import("term").engine.Engine;
23 const Pty = @import("pty").Pty; 23 const Pty = @import("pty").Pty;
24 const srv_mod = @import("server.zig"); 24 const srv_mod = @import("server.zig");
25 const Server = srv_mod.Server; 25 const Server = srv_mod.Server;
src/server/server_test_agent.zig
Old New
@@ -1,5 +1,5 @@
1 const std = @import("std"); 1 const std = @import("std");
2 const proto = @import("protocol"); 2 const proto = @import("term").protocol;
3 const quic = @import("quic"); 3 const quic = @import("quic");
4 const quic_server = @import("quic_server.zig"); 4 const quic_server = @import("quic_server.zig");
5 const TmpDir = @import("testtmp").TmpDir; 5 const TmpDir = @import("testtmp").TmpDir;
src/server/server_test_attach.zig
Old New
@@ -1,6 +1,6 @@
1 const std = @import("std"); 1 const std = @import("std");
2 const Engine = @import("engine").Engine; 2 const Engine = @import("term").engine.Engine;
3 const proto = @import("protocol"); 3 const proto = @import("term").protocol;
4 const TmpDir = @import("testtmp").TmpDir; 4 const TmpDir = @import("testtmp").TmpDir;
5 const h = @import("server_test_harness.zig"); 5 const h = @import("server_test_harness.zig");
6 const srv_mod = @import("server.zig"); 6 const srv_mod = @import("server.zig");
src/server/server_test_await.zig
Old New
@@ -1,5 +1,5 @@
1 const std = @import("std"); 1 const std = @import("std");
2 const proto = @import("protocol"); 2 const proto = @import("term").protocol;
3 const shellint = @import("shellint.zig"); 3 const shellint = @import("shellint.zig");
4 const TmpDir = @import("testtmp").TmpDir; 4 const TmpDir = @import("testtmp").TmpDir;
5 const h = @import("server_test_harness.zig"); 5 const h = @import("server_test_harness.zig");
src/server/server_test_clipboard.zig
Old New
@@ -1,6 +1,6 @@
1 const std = @import("std"); 1 const std = @import("std");
2 const Engine = @import("engine").Engine; 2 const Engine = @import("term").engine.Engine;
3 const proto = @import("protocol"); 3 const proto = @import("term").protocol;
4 const TmpDir = @import("testtmp").TmpDir; 4 const TmpDir = @import("testtmp").TmpDir;
5 const h = @import("server_test_harness.zig"); 5 const h = @import("server_test_harness.zig");
6 const srv_mod = @import("server.zig"); 6 const srv_mod = @import("server.zig");
src/server/server_test_deliver.zig
Old New
@@ -1,5 +1,5 @@
1 const std = @import("std"); 1 const std = @import("std");
2 const proto = @import("protocol"); 2 const proto = @import("term").protocol;
3 const TmpDir = @import("testtmp").TmpDir; 3 const TmpDir = @import("testtmp").TmpDir;
4 const h = @import("server_test_harness.zig"); 4 const h = @import("server_test_harness.zig");
5 const srv_mod = @import("server.zig"); 5 const srv_mod = @import("server.zig");
src/server/server_test_harness.zig
Old New
@@ -1,7 +1,7 @@
1 const std = @import("std"); 1 const std = @import("std");
2 const Engine = @import("engine").Engine; 2 const Engine = @import("term").engine.Engine;
3 const proto = @import("protocol"); 3 const proto = @import("term").protocol;
4 const replica_mod = @import("replica"); 4 const replica_mod = @import("term").replica;
5 const quic = @import("quic"); 5 const quic = @import("quic");
6 const quic_server = @import("quic_server.zig"); 6 const quic_server = @import("quic_server.zig");
7 const TmpDir = @import("testtmp").TmpDir; 7 const TmpDir = @import("testtmp").TmpDir;
src/server/server_test_modes.zig
Old New
@@ -1,5 +1,5 @@
1 const std = @import("std"); 1 const std = @import("std");
2 const proto = @import("protocol"); 2 const proto = @import("term").protocol;
3 const TmpDir = @import("testtmp").TmpDir; 3 const TmpDir = @import("testtmp").TmpDir;
4 const h = @import("server_test_harness.zig"); 4 const h = @import("server_test_harness.zig");
5 const srv_mod = @import("server.zig"); 5 const srv_mod = @import("server.zig");
src/server/server_test_quic.zig
Old New
@@ -1,6 +1,6 @@
1 const std = @import("std"); 1 const std = @import("std");
2 const Engine = @import("engine").Engine; 2 const Engine = @import("term").engine.Engine;
3 const proto = @import("protocol"); 3 const proto = @import("term").protocol;
4 const quic = @import("quic"); 4 const quic = @import("quic");
5 const quic_server = @import("quic_server.zig"); 5 const quic_server = @import("quic_server.zig");
6 const TmpDir = @import("testtmp").TmpDir; 6 const TmpDir = @import("testtmp").TmpDir;
src/server/server_test_session.zig
Old New
@@ -1,6 +1,6 @@
1 const std = @import("std"); 1 const std = @import("std");
2 const Engine = @import("engine").Engine; 2 const Engine = @import("term").engine.Engine;
3 const proto = @import("protocol"); 3 const proto = @import("term").protocol;
4 const quic = @import("quic"); 4 const quic = @import("quic");
5 const quic_server = @import("quic_server.zig"); 5 const quic_server = @import("quic_server.zig");
6 const xdg = @import("xdg"); 6 const xdg = @import("xdg");
src/server/server_test_upgrade.zig
Old New
@@ -1,5 +1,5 @@
1 const std = @import("std"); 1 const std = @import("std");
2 const proto = @import("protocol"); 2 const proto = @import("term").protocol;
3 const upgrade = @import("upgrade.zig"); 3 const upgrade = @import("upgrade.zig");
4 const TmpDir = @import("testtmp").TmpDir; 4 const TmpDir = @import("testtmp").TmpDir;
5 const h = @import("server_test_harness.zig"); 5 const h = @import("server_test_harness.zig");
src/server/upgrade.zig
Old New
@@ -6,7 +6,7 @@
6 //! neither may be held to this build's struct layout. 6 //! neither may be held to this build's struct layout.
7 7
8 const std = @import("std"); 8 const std = @import("std");
9 const proto = @import("protocol"); 9 const proto = @import("term").protocol;
10 10
11 /// Skew rule: an upgrade candidate must outrank the running daemon. 11 /// Skew rule: an upgrade candidate must outrank the running daemon.
12 /// SemanticVersion because our releases are `0.0.1-13`-shaped: the `-13` 12 /// SemanticVersion because our releases are `0.0.1-13`-shaped: the `-13`
src/tui/interact.zig
Old New
@@ -20,9 +20,9 @@
20 //! replica, and `replica.zig` is the one applier. 20 //! replica, and `replica.zig` is the one applier.
21 21
22 const std = @import("std"); 22 const std = @import("std");
23 const Engine = @import("engine").Engine; 23 const Engine = @import("term").engine.Engine;
24 const Replica = @import("replica").Replica; 24 const Replica = @import("term").replica.Replica;
25 const proto = @import("protocol"); 25 const proto = @import("term").protocol;
26 const predict = @import("predict.zig"); 26 const predict = @import("predict.zig");
27 const client_core = @import("client").core; 27 const client_core = @import("client").core;
28 // Named `paint_mod` because paintOverlay holds a local ArrayList called 28 // Named `paint_mod` because paintOverlay holds a local ArrayList called
src/tui/paint.zig
Old New
@@ -7,8 +7,8 @@
7 //! Also the single home of the synchronized-update bracket, for every 7 //! Also the single home of the synchronized-update bracket, for every
8 //! caller — including paintOverlay, which lives in interact.zig. 8 //! caller — including paintOverlay, which lives in interact.zig.
9 const std = @import("std"); 9 const std = @import("std");
10 const Engine = @import("engine").Engine; 10 const Engine = @import("term").engine.Engine;
11 const proto = @import("protocol"); 11 const proto = @import("term").protocol;
12 12
13 /// The synchronized-update bracket. Exists exactly once because a 13 /// The synchronized-update bracket. Exists exactly once because a
14 /// dropped half is invisible to both e2e suites (the bytes still 14 /// dropped half is invisible to both e2e suites (the bytes still
src/tui/predict.zig
Old New
@@ -20,7 +20,7 @@
20 //! and the queue is read by index — a slice goes stale on the next 20 //! and the queue is read by index — a slice goes stale on the next
21 //! append. 21 //! append.
22 const std = @import("std"); 22 const std = @import("std");
23 const proto = @import("protocol"); 23 const proto = @import("term").protocol;
24 24
25 /// One predicted character at one place on the grid. Printable ASCII only, 25 /// One predicted character at one place on the grid. Printable ASCII only,
26 /// deliberately: `ch` is a byte by value, so there is nothing here that can 26 /// deliberately: `ch` is a byte by value, so there is nothing here that can
src/tui/wall_host.zig
Old New
@@ -4,7 +4,7 @@
4 //! own — `applyHostList` is where a poll's answer becomes births and 4 //! own — `applyHostList` is where a poll's answer becomes births and
5 //! vanishings on the wall the root owns. 5 //! vanishings on the wall the root owns.
6 const std = @import("std"); 6 const std = @import("std");
7 const proto = @import("protocol"); 7 const proto = @import("term").protocol;
8 const client = @import("client"); 8 const client = @import("client");
9 const hosts = @import("client").hosts; 9 const hosts = @import("client").hosts;
10 const handoff = @import("client").handoff; 10 const handoff = @import("client").handoff;
src/tui/wall_layout.zig
Old New
@@ -3,7 +3,7 @@
3 //! rails; the sidecar saves that tree on the last detach and heals it back 3 //! rails; the sidecar saves that tree on the last detach and heals it back
4 //! per leaf against the hosts' live lists. 4 //! per leaf against the hosts' live lists.
5 const std = @import("std"); 5 const std = @import("std");
6 const proto = @import("protocol"); 6 const proto = @import("term").protocol;
7 const hosts = @import("client").hosts; 7 const hosts = @import("client").hosts;
8 const interact = @import("interact.zig"); 8 const interact = @import("interact.zig");
9 const layout = @import("client").layout; 9 const layout = @import("client").layout;
src/tui/wall_picker.zig
Old New
@@ -5,7 +5,7 @@
5 //! spelling editor. Paints under `paint_mu` while `Shared.picker_open` 5 //! spelling editor. Paints under `paint_mu` while `Shared.picker_open`
6 //! holds every tile off the terminal. 6 //! holds every tile off the terminal.
7 const std = @import("std"); 7 const std = @import("std");
8 const proto = @import("protocol"); 8 const proto = @import("term").protocol;
9 const client = @import("client"); 9 const client = @import("client");
10 const hosts = @import("client").hosts; 10 const hosts = @import("client").hosts;
11 const handoff = @import("client").handoff; 11 const handoff = @import("client").handoff;
src/tui/wall_pump.zig
Old New
@@ -4,7 +4,7 @@
4 //! this thread work through the tile's mailbox and doorbell and reads its 4 //! this thread work through the tile's mailbox and doorbell and reads its
5 //! answers back the same way; nothing here touches another tile. 5 //! answers back the same way; nothing here touches another tile.
6 const std = @import("std"); 6 const std = @import("std");
7 const proto = @import("protocol"); 7 const proto = @import("term").protocol;
8 const client = @import("client"); 8 const client = @import("client");
9 const handoff = @import("client").handoff; 9 const handoff = @import("client").handoff;
10 const interact = @import("interact.zig"); 10 const interact = @import("interact.zig");
src/tui/wall_test_harness.zig
Old New
@@ -1,9 +1,9 @@
1 //! Fixtures the wall's test files share: benches that stand a wall up 1 //! Fixtures the wall's test files share: benches that stand a wall up
2 //! without a terminal, and the transports and screens they read back. 2 //! without a terminal, and the transports and screens they read back.
3 const std = @import("std"); 3 const std = @import("std");
4 const proto = @import("protocol"); 4 const proto = @import("term").protocol;
5 const client = @import("client"); 5 const client = @import("client");
6 const Engine = @import("engine").Engine; 6 const Engine = @import("term").engine.Engine;
7 const layout = @import("client").layout; 7 const layout = @import("client").layout;
8 const wall_host = @import("wall_host.zig"); 8 const wall_host = @import("wall_host.zig");
9 const wall_picker = @import("wall_picker.zig"); 9 const wall_picker = @import("wall_picker.zig");
src/tui/wall_test_host.zig
Old New
@@ -1,6 +1,6 @@
1 //! The hosts file, the host table and the poller (wall_host.zig). 1 //! The hosts file, the host table and the poller (wall_host.zig).
2 const std = @import("std"); 2 const std = @import("std");
3 const proto = @import("protocol"); 3 const proto = @import("term").protocol;
4 const client = @import("client"); 4 const client = @import("client");
5 const hosts = @import("client").hosts; 5 const hosts = @import("client").hosts;
6 const TmpDir = @import("testtmp").TmpDir; 6 const TmpDir = @import("testtmp").TmpDir;
src/tui/wall_test_layout.zig
Old New
@@ -1,6 +1,6 @@
1 //! The pane tree, the rects it cuts and the layout sidecar (wall_layout.zig). 1 //! The pane tree, the rects it cuts and the layout sidecar (wall_layout.zig).
2 const std = @import("std"); 2 const std = @import("std");
3 const proto = @import("protocol"); 3 const proto = @import("term").protocol;
4 const hosts = @import("client").hosts; 4 const hosts = @import("client").hosts;
5 const layout = @import("client").layout; 5 const layout = @import("client").layout;
6 const TmpDir = @import("testtmp").TmpDir; 6 const TmpDir = @import("testtmp").TmpDir;
src/tui/wall_test_picker.zig
Old New
@@ -1,6 +1,6 @@
1 //! The host picker popup (wall_picker.zig). 1 //! The host picker popup (wall_picker.zig).
2 const std = @import("std"); 2 const std = @import("std");
3 const proto = @import("protocol"); 3 const proto = @import("term").protocol;
4 const client = @import("client"); 4 const client = @import("client");
5 const interact = @import("interact.zig"); 5 const interact = @import("interact.zig");
6 const layout = @import("client").layout; 6 const layout = @import("client").layout;
src/tui/wall_test_pump.zig
Old New
@@ -1,6 +1,6 @@
1 //! One tile's pump thread (wall_pump.zig). 1 //! One tile's pump thread (wall_pump.zig).
2 const std = @import("std"); 2 const std = @import("std");
3 const proto = @import("protocol"); 3 const proto = @import("term").protocol;
4 const client = @import("client"); 4 const client = @import("client");
5 const interact = @import("interact.zig"); 5 const interact = @import("interact.zig");
6 const TmpDir = @import("testtmp").TmpDir; 6 const TmpDir = @import("testtmp").TmpDir;
src/tui/wall_test_wall.zig
Old New
@@ -1,6 +1,6 @@
1 //! The wall itself: tiles, focus, births, endings and label bars. 1 //! The wall itself: tiles, focus, births, endings and label bars.
2 const std = @import("std"); 2 const std = @import("std");
3 const proto = @import("protocol"); 3 const proto = @import("term").protocol;
4 const client = @import("client"); 4 const client = @import("client");
5 const interact = @import("interact.zig"); 5 const interact = @import("interact.zig");
6 const fixture = @import("wall_test_harness.zig"); 6 const fixture = @import("wall_test_harness.zig");
src/tui/wallview.zig
Old New
@@ -18,14 +18,14 @@
18 //! A beside cut needs VT-safe truncation of a grid row, because the 18 //! A beside cut needs VT-safe truncation of a grid row, because the
19 //! terminal only clips at ITS edge: `engine.dumpVtRowClipped` is it. 19 //! terminal only clips at ITS edge: `engine.dumpVtRowClipped` is it.
20 const std = @import("std"); 20 const std = @import("std");
21 const proto = @import("protocol"); 21 const proto = @import("term").protocol;
22 const client = @import("client"); 22 const client = @import("client");
23 const hosts = @import("client").hosts; 23 const hosts = @import("client").hosts;
24 const handoff = @import("client").handoff; 24 const handoff = @import("client").handoff;
25 const askpass = @import("client").askpass; 25 const askpass = @import("client").askpass;
26 const spawn = @import("spawn"); 26 const spawn = @import("spawn");
27 const proxy = @import("proxy"); 27 const proxy = @import("proxy");
28 const Engine = @import("engine").Engine; 28 const Engine = @import("term").engine.Engine;
29 const paint = @import("paint.zig"); 29 const paint = @import("paint.zig");
30 const select = @import("select.zig"); 30 const select = @import("select.zig");
31 // Counters ride out through `Shared` because a detached pump never reaches 31 // Counters ride out through `Shared` because a detached pump never reaches
test/render.zig
Old New
@@ -3,7 +3,7 @@
3 //! text format, so the suite can diff what the client painted against 3 //! text format, so the suite can diff what the client painted against
4 //! what the daemon holds. Third helper beside rawmode/delaypipe. 4 //! what the daemon holds. Third helper beside rawmode/delaypipe.
5 const std = @import("std"); 5 const std = @import("std");
6 const Engine = @import("engine").Engine; 6 const Engine = @import("term").engine.Engine;
7 7
8 const alt_exit = "\x1b[?1049l"; 8 const alt_exit = "\x1b[?1049l";
9 9
test/wsclient.zig
Old New
@@ -26,9 +26,9 @@
26 //! Usage: wsclient --port N --tile IDX --out FILE --err FILE 26 //! Usage: wsclient --port N --tile IDX --out FILE --err FILE
27 //! [--origin STR] < script 27 //! [--origin STR] < script
28 const std = @import("std"); 28 const std = @import("std");
29 const Engine = @import("engine").Engine; 29 const Engine = @import("term").engine.Engine;
30 const Replica = @import("replica").Replica; 30 const Replica = @import("term").replica.Replica;
31 const proto = @import("protocol"); 31 const proto = @import("term").protocol;
32 // The script dialect this fixture and ptyclient both speak: the escape 32 // The script dialect this fixture and ptyclient both speak: the escape
33 // table and the exit codes (test/script.zig). 33 // table and the exit codes (test/script.zig).
34 const script = @import("script"); 34 const script = @import("script");