a73x

4fa1d021

refactor: the client link owns its core, hosts file, handoff, layout, keymap and askpass as child files behind pub seams

a73x   2026-08-30 12:18

Commit message
refactor: the client link owns its core, hosts file, handoff, layout, keymap and askpass as child files behind pub seams

build.zig
Old New
@@ -43,8 +43,8 @@ fn quicDeps(b: *std.Build, target: std.Build.ResolvedTarget) struct {
43 /// One wasm-side twin of a native module: same source, the wasm32 target, 43 /// One wasm-side twin of a native module: same source, the wasm32 target,
44 /// and ReleaseSmall — never `optimize`, because the artifact is embedded 44 /// and ReleaseSmall — never `optimize`, because the artifact is embedded
45 /// into muxweb and its Debug build is 3.7MB against ReleaseSmall's 345KB. 45 /// into muxweb and its Debug build is 3.7MB against ReleaseSmall's 345KB.
46 /// The five of them differ only in path, and the optimize mode is the one 46 /// They differ only in path, and the optimize mode is the one field that
47 /// field that must not drift between them. 47 /// must not drift between them.
48 fn wasmMod(b: *std.Build, wasm_target: std.Build.ResolvedTarget, path: []const u8) *std.Build.Module { 48 fn wasmMod(b: *std.Build, wasm_target: std.Build.ResolvedTarget, path: []const u8) *std.Build.Module {
49 return b.createModule(.{ 49 return b.createModule(.{
50 .root_source_file = b.path(path), 50 .root_source_file = b.path(path),
@@ -125,10 +125,6 @@ const mod_table = [_]ModSpec{
125 // Imported by every module that has such a test, which is why it is a 125 // Imported by every module that has such a test, which is why it is a
126 // module rather than three copies. 126 // module rather than three copies.
127 .{ .name = "testtmp", .path = "src/testtmp.zig", .layer = 0 }, 127 .{ .name = "testtmp", .path = "src/testtmp.zig", .layer = 0 },
128 // Normalized key events -> VT bytes. A leaf with no platform imports —
129 // each shell (browser, later xkb) produces the normalized form and this
130 // owns the bytes; must compile for wasm32.
131 .{ .name = "keymap", .path = "src/client/keymap.zig", .layer = 0, .wasm = true },
132 // What the two scripted fixtures share: the escape table and the exit 128 // What the two scripted fixtures share: the escape table and the exit
133 // codes. One copy, so ptyclient and wsclient cannot disagree about 129 // codes. One copy, so ptyclient and wsclient cannot disagree about
134 // what a scenario's heredoc sent. 130 // what a scenario's heredoc sent.
@@ -155,44 +151,19 @@ const mod_table = [_]ModSpec{
155 // struct is the flag table and the parser learns it at comptime. 151 // struct is the flag table and the parser learns it at comptime.
156 .{ .name = "cliflags", .path = "src/cli/flags.zig", .layer = 0 }, 152 .{ .name = "cliflags", .path = "src/cli/flags.zig", .layer = 0 },
157 // ---- layer 1: single-hop over the leaves ---- 153 // ---- layer 1: single-hop over the leaves ----
158 // Platform-neutral semantic decoding for terminal mode samples and
159 // side-channel events. The borrowed clipboard slice stays tied to the
160 // frame payload, so native and wasm consumers share one contract.
161 .{ .name = "client_core", .path = "src/client/client_core.zig", .layer = 1, .wasm = true, .imports = &.{"protocol"} },
162 // This image, as a path something can exec. Under src/cli/ because it 154 // This image, as a path something can exec. Under src/cli/ because it
163 // asks the OS about the process it is in — a question no headless 155 // asks the OS about the process it is in — a question no headless
164 // client may spell. 156 // client may spell.
165 .{ .name = "spawn", .path = "src/cli/spawn.zig", .layer = 1, .link_libc = true }, 157 .{ .name = "spawn", .path = "src/cli/spawn.zig", .layer = 1, .link_libc = true },
166 // The ssh→QUIC handoff's shared vocabulary: the announce line, the
167 // per-host cache, the dial-host strip. Both binaries import it —
168 // `muxd endpoint` writes the line, `mux HOST` reads it. xdg is for
169 // makePrivateParent only — the 0700-parent discipline the cache file
170 // shares with the key file; xdg is a leaf, so no cycle.
171 .{ .name = "handoff", .path = "src/client/handoff.zig", .layer = 1, .link_libc = true, .imports = &.{"xdg"}, .test_imports = &.{"testtmp"} },
172 // Both ends of ssh's prompt carriage: the client's listening socket and
173 // the `mux askpass` helper ssh execs. No terminal and no spawn — the
174 // wall paints the popup and ssh does the exec — so the whole protocol
175 // is drivable by a test with neither in the picture.
176 .{ .name = "askpass", .path = "src/client/askpass.zig", .layer = 1, .test_imports = &.{"testtmp"} },
177 // Row-level change tracking behind the delta stream. Engine plus 158 // Row-level change tracking behind the delta stream. Engine plus
178 // protocol and nothing else — no daemon, no clients — so the tracker's 159 // protocol and nothing else — no daemon, no clients — so the tracker's
179 // own tests drive it with an engine and no socket in sight. 160 // own tests drive it with an engine and no socket in sight.
180 .{ .name = "delta", .path = "src/engine/delta.zig", .layer = 1, .imports = &.{ "engine", "protocol" } }, 161 .{ .name = "delta", .path = "src/engine/delta.zig", .layer = 1, .imports = &.{ "engine", "protocol" } },
181 // The wall: the host spelling grammar, the daemon list, and the state
182 // file behind them, plus the one atomic writer every state file uses.
183 // One owner for a grammar the hub, the file and the CLI all have to
184 // agree on — xdg for where that file lives, so its tests need no hub,
185 // no daemon and no socket.
186 .{ .name = "hosts", .path = "src/client/hosts.zig", .layer = 1, .imports = &.{"xdg"}, .test_imports = &.{"testtmp"} },
187 // The replay core: snapshot/delta application and the resume 162 // The replay core: snapshot/delta application and the resume
188 // coordinates, shared by the CLI client, the wasm core, and the 163 // coordinates, shared by the CLI client, the wasm core, and the
189 // server's test fixtures. Engine plus protocol and nothing else, and 164 // server's test fixtures. Engine plus protocol and nothing else, and
190 // deliberately platform-free — it must compile for wasm32. 165 // deliberately platform-free — it must compile for wasm32.
191 .{ .name = "replica", .path = "src/engine/replica.zig", .layer = 1, .wasm = true, .imports = &.{ "engine", "protocol" } }, 166 .{ .name = "replica", .path = "src/engine/replica.zig", .layer = 1, .wasm = true, .imports = &.{ "engine", "protocol" } },
192 // The container tree that owns the wall's pane geometry. Pure: no tty,
193 // no engine, no imports — floors arrive as a parameter so wallview can
194 // pass the protocol minimums without this module knowing they are that.
195 .{ .name = "layout", .path = "src/client/layout.zig", .layer = 1 },
196 // 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
197 // `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
198 // convergence check. Imports the engine module so both sides of the 169 // convergence check. Imports the engine module so both sides of the
@@ -229,20 +200,27 @@ const mod_table = [_]ModSpec{
229 // state anybody else can see — but the predicting itself is interact's 200 // state anybody else can see — but the predicting itself is interact's
230 // now, along with the rest of the terminal-facing machinery; what stays 201 // now, along with the rest of the terminal-facing machinery; what stays
231 // here is Target/Transport, the attach loop and the session's meanings. 202 // here is Target/Transport, the attach loop and the session's meanings.
232 // `hosts` is the host grammar: `Target.fromSpec` takes a `hosts.Spec`, 203 // The core decoder, the hosts file, the handoff vocabulary, the pane
233 // and `resolveHost` sits here rather than in either front so the CLI 204 // tree, the key table and the askpass carriage are CHILD FILES of this
234 // wall and the browser hub resolve a host line the same way. Nothing 205 // root re-exported as `client.core`, `.hosts`, `.handoff`, `.layout`,
235 // here WRITES that file — `wall_host.recordHost` and `hub_main` do. 206 // `.keymap`, `.askpass` — one row, so a second module claiming any of
236 .{ .name = "client", .path = "src/client/client.zig", .layer = 3, .link_libc = true, .imports = &.{ "protocol", "replica", "keymap", "quic", "handoff", "hosts", "xdg", "sockpath", "askpass" }, .test_imports = &.{"testtmp"}, .quic_tests = true }, 207 // those files is a file-in-multiple-modules compile error. `resolveHost`
208 // 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 —
210 // `wall_host.recordHost` and `hub_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 },
212 // ---- layer 4 ----
237 // The daemon entrypoint loads the key and constructs the listener, so it 213 // The daemon entrypoint loads the key and constructs the listener, so it
238 // needs `quic` directly; the listener type and the upgrade vocabulary it 214 // needs `quic` directly; the listener type and the upgrade vocabulary it
239 // reaches through the daemon root's re-exports, being that root's own 215 // reaches through the daemon root's re-exports, being that root's own
240 // child files now. `muxd endpoint` prints the announce line handoff 216 // child files now. `mux d endpoint` prints the announce line, whose
241 // spells; sockpath is the sun_path bound, checked before any verb acts 217 // vocabulary is `client.handoff` — the daemon writes the line and the
242 // on the path; and the keygen round-trip test needs a directory to 218 // client reads it, so one owner or the two spell it differently; naming
243 // generate into, which the daemon itself never touches. 219 // `client` for it is what puts this entrypoint above the link rather
244 .{ .name = "daemon_main", .path = "src/cli/main.zig", .layer = 3, .link_libc = true, .imports = &.{ "daemon", "protocol", "proxy", "quic", "xdg", "spawn", "handoff", "sockpath", "cliflags" }, .test_imports = &.{"testtmp"}, .quic_tests = true }, 220 // than beside it. sockpath is the sun_path bound, checked before any
245 // ---- layer 4 ---- 221 // verb acts on the path; and the keygen round-trip test needs a
222 // directory to generate into, which the daemon itself never touches.
223 .{ .name = "daemon_main", .path = "src/cli/main.zig", .layer = 4, .link_libc = true, .imports = &.{ "daemon", "protocol", "proxy", "quic", "xdg", "spawn", "client", "sockpath", "cliflags" }, .test_imports = &.{"testtmp"}, .quic_tests = true },
246 // The muxweb hub's HTTP/WebSocket decisions: Origin gate, route 224 // The muxweb hub's HTTP/WebSocket decisions: Origin gate, route
247 // table, WS endpoint naming. Assets are injected (the exe root 225 // table, WS endpoint naming. Assets are injected (the exe root
248 // @embedFiles them), so its tests build no artifacts. 226 // @embedFiles them), so its tests build no artifacts.
@@ -250,32 +228,33 @@ const mod_table = [_]ModSpec{
250 // The CLI wall (`mux wall`): multiattach stripes in one terminal, one of 228 // The CLI wall (`mux wall`): multiattach stripes in one terminal, one of
251 // which can be ZOOMED — promoted to the terminal's size and typed 229 // which can be ZOOMED — promoted to the terminal's size and typed
252 // through. Same layer as webhub for the same reason — both sit on 230 // through. Same layer as webhub for the same reason — both sit on
253 // client's Transport and hosts' grammar; neither may import the other; 231 // client's Transport and `client.hosts`' grammar; neither may import the
254 // both resolve a spelling through `client.Target.fromSpec`. 232 // other; both resolve a spelling through `client.Target.fromSpec`.
255 // The interaction loop, the painter, the selector and the prediction 233 // The interaction loop, the painter, the selector and the prediction
256 // overlay are CHILD FILES of this root rather than modules of their own, 234 // overlay are CHILD FILES of this root rather than modules of their own,
257 // so nothing outside src/tui/ can name one: a second module claiming any 235 // so nothing outside src/tui/ can name one: a second module claiming any
258 // of those files is a file-in-multiple-modules compile error. Their 236 // of those files is a file-in-multiple-modules compile error. `replica`
259 // needs — replica, client_core, keymap — are why this row imports what a 237 // is what a painter and a keyboard loop want and the wall itself never
260 // painter and a keyboard loop want and the wall itself never spells. 238 // spells; the decoder and the key table they also want reach them
261 .{ .name = "wall", .path = "src/tui/wallview.zig", .layer = 4, .link_libc = true, .imports = &.{ "protocol", "client", "hosts", "handoff", "proxy", "engine", "replica", "client_core", "keymap", "layout", "askpass", "spawn" }, .test_imports = &.{"testtmp"}, .quic_tests = true }, 239 // through `client`'s seams.
240 .{ .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 },
262 // ---- layer 5 ---- 241 // ---- layer 5 ----
263 // hosts owns the host grammar and the state file, so argv is parsed by 242 // hosts owns the host grammar and the state file, so argv is parsed by
264 // the SAME rules the CLI's own `mux hosts add` is — one grammar, not 243 // the SAME rules the CLI's own `mux hosts add` is — one grammar, not
265 // two. Resolution is `client.resolveHost`, shared with the CLI wall; 244 // two. Resolution is `client.resolveHost`, shared with the CLI wall;
266 // sockpath stays for the one startup message that names the sun_path 245 // sockpath stays for the one startup message that names the sun_path
267 // bound. 246 // bound.
268 .{ .name = "hub_main", .path = "src/cli/webhub_main.zig", .layer = 5, .link_libc = true, .imports = &.{ "client", "webhub", "hosts", "xdg", "sockpath", "cliflags" }, .quic_tests = true }, 247 .{ .name = "hub_main", .path = "src/cli/webhub_main.zig", .layer = 5, .link_libc = true, .imports = &.{ "client", "webhub", "xdg", "sockpath", "cliflags" }, .quic_tests = true },
269 // sockpath is the sun_path bound only; the client binds no socket itself. 248 // sockpath is the sun_path bound only; the client binds no socket itself.
270 // protocol is the session-name validator alone (validSessionName): a bad 249 // protocol is the session-name validator alone (validSessionName): a bad
271 // --session has to be a usage error here, at parse, not bytes some 250 // --session has to be a usage error here, at parse, not bytes some
272 // daemon downstream has to notice and refuse. Layer 5 since the no-arg 251 // daemon downstream has to notice and refuse. Layer 5 since the no-arg
273 // wall pulled in wallview (layer 4). 252 // wall pulled in wallview (layer 4).
274 .{ .name = "client_main", .path = "src/cli/mux_main.zig", .layer = 5, .link_libc = true, .imports = &.{ "client", "protocol", "xdg", "spawn", "handoff", "sockpath", "wall", "hosts", "cliflags" }, .test_imports = &.{"testtmp"}, .quic_tests = true }, 253 .{ .name = "client_main", .path = "src/cli/mux_main.zig", .layer = 5, .link_libc = true, .imports = &.{ "client", "protocol", "xdg", "spawn", "sockpath", "wall", "cliflags" }, .test_imports = &.{"testtmp"}, .quic_tests = true },
275 // ---- layer 6: the one binary ---- 254 // ---- layer 6: the one binary ----
276 // Four words, one image. Nothing but the mode letter lives here, which 255 // Four words, one image. Nothing but the mode letter lives here, which
277 // is why it may sit above every other main without owning any of them. 256 // is why it may sit above every other main without owning any of them.
278 .{ .name = "mux", .path = "src/cli/mux.zig", .layer = 6, .link_libc = true, .imports = &.{ "daemon_main", "agent_main", "hub_main", "client_main", "askpass" } }, 257 .{ .name = "mux", .path = "src/cli/mux.zig", .layer = 6, .link_libc = true, .imports = &.{ "daemon_main", "agent_main", "hub_main", "client_main", "client" } },
279 }; 258 };
280 259
281 /// Comptime row lookup. Every hand-written module name in this file goes 260 /// Comptime row lookup. Every hand-written module name in this file goes
@@ -817,13 +796,12 @@ fn docGate(b: *std.Build, target: std.Build.ResolvedTarget, check_step: *std.Bui
817 /// nothing but the one binary, and they carry the argument parsers — a 796 /// nothing but the one binary, and they carry the argument parsers — a
818 /// test that is never built is not a test (decisions.md). 797 /// test that is never built is not a test (decisions.md).
819 const test_order = [_][]const u8{ 798 const test_order = [_][]const u8{
820 "script", "protocol", "client_core", "engine", "pty", 799 "script", "protocol", "engine", "pty", "delta",
821 "delta", "hosts", "askpass", "replica", "keymap", 800 "replica", "webhub", "wall", "sockpath", "agent_main",
822 "webhub", "wall", "sockpath", "agent_main", "daemon", 801 "daemon", "client", "proxy", "client_main", "quic",
823 "client", "proxy", "client_main", "quic", "daemon_main", 802 "daemon_main", "testtmp", "rawmode", "delaypipe", "xdg",
824 "testtmp", "rawmode", "delaypipe", "xdg", "spawn", 803 "spawn", "render", "ptyclient", "hub_main", "wsclient",
825 "handoff", "layout", "render", "ptyclient", "hub_main", 804 "cliflags", "mux",
826 "wsclient", "cliflags", "mux",
827 }; 805 };
828 806
829 comptime { 807 comptime {
@@ -983,8 +961,8 @@ pub fn build(b: *std.Build) void {
983 b.installArtifact(ptyclient_exe); 961 b.installArtifact(ptyclient_exe);
984 962
985 // ---- The wasm core (M-web Task 4) ---- 963 // ---- The wasm core (M-web Task 4) ----
986 // A SECOND resolved target: modules are target-bound, so the four 964 // A SECOND resolved target: modules are target-bound, so the three
987 // wasm-clean modules (engine, protocol, replica, keymap) and the 965 // wasm-clean modules (engine, protocol, replica) and the
988 // ghostty dependency are instantiated again against wasm32. Always 966 // ghostty dependency are instantiated again against wasm32. Always
989 // ReleaseSmall — the artifact is @embedFile'd into muxweb, and its 967 // ReleaseSmall — the artifact is @embedFile'd into muxweb, and its
990 // Debug build is 3.7MB against ReleaseSmall's 345KB (spike-measured). 968 // Debug build is 3.7MB against ReleaseSmall's 345KB (spike-measured).
@@ -1014,22 +992,21 @@ pub fn build(b: *std.Build) void {
1014 } 992 }
1015 } 993 }
1016 const engine_wasm_mod = wasm_mods[comptime idxOf("engine")].?; 994 const engine_wasm_mod = wasm_mods[comptime idxOf("engine")].?;
1017 const client_core_wasm_mod = wasm_mods[comptime idxOf("client_core")].?;
1018 if (ghostty_wasm_dep) |dep| { 995 if (ghostty_wasm_dep) |dep| {
1019 engine_wasm_mod.addImport("ghostty-vt", dep.module("ghostty-vt")); 996 engine_wasm_mod.addImport("ghostty-vt", dep.module("ghostty-vt"));
1020 } 997 }
1021 const wasm_core_mod = wasmMod(b, wasm_target, "src/client/wasm_core.zig"); 998 const wasm_core_mod = wasmMod(b, wasm_target, "src/client/wasm_core.zig");
1022 wasm_core_mod.addImport("engine", engine_wasm_mod); 999 wasm_core_mod.addImport("engine", engine_wasm_mod);
1023 wasm_core_mod.addImport("protocol", wasm_mods[comptime idxOf("protocol")].?); 1000 wasm_core_mod.addImport("protocol", wasm_mods[comptime idxOf("protocol")].?);
1024 wasm_core_mod.addImport("client_core", wasm_mods[comptime idxOf("client_core")].?);
1025 wasm_core_mod.addImport("replica", wasm_mods[comptime idxOf("replica")].?); 1001 wasm_core_mod.addImport("replica", wasm_mods[comptime idxOf("replica")].?);
1026 wasm_core_mod.addImport("keymap", wasm_mods[comptime idxOf("keymap")].?);
1027 // Compile a tiny, never-embedded canary that calls the semantic decoder 1002 // Compile a tiny, never-embedded canary that calls the semantic decoder
1028 // through typed mode and clipboard result paths. This build-only object 1003 // through typed mode and clipboard result paths. This build-only object
1029 // forces the production API and its validation helpers through wasm 1004 // forces the production API and its validation helpers through wasm
1030 // semantic analysis without adding runtime exports or behavior. 1005 // semantic analysis without adding runtime exports or behavior. It
1006 // names `protocol` because client_core.zig, its relative child now,
1007 // imports that module by name.
1031 const client_core_wasm_check_mod = wasmMod(b, wasm_target, "src/client/client_core_wasm_check.zig"); 1008 const client_core_wasm_check_mod = wasmMod(b, wasm_target, "src/client/client_core_wasm_check.zig");
1032 client_core_wasm_check_mod.addImport("client_core", client_core_wasm_mod); 1009 client_core_wasm_check_mod.addImport("protocol", wasm_mods[comptime idxOf("protocol")].?);
1033 const client_core_wasm_guard = b.addObject(.{ 1010 const client_core_wasm_guard = b.addObject(.{
1034 .name = "client_core_wasm_check", 1011 .name = "client_core_wasm_check",
1035 .root_module = client_core_wasm_check_mod, 1012 .root_module = client_core_wasm_check_mod,
src/cli/main.zig
Old New
@@ -13,7 +13,7 @@ const quic_server = @import("daemon").quic_server;
13 const build_options = @import("build_options"); 13 const build_options = @import("build_options");
14 const xdg = @import("xdg"); 14 const xdg = @import("xdg");
15 const spawn = @import("spawn"); 15 const spawn = @import("spawn");
16 const handoff = @import("handoff"); 16 const handoff = @import("client").handoff;
17 const sockpath = @import("sockpath"); 17 const sockpath = @import("sockpath");
18 const upgrade = @import("daemon").upgrade; 18 const upgrade = @import("daemon").upgrade;
19 const cliflags = @import("cliflags"); 19 const cliflags = @import("cliflags");
src/cli/mux.zig
Old New
@@ -14,7 +14,7 @@ const daemon = @import("daemon_main");
14 const agent = @import("agent_main"); 14 const agent = @import("agent_main");
15 const hub = @import("hub_main"); 15 const hub = @import("hub_main");
16 const client = @import("client_main"); 16 const client = @import("client_main");
17 const askpass = @import("askpass"); 17 const askpass = @import("client").askpass;
18 18
19 /// The whole grammar, as a value, so the one decision this file makes can be 19 /// The whole grammar, as a value, so the one decision this file makes can be
20 /// asked without a process to exit from. 20 /// asked without a process to exit from.
src/cli/mux_main.zig
Old New
@@ -19,10 +19,10 @@ const proto = @import("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");
22 const handoff = @import("handoff"); 22 const handoff = @import("client").handoff;
23 const sockpath = @import("sockpath"); 23 const sockpath = @import("sockpath");
24 const wallview = @import("wall"); 24 const wallview = @import("wall");
25 const hosts = @import("hosts"); 25 const hosts = @import("client").hosts;
26 const cliflags = @import("cliflags"); 26 const cliflags = @import("cliflags");
27 const TmpDir = @import("testtmp").TmpDir; 27 const TmpDir = @import("testtmp").TmpDir;
28 28
src/cli/webhub_main.zig
Old New
@@ -13,7 +13,7 @@
13 const std = @import("std"); 13 const std = @import("std");
14 const client = @import("client"); 14 const client = @import("client");
15 const webhub = @import("webhub"); 15 const webhub = @import("webhub");
16 const hosts = @import("hosts"); 16 const hosts = @import("client").hosts;
17 const build_options = @import("build_options"); 17 const build_options = @import("build_options");
18 const xdg = @import("xdg"); 18 const xdg = @import("xdg");
19 const sockpath = @import("sockpath"); 19 const sockpath = @import("sockpath");
src/client/client.zig
Old New
@@ -14,19 +14,25 @@
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("replica").Replica;
17 // For `keymap.detach_key` only — the abort byte both opening waits watch for.
18 // The dial has no session to command and no terminal to drive, which is why
19 // the byte comes from keymap rather than from the filter that consumes it.
20 const keymap = @import("keymap");
21 const proto = @import("protocol"); 17 const proto = @import("protocol");
22 const TmpDir = @import("testtmp").TmpDir; 18 const TmpDir = @import("testtmp").TmpDir;
23 const quic = @import("quic"); 19 const quic = @import("quic");
24 const handoff = @import("handoff");
25 const askpass = @import("askpass");
26 const hosts = @import("hosts");
27 const xdg = @import("xdg"); 20 const xdg = @import("xdg");
28 const sockpath = @import("sockpath"); 21 const sockpath = @import("sockpath");
29 22
23 // The client link's public seams: the wall, the hub and the mains reach
24 // these as client.X — the table stays one row, the files stay children.
25 pub const hosts = @import("hosts.zig");
26 pub const handoff = @import("handoff.zig");
27 pub const layout = @import("layout.zig");
28 // This file uses `keymap.detach_key` only — the abort byte both opening waits
29 // watch for. The dial has no session to command and no terminal to drive,
30 // which is why the byte comes from keymap rather than from the filter that
31 // consumes it.
32 pub const keymap = @import("keymap.zig");
33 pub const askpass = @import("askpass.zig");
34 pub const core = @import("client_core.zig");
35
30 /// A session name held by value. The names a switch travels on are decoded 36 /// A session name held by value. The names a switch travels on are decoded
31 /// out of a frame payload that is freed before the re-dial, so they cannot 37 /// out of a frame payload that is freed before the re-dial, so they cannot
32 /// be carried as slices. 38 /// be carried as slices.
@@ -3032,6 +3038,12 @@ test "client: a wall spelling that does not fit fails rather than truncates" {
3032 // decisions.md). Pub decls only: std.meta.declarations sees nothing private. 3038 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
3033 test { 3039 test {
3034 std.testing.refAllDeclsRecursive(@This()); 3040 std.testing.refAllDeclsRecursive(@This());
3041 _ = @import("client_core.zig");
3042 _ = @import("hosts.zig");
3043 _ = @import("handoff.zig");
3044 _ = @import("layout.zig");
3045 _ = @import("keymap.zig");
3046 _ = @import("askpass.zig");
3035 } 3047 }
3036 3048
3037 /// A daemon stand-in for the birth tests: accepts once, records every frame 3049 /// A daemon stand-in for the birth tests: accepts once, records every frame
src/client/client_core_wasm_check.zig
Old New
@@ -1,4 +1,4 @@
1 const client_core = @import("client_core"); 1 const client_core = @import("client_core.zig");
2 2
3 /// Compile-only canary: keep a real typed mode receive call in the wasm 3 /// Compile-only canary: keep a real typed mode receive call in the wasm
4 /// build, without exporting or embedding this check in the hub. 4 /// build, without exporting or embedding this check in the hub.
src/client/wasm_core.zig
Old New
@@ -20,9 +20,9 @@ const std = @import("std");
20 const builtin = @import("builtin"); 20 const builtin = @import("builtin");
21 const Engine = @import("engine").Engine; 21 const Engine = @import("engine").Engine;
22 const Replica = @import("replica").Replica; 22 const Replica = @import("replica").Replica;
23 const keymap = @import("keymap"); 23 const keymap = @import("keymap.zig");
24 const proto = @import("protocol"); 24 const proto = @import("protocol");
25 const client_core = @import("client_core"); 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
28 /// needs no libc, no syscalls, no host imports. 28 /// needs no libc, no syscalls, no host imports.
src/tui/interact.zig
Old New
@@ -24,15 +24,15 @@ const Engine = @import("engine").Engine;
24 const Replica = @import("replica").Replica; 24 const Replica = @import("replica").Replica;
25 const proto = @import("protocol"); 25 const proto = @import("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
29 // `paint`, which a container-level `paint` would collide with. 29 // `paint`, which a container-level `paint` would collide with.
30 const paint_mod = @import("paint.zig"); 30 const paint_mod = @import("paint.zig");
31 const askpass = @import("askpass"); 31 const askpass = @import("client").askpass;
32 const select = @import("select.zig"); 32 const select = @import("select.zig");
33 // The command prefix byte. Owned by keymap because the dialler in client.zig 33 // The command prefix byte. Owned by keymap because the dialler in client.zig
34 // watches for it with no session and no terminal in the picture. 34 // watches for it with no session and no terminal in the picture.
35 const detach_key = @import("keymap").detach_key; 35 const detach_key = @import("client").keymap.detach_key;
36 36
37 /// What `select` answers, in the shape `paint` asks for. 37 /// What `select` answers, in the shape `paint` asks for.
38 /// 38 ///
src/tui/wall_host.zig
Old New
@@ -6,8 +6,8 @@
6 const std = @import("std"); 6 const std = @import("std");
7 const proto = @import("protocol"); 7 const proto = @import("protocol");
8 const client = @import("client"); 8 const client = @import("client");
9 const hosts = @import("hosts"); 9 const hosts = @import("client").hosts;
10 const handoff = @import("handoff"); 10 const handoff = @import("client").handoff;
11 const wall_layout = @import("wall_layout.zig"); 11 const wall_layout = @import("wall_layout.zig");
12 const wv = @import("wallview.zig"); 12 const wv = @import("wallview.zig");
13 const Shared = wv.Shared; 13 const Shared = wv.Shared;
src/tui/wall_layout.zig
Old New
@@ -4,9 +4,9 @@
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("protocol");
7 const hosts = @import("hosts"); 7 const hosts = @import("client").hosts;
8 const interact = @import("interact.zig"); 8 const interact = @import("interact.zig");
9 const layout = @import("layout"); 9 const layout = @import("client").layout;
10 const wall_host = @import("wall_host.zig"); 10 const wall_host = @import("wall_host.zig");
11 const wv = @import("wallview.zig"); 11 const wv = @import("wallview.zig");
12 const Resolved = wall_host.Resolved; 12 const Resolved = wall_host.Resolved;
src/tui/wall_picker.zig
Old New
@@ -7,10 +7,10 @@
7 const std = @import("std"); 7 const std = @import("std");
8 const proto = @import("protocol"); 8 const proto = @import("protocol");
9 const client = @import("client"); 9 const client = @import("client");
10 const hosts = @import("hosts"); 10 const hosts = @import("client").hosts;
11 const handoff = @import("handoff"); 11 const handoff = @import("client").handoff;
12 const interact = @import("interact.zig"); 12 const interact = @import("interact.zig");
13 const askpass = @import("askpass"); 13 const askpass = @import("client").askpass;
14 const wall_host = @import("wall_host.zig"); 14 const wall_host = @import("wall_host.zig");
15 const wall_layout = @import("wall_layout.zig"); 15 const wall_layout = @import("wall_layout.zig");
16 const wv = @import("wallview.zig"); 16 const wv = @import("wallview.zig");
src/tui/wall_pump.zig
Old New
@@ -6,7 +6,7 @@
6 const std = @import("std"); 6 const std = @import("std");
7 const proto = @import("protocol"); 7 const proto = @import("protocol");
8 const client = @import("client"); 8 const client = @import("client");
9 const handoff = @import("handoff"); 9 const handoff = @import("client").handoff;
10 const interact = @import("interact.zig"); 10 const interact = @import("interact.zig");
11 const wv = @import("wallview.zig"); 11 const wv = @import("wallview.zig");
12 const EndReason = wv.EndReason; 12 const EndReason = wv.EndReason;
src/tui/wall_test_harness.zig
Old New
@@ -4,7 +4,7 @@ const std = @import("std");
4 const proto = @import("protocol"); 4 const proto = @import("protocol");
5 const client = @import("client"); 5 const client = @import("client");
6 const Engine = @import("engine").Engine; 6 const Engine = @import("engine").Engine;
7 const layout = @import("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");
10 const wall_pump = @import("wall_pump.zig"); 10 const wall_pump = @import("wall_pump.zig");
src/tui/wall_test_host.zig
Old New
@@ -2,7 +2,7 @@
2 const std = @import("std"); 2 const std = @import("std");
3 const proto = @import("protocol"); 3 const proto = @import("protocol");
4 const client = @import("client"); 4 const client = @import("client");
5 const hosts = @import("hosts"); 5 const hosts = @import("client").hosts;
6 const TmpDir = @import("testtmp").TmpDir; 6 const TmpDir = @import("testtmp").TmpDir;
7 const fixture = @import("wall_test_harness.zig"); 7 const fixture = @import("wall_test_harness.zig");
8 const wall_host = @import("wall_host.zig"); 8 const wall_host = @import("wall_host.zig");
src/tui/wall_test_layout.zig
Old New
@@ -1,8 +1,8 @@
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("protocol");
4 const hosts = @import("hosts"); 4 const hosts = @import("client").hosts;
5 const layout = @import("layout"); 5 const layout = @import("client").layout;
6 const TmpDir = @import("testtmp").TmpDir; 6 const TmpDir = @import("testtmp").TmpDir;
7 const fixture = @import("wall_test_harness.zig"); 7 const fixture = @import("wall_test_harness.zig");
8 const wall_host = @import("wall_host.zig"); 8 const wall_host = @import("wall_host.zig");
src/tui/wall_test_picker.zig
Old New
@@ -3,7 +3,7 @@ const std = @import("std");
3 const proto = @import("protocol"); 3 const proto = @import("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("layout"); 6 const layout = @import("client").layout;
7 const fixture = @import("wall_test_harness.zig"); 7 const fixture = @import("wall_test_harness.zig");
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_pump.zig
Old New
@@ -4,7 +4,7 @@ const proto = @import("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;
7 const askpass = @import("askpass"); 7 const askpass = @import("client").askpass;
8 const fixture = @import("wall_test_harness.zig"); 8 const fixture = @import("wall_test_harness.zig");
9 const wall_pump = @import("wall_pump.zig"); 9 const wall_pump = @import("wall_pump.zig");
10 const wv = @import("wallview.zig"); 10 const wv = @import("wallview.zig");
src/tui/wallview.zig
Old New
@@ -20,9 +20,9 @@
20 const std = @import("std"); 20 const std = @import("std");
21 const proto = @import("protocol"); 21 const proto = @import("protocol");
22 const client = @import("client"); 22 const client = @import("client");
23 const hosts = @import("hosts"); 23 const hosts = @import("client").hosts;
24 const handoff = @import("handoff"); 24 const handoff = @import("client").handoff;
25 const askpass = @import("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("engine").Engine;
@@ -33,7 +33,7 @@ const select = @import("select.zig");
33 // The chord table and the prediction hooks a focused tile shares with the 33 // The chord table and the prediction hooks a focused tile shares with the
34 // client: one interaction core, not a second copy (interact.zig). 34 // client: one interaction core, not a second copy (interact.zig).
35 const interact = @import("interact.zig"); 35 const interact = @import("interact.zig");
36 const layout = @import("layout"); 36 const layout = @import("client").layout;
37 const TmpDir = @import("testtmp").TmpDir; 37 const TmpDir = @import("testtmp").TmpDir;
38 const wall_host = @import("wall_host.zig"); 38 const wall_host = @import("wall_host.zig");
39 const wall_layout = @import("wall_layout.zig"); 39 const wall_layout = @import("wall_layout.zig");