968eb544
fix: the refusal and the usage page say what a host line is under the new model
a73x 2026-09-03 05:20
Commit message
src/cli/mux_main.zig
| Old | New | ||
|---|---|---|---|
| @@ -44,28 +44,30 @@ const usage = | |||
| 44 | \\ whoever typed last is whose agent signs, and only while attached | 44 | \\ whoever typed last is whose agent signs, and only while attached |
| 45 | \\ --version prints the version, --help prints this | 45 | \\ --version prints the version, --help prints this |
| 46 | \\ | 46 | \\ |
| 47 | \\ mux the wall: every session every listed daemon has live | 47 | \\ mux the wall: the panes your layout file names |
| 48 | \\ mux -A the same wall, entered on the local session with the | 48 | \\ mux -A the same wall, entered on the local session with the |
| 49 | \\ agent armed — bare `mux` carries no agent | 49 | \\ agent armed — bare `mux` carries no agent |
| 50 | \\ mux hosts list the daemons on the wall, with their live session counts | 50 | \\ mux hosts list the daemons you can browse, with their live session counts |
| 51 | \\ mux hosts add SPELLING put a daemon on the wall without opening it | 51 | \\ mux hosts add SPELLING record a daemon without opening it |
| 52 | \\ mux hosts rm SPELLING take one off (its sessions keep running) | 52 | \\ mux hosts rm SPELLING take one off (its sessions keep running) |
| 53 | \\ | 53 | \\ |
| 54 | \\ SPELLING names a DAEMON — HOST, quic://HOST[:PORT], or --sock PATH | 54 | \\ SPELLING names a DAEMON — HOST, quic://HOST[:PORT], or --sock PATH |
| 55 | \\ (one argument, or two as in `mux web`) — and never a session: `#NAME` | 55 | \\ (one argument, or two as in `mux web`) — and never a session: `#NAME` |
| 56 | \\ is refused, because the wall shows every session a daemon has. | 56 | \\ is refused, because a host line names a machine and the layout file |
| 57 | \\ is what names sessions. | ||
| 57 | \\ | 58 | \\ |
| 58 | \\ On the wall, `Ctrl-\ 1-9` focuses a tile and types into it, | 59 | \\ On the wall, `Ctrl-\ 1-9` focuses a pane and types into it, |
| 59 | \\ `Ctrl-\ n/p` walk the tiles, `Ctrl-\ h/j/k/l` moves between panes, | 60 | \\ `Ctrl-\ n/p` walk the panes, `Ctrl-\ h/j/k/l` moves between them, |
| 60 | \\ `Ctrl-\ c` and `Ctrl-\ |/-` create a session on the focused tile's | 61 | \\ `Ctrl-\ c` and `Ctrl-\ |/-` create a session on the focused pane's |
| 61 | \\ daemon, `Ctrl-\ x` ends the focused session (twice when others are | 62 | \\ daemon, `Ctrl-\ x` takes the focused pane off the wall (the session |
| 62 | \\ attached), `Ctrl-\ f` fullscreen, `Ctrl-\ r` resize mode, | 63 | \\ keeps running), `Ctrl-\ f` fullscreen, `Ctrl-\ r` resize mode, |
| 63 | \\ `Ctrl-\ w` zooms out, `Ctrl-\ d` leaves. | 64 | \\ `Ctrl-\ w` zooms out, `Ctrl-\ d` leaves. |
| 64 | \\ | 65 | \\ |
| 65 | \\ `Ctrl-\ s` opens the host picker: every DAEMON on the wall with what | 66 | \\ `Ctrl-\ s` opens the picker, which is how a session joins the wall: |
| 66 | \\ its last poll said. j/k or the arrows move, 1-9 pick a row, Enter or | 67 | \\ every DAEMON with what its last poll said, Enter for that daemon's |
| 67 | \\ c starts a session on the selected machine, x forgets it (the | 68 | \\ sessions, Enter again to add one as a pane. j/k or the arrows move, |
| 68 | \\ sessions keep running), a adds one by spelling, Esc closes. | 69 | \\ 1-9 pick a row, c starts a new session, x forgets a host or ends a |
| 70 | \\ session, a adds a host by spelling, Esc backs out. | ||
| 69 | \\ | 71 | \\ |
| 70 | ; | 72 | ; |
| 71 | 73 | ||
| @@ -858,7 +860,7 @@ test "hosts list: a line longer than the row buffer is still shown, because rm m | |||
| 858 | try std.testing.expect(std.mem.endsWith( | 860 | try std.testing.expect(std.mem.endsWith( |
| 859 | u8, | 861 | u8, |
| 860 | text, | 862 | text, |
| 861 | "\t[bad host: names a session after '#': the wall lists daemons and shows every session they have]\n", | 863 | "\t[bad host: names a session after '#': a host line names a daemon; the layout names sessions]\n", |
| 862 | )); | 864 | )); |
| 863 | } | 865 | } |
| 864 | 866 | ||
src/client/hosts.zig
| Old | New | ||
|---|---|---|---|
| @@ -89,7 +89,7 @@ pub fn isParse(err: anyerror) bool { | |||
| 89 | 89 | ||
| 90 | pub fn reason(err: anyerror) []const u8 { | 90 | pub fn reason(err: anyerror) []const u8 { |
| 91 | return switch (err) { | 91 | return switch (err) { |
| 92 | error.HasSession => "names a session after '#': the wall lists daemons and shows every session they have", | 92 | error.HasSession => "names a session after '#': a host line names a daemon; the layout names sessions", |
| 93 | error.EmptySpec => "empty host", | 93 | error.EmptySpec => "empty host", |
| 94 | error.BadByte => "control byte in host", | 94 | error.BadByte => "control byte in host", |
| 95 | error.BadSpelling => "punctuation in host: a host line names a machine, not a command", | 95 | error.BadSpelling => "punctuation in host: a host line names a machine, not a command", |
| @@ -306,7 +306,10 @@ test "hosts.parse: three spellings classify; a '#' is refused by name" { | |||
| 306 | try std.testing.expectError(error.EmptySpec, parse("")); | 306 | try std.testing.expectError(error.EmptySpec, parse("")); |
| 307 | try std.testing.expectError(error.EmptySpec, parse("--sock ")); | 307 | try std.testing.expectError(error.EmptySpec, parse("--sock ")); |
| 308 | try std.testing.expectError(error.BadByte, parse("bo\x01x")); | 308 | try std.testing.expectError(error.BadByte, parse("bo\x01x")); |
| 309 | try std.testing.expect(std.mem.indexOf(u8, reason(error.HasSession), "daemons") != null); | 309 | // The sentence has to say what a host line IS, not merely that this one |
| 310 | // is wrong: "names a session" alone leaves the user with no next move. | ||
| 311 | try std.testing.expect(std.mem.indexOf(u8, reason(error.HasSession), "daemon") != null); | ||
| 312 | try std.testing.expect(std.mem.indexOf(u8, reason(error.HasSession), "layout") != null); | ||
| 310 | } | 313 | } |
| 311 | 314 | ||
| 312 | test "hosts.parse: a HOST spelling is one word — the file's grammar is one host per line" { | 315 | test "hosts.parse: a HOST spelling is one word — the file's grammar is one host per line" { |
src/tui/wall_test_host.zig
| Old | New | ||
|---|---|---|---|
| @@ -548,7 +548,7 @@ test "addHost: the prompt adds a DAEMON — a flag, a session and a host already | |||
| 548 | // daemon now, and the refusal says which half was one too many. | 548 | // daemon now, and the refusal says which half was one too many. |
| 549 | try std.testing.expect(wall_host.addHost(alloc, &shared, &table, &n, "--sock /c#work", null, 30_000, path) == .refused); | 549 | try std.testing.expect(wall_host.addHost(alloc, &shared, &table, &n, "--sock /c#work", null, 30_000, path) == .refused); |
| 550 | try std.testing.expectEqual(@as(usize, 2), n); | 550 | try std.testing.expectEqual(@as(usize, 2), n); |
| 551 | try std.testing.expect(std.mem.indexOf(u8, wv.takeNotice(&shared, &buf), "daemons") != null); | 551 | try std.testing.expect(std.mem.indexOf(u8, wv.takeNotice(&shared, &buf), "names a daemon") != null); |
| 552 | 552 | ||
| 553 | // A real one: it lands in the table AND in the file, because the wall | 553 | // A real one: it lands in the table AND in the file, because the wall |
| 554 | // the user is looking at and the wall they get back are the same wall. | 554 | // the user is looking at and the wall they get back are the same wall. |