diff --git a/crates/tty7-cli/src/cli.rs b/crates/tty7-cli/src/cli.rs index 63ccb3ee..8230a4b4 100644 --- a/crates/tty7-cli/src/cli.rs +++ b/crates/tty7-cli/src/cli.rs @@ -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, diff --git a/crates/tty7-core/src/daemon/pane.rs b/crates/tty7-core/src/daemon/pane.rs index 8c58a049..9454e2bc 100644 --- a/crates/tty7-core/src/daemon/pane.rs +++ b/crates/tty7-core/src/daemon/pane.rs @@ -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; diff --git a/docs/cli/reference.mdx b/docs/cli/reference.mdx index 046fa8d3..601b46e0 100644 --- a/docs/cli/reference.mdx +++ b/docs/cli/reference.mdx @@ -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.