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
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"); |