d6bede8d
feat: Ctrl-\ r trades cells between panes
a73x 2026-08-24 18:06
Commit message
docscheck.budget
| Old | New | ||
|---|---|---|---|
| @@ -27,7 +27,7 @@ shellint.zig 0 | |||
| 27 | sockpath.zig 0 | 27 | sockpath.zig 0 |
| 28 | spawn.zig 0 | 28 | spawn.zig 0 |
| 29 | testtmp.zig 0 | 29 | testtmp.zig 0 |
| 30 | wallview.zig 694 | 30 | wallview.zig 858 |
| 31 | wall.zig 0 | 31 | wall.zig 0 |
| 32 | wasm_core.zig 0 | 32 | wasm_core.zig 0 |
| 33 | webhub_main.zig 0 | 33 | webhub_main.zig 0 |
src/wallview.zig
| Old | New | ||
|---|---|---|---|
| @@ -125,6 +125,64 @@ fn dirOf(d: interact.PrefixFilter.Dir) layout.Dir { | |||
| 125 | }; | 125 | }; |
| 126 | } | 126 | } |
| 127 | 127 | ||
| 128 | /// layout.resize always GAINS focus cells; shrink = grow a neighbor at | ||
| 129 | /// focus's expense. Fullscreen refuses — a hidden layout resizing | ||
| 130 | /// invisibly is surprise, not power. | ||
| 131 | fn doResize( | ||
| 132 | alloc: std.mem.Allocator, | ||
| 133 | tiles: []Tile, | ||
| 134 | present: []const bool, | ||
| 135 | shared: *Shared, | ||
| 136 | sel: usize, | ||
| 137 | d: interact.PrefixFilter.Dir, | ||
| 138 | ) bool { | ||
| 139 | if (shared.fullscreen) return false; | ||
| 140 | const ld = dirOf(d); | ||
| 141 | const grow = switch (ld) { | ||
| 142 | .right, .down => true, | ||
| 143 | .left, .up => false, | ||
| 144 | }; | ||
| 145 | const flat = shared.base_flat orelse shared.last_flat orelse return false; | ||
| 146 | const focus_tile: u8 = @intCast(sel); | ||
| 147 | var moved = false; | ||
| 148 | if (grow) { | ||
| 149 | // Focus gains from the sibling toward `ld`; if none there (edge | ||
| 150 | // pane), try the opposite side — gaining from either sibling | ||
| 151 | // widens or tallens the focus. | ||
| 152 | if (shared.tree.resize(alloc, shared.size.rows, shared.size.cols, wallFloors(tiles.len), focus_tile, ld, 1)) { | ||
| 153 | moved = true; | ||
| 154 | } else { | ||
| 155 | const opp = switch (ld) { | ||
| 156 | .right => layout.Dir.left, | ||
| 157 | .down => layout.Dir.up, | ||
| 158 | .left => layout.Dir.right, | ||
| 159 | .up => layout.Dir.down, | ||
| 160 | }; | ||
| 161 | moved = shared.tree.resize(alloc, shared.size.rows, shared.size.cols, wallFloors(tiles.len), focus_tile, opp, 1); | ||
| 162 | } | ||
| 163 | } else { | ||
| 164 | // Shrink: a neighbor on the same axis gains a cell from focus. | ||
| 165 | // Try the side the key points at first, then the opposite — a | ||
| 166 | // pane pressed against one wall can still shrink toward the other. | ||
| 167 | const opp = switch (ld) { | ||
| 168 | .left => layout.Dir.right, | ||
| 169 | .up => layout.Dir.down, | ||
| 170 | .right => layout.Dir.left, | ||
| 171 | .down => layout.Dir.up, | ||
| 172 | }; | ||
| 173 | if (layout.neighbor(flat, focus_tile, ld)) |nb| { | ||
| 174 | moved = shared.tree.resize(alloc, shared.size.rows, shared.size.cols, wallFloors(tiles.len), nb, opp, 1); | ||
| 175 | } | ||
| 176 | if (!moved) { | ||
| 177 | if (layout.neighbor(flat, focus_tile, opp)) |nb| { | ||
| 178 | moved = shared.tree.resize(alloc, shared.size.rows, shared.size.cols, wallFloors(tiles.len), nb, ld, 1); | ||
| 179 | } | ||
| 180 | } | ||
| 181 | } | ||
| 182 | if (moved) relayout(alloc, tiles, present, shared, sel); | ||
| 183 | return moved; | ||
| 184 | } | ||
| 185 | |||
| 128 | const State = enum { | 186 | const State = enum { |
| 129 | connecting, | 187 | connecting, |
| 130 | up, | 188 | up, |
| @@ -2622,8 +2680,9 @@ pub fn run(alloc: std.mem.Allocator, resolved: []const Resolved, entry: Entry) ! | |||
| 2622 | shared.fullscreen = !shared.fullscreen; | 2680 | shared.fullscreen = !shared.fullscreen; |
| 2623 | relayout(alloc, tiles[0..live], present[0..live], &shared, shared.sel); | 2681 | relayout(alloc, tiles[0..live], present[0..live], &shared, shared.sel); |
| 2624 | }, | 2682 | }, |
| 2625 | .resize, | 2683 | .resize => |d| { |
| 2626 | => {}, | 2684 | _ = doResize(alloc, tiles[0..live], present[0..live], &shared, z, d); |
| 2685 | }, | ||
| 2627 | .focus => |idx| { | 2686 | .focus => |idx| { |
| 2628 | if (idx > 0 and idx <= live and present[idx - 1] and idx - 1 != z) { | 2687 | if (idx > 0 and idx <= live and present[idx - 1] and idx - 1 != z) { |
| 2629 | last_focus = z; | 2688 | last_focus = z; |
| @@ -3217,6 +3276,102 @@ test "focus_dir while fullscreened follows the focus" { | |||
| 3217 | try std.testing.expectEqual(@as(usize, 1), shared.sel); | 3276 | try std.testing.expectEqual(@as(usize, 1), shared.sel); |
| 3218 | } | 3277 | } |
| 3219 | 3278 | ||
| 3279 | test "resize: l grows the focused pane, h shrinks it" { | ||
| 3280 | // Spec: h shrinks width, l grows width. layout.resize always makes | ||
| 3281 | // the focus GAIN cells, so shrink = grow a neighbor at focus's | ||
| 3282 | // expense. Two beside panes at 80x24 (floors {3,2}): equal split is | ||
| 3283 | // 39/40 (rail takes 1). l moves the rail right; h moves it left. | ||
| 3284 | const alloc = std.testing.allocator; | ||
| 3285 | var shared = Shared{ .out_fd = -1, .size = .{ .cols = 80, .rows = 24 }, .is_tty = false }; | ||
| 3286 | shared.tree = layout.Tree.init(alloc); | ||
| 3287 | shared.flat_alloc = alloc; | ||
| 3288 | defer shared.tree.deinit(); | ||
| 3289 | defer if (shared.last_flat) |*f| f.deinit(alloc); | ||
| 3290 | defer if (shared.base_flat) |*f| f.deinit(alloc); | ||
| 3291 | try shared.tree.addFirst(0); | ||
| 3292 | try shared.tree.splitRight(0, 1); | ||
| 3293 | const tiles = try alloc.alloc(Tile, 2); | ||
| 3294 | defer alloc.free(tiles); | ||
| 3295 | const present = [_]bool{ true, true }; | ||
| 3296 | for (tiles, 0..) |*t, i| { | ||
| 3297 | t.* = Tile{ | ||
| 3298 | .r = .{ .target = .{ .sock = "/tmp/x" }, .label = "x", .session = "" }, | ||
| 3299 | .rect = .{ .top = 0, .left = 0, .rows = 24, .cols = 80 }, | ||
| 3300 | .shared = &shared, | ||
| 3301 | .idx = i, | ||
| 3302 | .wake_r = -1, | ||
| 3303 | .wake_w = -1, | ||
| 3304 | }; | ||
| 3305 | } | ||
| 3306 | relayout(alloc, tiles, &present, &shared, 0); | ||
| 3307 | const before = tiles[0].rect.cols; | ||
| 3308 | // l (grow width): focus 0 gains from its right sibling. | ||
| 3309 | try std.testing.expect(doResize(alloc, tiles, &present, &shared, 0, .right)); | ||
| 3310 | try std.testing.expect(tiles[0].rect.cols > before); | ||
| 3311 | // h (shrink width): the right neighbor gains a cell back from focus. | ||
| 3312 | try std.testing.expect(doResize(alloc, tiles, &present, &shared, 0, .left)); | ||
| 3313 | try std.testing.expectEqual(before, tiles[0].rect.cols); | ||
| 3314 | } | ||
| 3315 | |||
| 3316 | test "resize: j grows height, k shrinks height" { | ||
| 3317 | const alloc = std.testing.allocator; | ||
| 3318 | var shared = Shared{ .out_fd = -1, .size = .{ .cols = 80, .rows = 24 }, .is_tty = false }; | ||
| 3319 | shared.tree = layout.Tree.init(alloc); | ||
| 3320 | shared.flat_alloc = alloc; | ||
| 3321 | defer shared.tree.deinit(); | ||
| 3322 | defer if (shared.last_flat) |*f| f.deinit(alloc); | ||
| 3323 | defer if (shared.base_flat) |*f| f.deinit(alloc); | ||
| 3324 | try shared.tree.addFirst(0); | ||
| 3325 | try shared.tree.splitBelow(0, 1); | ||
| 3326 | const tiles = try alloc.alloc(Tile, 2); | ||
| 3327 | defer alloc.free(tiles); | ||
| 3328 | const present = [_]bool{ true, true }; | ||
| 3329 | for (tiles, 0..) |*t, i| { | ||
| 3330 | t.* = Tile{ | ||
| 3331 | .r = .{ .target = .{ .sock = "/tmp/x" }, .label = "x", .session = "" }, | ||
| 3332 | .rect = .{ .top = 0, .left = 0, .rows = 24, .cols = 80 }, | ||
| 3333 | .shared = &shared, | ||
| 3334 | .idx = i, | ||
| 3335 | .wake_r = -1, | ||
| 3336 | .wake_w = -1, | ||
| 3337 | }; | ||
| 3338 | } | ||
| 3339 | relayout(alloc, tiles, &present, &shared, 0); | ||
| 3340 | const before = tiles[0].rect.rows; | ||
| 3341 | try std.testing.expect(doResize(alloc, tiles, &present, &shared, 0, .down)); | ||
| 3342 | try std.testing.expect(tiles[0].rect.rows > before); | ||
| 3343 | try std.testing.expect(doResize(alloc, tiles, &present, &shared, 0, .up)); | ||
| 3344 | try std.testing.expectEqual(before, tiles[0].rect.rows); | ||
| 3345 | } | ||
| 3346 | |||
| 3347 | test "resize refuses while fullscreened" { | ||
| 3348 | const alloc = std.testing.allocator; | ||
| 3349 | var shared = Shared{ .out_fd = -1, .size = .{ .cols = 80, .rows = 24 }, .is_tty = false }; | ||
| 3350 | shared.tree = layout.Tree.init(alloc); | ||
| 3351 | shared.flat_alloc = alloc; | ||
| 3352 | defer shared.tree.deinit(); | ||
| 3353 | defer if (shared.last_flat) |*f| f.deinit(alloc); | ||
| 3354 | defer if (shared.base_flat) |*f| f.deinit(alloc); | ||
| 3355 | try shared.tree.addFirst(0); | ||
| 3356 | try shared.tree.splitRight(0, 1); | ||
| 3357 | const tiles = try alloc.alloc(Tile, 2); | ||
| 3358 | defer alloc.free(tiles); | ||
| 3359 | const present = [_]bool{ true, true }; | ||
| 3360 | for (tiles, 0..) |*t, i| { | ||
| 3361 | t.* = Tile{ | ||
| 3362 | .r = .{ .target = .{ .sock = "/tmp/x" }, .label = "x", .session = "" }, | ||
| 3363 | .rect = .{ .top = 0, .left = 0, .rows = 24, .cols = 80 }, | ||
| 3364 | .shared = &shared, | ||
| 3365 | .idx = i, | ||
| 3366 | .wake_r = -1, | ||
| 3367 | .wake_w = -1, | ||
| 3368 | }; | ||
| 3369 | } | ||
| 3370 | shared.fullscreen = true; | ||
| 3371 | relayout(alloc, tiles, &present, &shared, 0); | ||
| 3372 | try std.testing.expect(!doResize(alloc, tiles, &present, &shared, 0, .right)); | ||
| 3373 | } | ||
| 3374 | |||
| 3220 | test "a pump's answer that grew the wall re-cuts it; a mere focus move does not" { | 3375 | test "a pump's answer that grew the wall re-cuts it; a mere focus move does not" { |
| 3221 | const alloc = std.testing.allocator; | 3376 | const alloc = std.testing.allocator; |
| 3222 | var shared = Shared{ .out_fd = -1, .size = .{ .cols = 80, .rows = 24 }, .is_tty = false }; | 3377 | var shared = Shared{ .out_fd = -1, .size = .{ .cols = 80, .rows = 24 }, .is_tty = false }; |
test/e2e.sh
| Old | New | ||
|---|---|---|---|
| @@ -371,8 +371,10 @@ ZMOUSESH="${TMPDIR:-/tmp}/mux-e2e-wallappmouse-$$.sh" | |||
| 371 | # reason every wall leg has. | 371 | # reason every wall leg has. |
| 372 | SOCK53="${TMPDIR:-/tmp}/muxd-e2e-sbrect-$$.sock" | 372 | SOCK53="${TMPDIR:-/tmp}/muxd-e2e-sbrect-$$.sock" |
| 373 | SOCK54="${TMPDIR:-/tmp}/muxd-e2e-fullscreen-$$.sock" | 373 | SOCK54="${TMPDIR:-/tmp}/muxd-e2e-fullscreen-$$.sock" |
| 374 | SOCK55="${TMPDIR:-/tmp}/muxd-e2e-resize-$$.sock" | ||
| 374 | D54PID="" | 375 | D54PID="" |
| 375 | D55PID="" | 376 | D55PID="" |
| 377 | D56PID="" | ||
| 376 | 378 | ||
| 377 | # The wall as attach HISTORY (phase 2). A daemon AND a state home of its | 379 | # The wall as attach HISTORY (phase 2). A daemon AND a state home of its |
| 378 | # own, for the dynamic-wall leg's reason turned up one notch: what these | 380 | # own, for the dynamic-wall leg's reason turned up one notch: what these |
| @@ -1318,6 +1320,7 @@ cleanup() { | |||
| 1318 | [ -S "$SOCK52" ] && "$MUXD" stop --sock "$SOCK52" 2>/dev/null || true | 1320 | [ -S "$SOCK52" ] && "$MUXD" stop --sock "$SOCK52" 2>/dev/null || true |
| 1319 | [ -S "$SOCK53" ] && "$MUXD" stop --sock "$SOCK53" 2>/dev/null || true | 1321 | [ -S "$SOCK53" ] && "$MUXD" stop --sock "$SOCK53" 2>/dev/null || true |
| 1320 | [ -S "$SOCK54" ] && "$MUXD" stop --sock "$SOCK54" 2>/dev/null || true | 1322 | [ -S "$SOCK54" ] && "$MUXD" stop --sock "$SOCK54" 2>/dev/null || true |
| 1323 | [ -S "$SOCK55" ] && "$MUXD" stop --sock "$SOCK55" 2>/dev/null || true | ||
| 1321 | 1324 | ||
| 1322 | # ---- the leak sweep (hygiene kit, 6a) ---- | 1325 | # ---- the leak sweep (hygiene kit, 6a) ---- |
| 1323 | # Here rather than at the bottom of the file, which `set -e` reaches only | 1326 | # Here rather than at the bottom of the file, which `set -e` reaches only |
| @@ -1333,7 +1336,7 @@ cleanup() { | |||
| 1333 | "$D26PID" "$D27PID" "$D28PID" "$D29PID" "$D30PID" "$D31PID" \ | 1336 | "$D26PID" "$D27PID" "$D28PID" "$D29PID" "$D30PID" "$D31PID" \ |
| 1334 | "$D32PID" "$D33PID" "$D34PID" "$D35PID" "$D36PID" "$D37PID" \ | 1337 | "$D32PID" "$D33PID" "$D34PID" "$D35PID" "$D36PID" "$D37PID" \ |
| 1335 | "$D38PID" "$D39PID" "$D40PID" "$D41PID" "$D42PID" "$D43PID" "$D54PID" \ | 1338 | "$D38PID" "$D39PID" "$D40PID" "$D41PID" "$D42PID" "$D43PID" "$D54PID" \ |
| 1336 | "$D55PID" | 1339 | "$D55PID" "$D56PID" |
| 1337 | _leak=0 | 1340 | _leak=0 |
| 1338 | leak_sweep "$_rc" || _leak=1 | 1341 | leak_sweep "$_rc" || _leak=1 |
| 1339 | 1342 | ||
| @@ -1405,7 +1408,10 @@ cleanup() { | |||
| 1405 | "$OUT.sb2cap" "$OUT.sb2cap.err" "$OUT.sb2pc" "$OUT.sb2stop" "$SOCK53" \ | 1408 | "$OUT.sb2cap" "$OUT.sb2cap.err" "$OUT.sb2pc" "$OUT.sb2stop" "$SOCK53" \ |
| 1406 | "$OUT.fs.d" "$OUT.fsa" "$OUT.fsa.err" "$OUT.fsb" "$OUT.fsb.err" \ | 1409 | "$OUT.fs.d" "$OUT.fsa" "$OUT.fsa.err" "$OUT.fsb" "$OUT.fsb.err" \ |
| 1407 | "$OUT.fscap" "$OUT.fscap.err" "$OUT.fspc" "$OUT.fsfa" "$OUT.fsfb" \ | 1410 | "$OUT.fscap" "$OUT.fscap.err" "$OUT.fspc" "$OUT.fsfa" "$OUT.fsfb" \ |
| 1408 | "$OUT.fsstop" "$SOCK54" | 1411 | "$OUT.fsstop" "$SOCK54" \ |
| 1412 | "$OUT.rsz.d" "$OUT.rszcap" "$OUT.rszcap.err" "$OUT.rszpc" \ | ||
| 1413 | "$OUT.rsza" "$OUT.rsza.err" "$OUT.rszb" "$OUT.rszb.err" \ | ||
| 1414 | "$OUT.rszsta" "$OUT.rszstb" "$OUT.rszstop" "$SOCK55" | ||
| 1409 | # ...and the non-tty capture that leg's session feeds. | 1415 | # ...and the non-tty capture that leg's session feeds. |
| 1410 | rm -f "$OUT.nogate" | 1416 | rm -f "$OUT.nogate" |
| 1411 | # ...and its other half: the paste capture and the file nvim wrote, which | 1417 | # ...and its other half: the paste capture and the file nvim wrote, which |
| @@ -7668,8 +7674,89 @@ assert_stopped "$SOCK54" "$D55PID" "fullscreen" "$OUT.fsstop" | |||
| 7668 | D55PID="" | 7674 | D55PID="" |
| 7669 | ok "fullscreen gives the focused tile the terminal; focus follows; f restores" | 7675 | ok "fullscreen gives the focused tile the terminal; focus follows; f restores" |
| 7670 | 7676 | ||
| 7671 | [ "$OK_COUNT" = "64" ] || { | 7677 | # ---- Ctrl-\ r trades cells between panes ----------------------------- |
| 7672 | echo "e2e FAIL: $OK_COUNT scenario checkpoints ran, the pin says 64 —" | 7678 | # |
| 7679 | # Two beside panes at 80x24 (aspect rule → beside). Ctrl-\ r enters | ||
| 7680 | # resize mode; lll grows the focused pane's width by 3 cells, moving the | ||
| 7681 | # rail 3 columns right. The rail is a reverse-video space painted at a | ||
| 7682 | # CUP column; grep the column before and after. Esc exits resize mode, | ||
| 7683 | # and typed prose lands in the focused session (marker echo). | ||
| 7684 | # | ||
| 7685 | # No convergence point: the rail column is a terminal-capture assertion, | ||
| 7686 | # not a daemon-grid comparison. | ||
| 7687 | "$MUXD" run --sock "$SOCK55" --shell /bin/sh > "$OUT.rsz.d" 2>&1 & | ||
| 7688 | D56PID=$! | ||
| 7689 | wait_sock "$SOCK55" "$OUT.rsz.d" "resize daemon never bound" | ||
| 7690 | |||
| 7691 | pipe_mux "$OUT.rsza" "$OUT.rsza.err" timeout 40 "$MUX" --sock "$SOCK55" --session a | ||
| 7692 | pipe_send 'printf "rsz-%%s\\n" init-a\n' | ||
| 7693 | await_out "$OUT.rsza" "rsz-init-a" "resize: session a's marker never reached the client" | ||
| 7694 | pipe_detach | ||
| 7695 | wait_grid "$SOCK55" "rsz-init-a" "resize: session a's marker" a | ||
| 7696 | pipe_mux "$OUT.rszb" "$OUT.rszb.err" timeout 40 "$MUX" --sock "$SOCK55" --session b | ||
| 7697 | pipe_send 'printf "rsz-%%s\\n" init-b\n' | ||
| 7698 | await_out "$OUT.rszb" "rsz-init-b" "resize: session b's marker never reached the client" | ||
| 7699 | pipe_detach | ||
| 7700 | wait_grid "$SOCK55" "rsz-init-b" "resize: session b's marker" b | ||
| 7701 | |||
| 7702 | set +e | ||
| 7703 | # 80x24: aspect rule makes beside; two panes split 39/40 with the rail | ||
| 7704 | # at column 40 (1-indexed). Three l presses grow the left pane by 3, | ||
| 7705 | # moving the rail to column 43. | ||
| 7706 | timeout 90 "$PTYCLIENT" --cols 80 --rows 24 --out "$OUT.rszcap" --err "$OUT.rszcap.err" -- \ | ||
| 7707 | "$MUX" wall "--sock $SOCK55#a" "--sock $SOCK55#b" > "$OUT.rszpc" 2>&1 <<'EOF' | ||
| 7708 | expect rsz-init-b 20000 | ||
| 7709 | settle 700 20000 | ||
| 7710 | send \x1cr | ||
| 7711 | settle 300 10000 | ||
| 7712 | send l | ||
| 7713 | settle 300 10000 | ||
| 7714 | send l | ||
| 7715 | settle 300 10000 | ||
| 7716 | send l | ||
| 7717 | settle 500 15000 | ||
| 7718 | send \x1b | ||
| 7719 | settle 300 10000 | ||
| 7720 | send printf 'rsz-%s\n' grown\n | ||
| 7721 | expect rsz-grown 10000 | ||
| 7722 | settle 400 15000 | ||
| 7723 | send \x1cd | ||
| 7724 | waitexit 10000 | ||
| 7725 | EOF | ||
| 7726 | RC=$? | ||
| 7727 | set -e | ||
| 7728 | [ "$RC" -eq 0 ] || { | ||
| 7729 | echo "e2e FAIL: resize: ptyclient leg exited $RC:" | ||
| 7730 | cat "$OUT.rszpc" "$OUT.rszcap.err"; exit 1; } | ||
| 7731 | # The rail paints as ESC[row;colH followed by ESC[7m (reverse video). | ||
| 7732 | # Extract the column from every CUP that precedes a reverse-video | ||
| 7733 | # space — the rail's column — and verify it moved right by 3. | ||
| 7734 | _rail_re=$'\x1b\\[[0-9][0-9]*;[0-9][0-9]*H\x1b\\[7m' | ||
| 7735 | # Before resize: rail at column 40 (1-indexed). After lll: column 43. | ||
| 7736 | # Grab all rail CUP columns; the last one is the post-resize position. | ||
| 7737 | _rail_cols=$(grep -ao "$_rail_re" "$OUT.rszcap" | \ | ||
| 7738 | sed 's/.*\x1b\[[0-9]*;\([0-9]*\)H\x1b\[7m/\1/' | sort -n) | ||
| 7739 | _last_rail=$(echo "$_rail_cols" | tail -1) | ||
| 7740 | [ -n "$_last_rail" ] || { | ||
| 7741 | echo "e2e FAIL: resize: no rail glyph found in the capture:" | ||
| 7742 | cat "$OUT.rszcap"; exit 1; } | ||
| 7743 | # 39 cols + rail at 40 initially; 42 cols + rail at 43 after lll. | ||
| 7744 | [ "$_last_rail" -ge 43 ] || { | ||
| 7745 | echo "e2e FAIL: resize: rail column is $_last_rail, want >=43 (40 + 3 grows):" | ||
| 7746 | cat "$OUT.rszcap"; exit 1; } | ||
| 7747 | # Esc ended resize mode and typed prose reached the focused session. | ||
| 7748 | timeout 20 "$MUXA" capture --sock "$SOCK55" --session a > "$OUT.rszsta" 2>&1 | ||
| 7749 | timeout 20 "$MUXA" capture --sock "$SOCK55" --session b > "$OUT.rszstb" 2>&1 | ||
| 7750 | if ! grep -q "rsz-grown" "$OUT.rszsta" && ! grep -q "rsz-grown" "$OUT.rszstb"; then | ||
| 7751 | echo "e2e FAIL: resize: rsz-grown reached neither session after Esc:" | ||
| 7752 | cat "$OUT.rszsta" "$OUT.rszstb"; exit 1 | ||
| 7753 | fi | ||
| 7754 | assert_stopped "$SOCK55" "$D56PID" "resize" "$OUT.rszstop" | ||
| 7755 | D56PID="" | ||
| 7756 | ok "Ctrl-\\ r trades cells between panes; Esc returns to prose" | ||
| 7757 | |||
| 7758 | [ "$OK_COUNT" = "65" ] || { | ||
| 7759 | echo "e2e FAIL: $OK_COUNT scenario checkpoints ran, the pin says 65 —" | ||
| 7673 | echo " a scenario was added (update the pin) or silently lost" | 7760 | echo " a scenario was added (update the pin) or silently lost" |
| 7674 | exit 1 | 7761 | exit 1 |
| 7675 | } | 7762 | } |
| @@ -7677,4 +7764,4 @@ ok "fullscreen gives the focused tile the terminal; focus follows; f restores" | |||
| 7677 | echo "e2e FAIL: $CONV_COUNT convergence points ran, the pin says 37" | 7764 | echo "e2e FAIL: $CONV_COUNT convergence points ran, the pin says 37" |
| 7678 | exit 1 | 7765 | exit 1 |
| 7679 | } | 7766 | } |
| 7680 | echo "e2e OK (63 scenarios, 37 convergence points)" | 7767 | echo "e2e OK (64 scenarios, 37 convergence points)" |