docs: a restored pane is a resized pane, as far as capture is concerned

`--scrollback`'s help said the flag makes no difference "for a
never-resized pane", and the reference said the same. Both are wrong for
the panes where the difference matters most.

I killed a daemon under a running GUI, watched it come back, and captured
a pane that had `RESTORE-MARKER-7788` in it. Plain `capture` answered with
two lines — the restore banner and a prompt — while `--scrollback` had the
marker and everything around it. Nothing had been resized.

`ReplayRing::seeded` ends with `resize(size)`, deliberately: the restored
screen is replayed at the size it was recorded at, and the new shell
writes at the size the pane came back as. When those differ the restored
screen is sealed into an earlier segment, and the default capture cannot
see it.

That is the agent-facing primitive answering "almost nothing" for a pane
that kept its screen, with the help explaining that this only happens
after a resize. Both now say restores count, and say what a plain capture
looks like when it does.

`resize` returns early on an unchanged size, so a pane that comes back the
same shape really does keep one segment — the test covers both sides of
that, which is the part the wording turns on.
This commit is contained in:
l0ng-ai
2026-08-16 11:05:39 +08:00
parent e6b4265621
commit ed29cdce47
3 changed files with 48 additions and 2 deletions
+5 -2
View File
@@ -409,8 +409,11 @@ pub struct CaptureArgs {
#[arg(
long,
help = "Print the whole scrollback ring; the ring splits into segments on resize, \
and without this flag only the last segment is printed (for a \
never-resized pane the two are identical)"
and without this flag only the last segment is printed. A pane restored \
after its daemon died counts as resized: the screen it was seeded with was \
recorded at the size it had then, so it sits in an earlier segment unless \
the new pane happens to match it, and plain `capture` shows the banner and \
the new prompt alone"
)]
pub scrollback: bool,
+38
View File
@@ -3660,6 +3660,44 @@ mod tests {
assert!(rx.try_recv().is_err());
}
/// The seam a restore leaves is the snapshot's *size*, not the restore.
///
/// `tty7 capture` prints the last segment alone, so this decides whether a
/// restored pane answers with the screen it kept or with the banner and a
/// prompt. Restoring at the size the screen was recorded at leaves one
/// segment and the capture holds everything; a pane that comes back a
/// different shape does not, which is what the flag's help now says.
#[test]
fn a_restore_only_starts_a_segment_when_the_pane_comes_back_a_different_size() {
use crate::daemon::scrollback::Segment;
let seed = |at: WinSize, back: WinSize| {
let mut ring = ReplayRing::seeded(
vec![Segment {
size: at,
bytes: b"what the dead pane had on it".to_vec(),
}],
back,
);
ring.append(b"the new shell's prompt");
ring
};
let same = seed(ws(80, 24), ws(80, 24));
assert_eq!(
same.segments.len(),
1,
"the same size, so the new shell writes on into the restored screen"
);
let different = seed(ws(100, 24), ws(80, 30));
assert_eq!(
different.segments.len(),
2,
"a different size, so the restored screen is left behind in its own segment"
);
}
#[test]
fn a_seeded_ring_replays_the_old_screen_at_the_size_it_was_written() {
use crate::daemon::scrollback::Segment;
+5
View File
@@ -161,6 +161,11 @@ The pane's replay. Two independent choices:
`--scrollback`. The ring splits into segments on resize, so for a pane that was
never resized the two are identical.
A pane restored after its daemon died counts as resized. The screen it was
seeded with was recorded at whatever size it had then, so unless the new pane
comes back that same shape, plain `capture` answers with the restore banner and
the new prompt alone — everything above the banner needs `--scrollback`.
**In what form** — without `--plain`, the stored bytes with ANSI escapes intact,
decoded as UTF-8 (invalid bytes become U+FFFD). With `--plain`, those bytes
replayed through a terminal grid and printed as the text they produced.