fix(tui/scrollback): keep last line visible in tiny viewports

This commit is contained in:
James Peter
2026-01-13 23:37:40 +10:00
parent f710c9c889
commit 7c1e7747c3
2 changed files with 8 additions and 49 deletions
+3 -44
View File
@@ -38126,50 +38126,9 @@ impl WidgetRef for &ChatWidget<'_> {
scroll_from_top = scroll_from_top.saturating_sub(overscan_extra);
}
if clamped_scroll_offset == 0 && content_area.height == 1 {
let history_len = self.history_cells.len();
let last_kind = if request_count == 0 {
None
} else if request_count - 1 < history_len {
Some(self.history_cells[request_count - 1].kind())
} else {
let mut extra_idx = (request_count - 1).saturating_sub(history_len);
if let Some(ref cell) = self.active_exec_cell {
if extra_idx == 0 {
Some(cell.kind())
} else {
extra_idx = extra_idx.saturating_sub(1);
if let Some(ref cell) = streaming_cell {
if extra_idx == 0 {
Some(cell.kind())
} else {
extra_idx = extra_idx.saturating_sub(1);
queued_preview_cells
.get(extra_idx)
.map(|cell| cell.kind())
}
} else {
queued_preview_cells.get(extra_idx).map(|cell| cell.kind())
}
}
} else if let Some(ref cell) = streaming_cell {
if extra_idx == 0 {
Some(cell.kind())
} else {
extra_idx = extra_idx.saturating_sub(1);
queued_preview_cells.get(extra_idx).map(|cell| cell.kind())
}
} else {
queued_preview_cells.get(extra_idx).map(|cell| cell.kind())
}
};
if matches!(last_kind, Some(history_cell::HistoryCellType::Assistant)) {
// Assistant cells have bottom padding; when the history viewport is only
// one row tall (e.g., large composer), avoid landing on a blank padding row.
scroll_from_top = scroll_from_top.saturating_sub(1);
}
}
// NOTE: when pinned to the bottom, we intentionally do not try to skip
// potential trailing padding rows inside cells. Doing so is brittle and
// can hide the last visible content line in very small viewports.
tracing::debug!(
target: "code_tui::scrollback",
+5 -5
View File
@@ -591,7 +591,9 @@ fn exec_interrupts_flush_when_stream_idles() {
});
harness.flush_into_widget();
// Drain any pending commits so the stream is idle but still marked active.
harness.drive_commit_tick();
for _ in 0..3 {
harness.drive_commit_tick();
}
// Queue an Exec begin; it should not stay deferred once the stream is idle.
harness.handle_event(Event {
@@ -613,11 +615,9 @@ fn exec_interrupts_flush_when_stream_idles() {
std::thread::sleep(Duration::from_millis(200));
harness.flush_into_widget();
let output = render_chat_widget_to_vt100(&mut harness, 80, 12);
assert!(
output.contains("Thinking through the next steps"),
"stream flush should still render the latest output:\n{}",
output
harness.running_exec_call_ids().contains(&call_id),
"exec begin should flush once the stream is idle"
);
}