fix(claudbg-4gtn): clamp Space/PageDown scroll to end of transcript content

Compute total rendered lines on each page-down keypress and cap the
scroll offset so it cannot scroll past the last line.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
main
Elijah Voigt 2 months ago
parent 3b209745fc
commit ae337dcf94

@ -1,10 +1,15 @@
--- ---
# claudbg-4gtn # claudbg-4gtn
title: 'TUI: Space/PageDown scrolls past end of transcript' title: 'TUI: Space/PageDown scrolls past end of transcript'
status: todo status: completed
type: bug type: bug
priority: normal
created_at: 2026-03-31T23:45:06Z created_at: 2026-03-31T23:45:06Z
updated_at: 2026-03-31T23:45:06Z updated_at: 2026-04-01T05:51:55Z
--- ---
Space/PageDown in the transcript view continues scrolling past the last line. It should stop at the bottom of the transcript content. Space/PageDown in the transcript view continues scrolling past the last line. It should stop at the bottom of the transcript content.
## Summary of Changes
In `src/tui/screens/transcript.rs`, the PageDown/Space handler now clamps scroll to the actual content length: computes `total_lines = build_chat_lines(...).len()` and `max_scroll = total_lines.saturating_sub(page_height)`, then applies `.min(max_scroll)` to prevent scrolling into empty space. Updated 3 existing tests to populate entries before testing scroll behavior.

@ -624,7 +624,10 @@ pub fn handle_transcript_event(event: Event, state: &mut AppState) -> bool {
} }
KeyCode::PageDown | KeyCode::Char(' ') => { KeyCode::PageDown | KeyCode::Char(' ') => {
let page = (state.transcript_page_height as usize).max(1); let page = (state.transcript_page_height as usize).max(1);
state.transcript_scroll = state.transcript_scroll.saturating_add(page); let total_lines =
build_chat_lines(&state.transcript_entries, state.color_enabled).len();
let max_scroll = total_lines.saturating_sub(state.transcript_page_height as usize);
state.transcript_scroll = state.transcript_scroll.saturating_add(page).min(max_scroll);
true true
} }
// Horizontal scroll — only meaningful in ChatLog. // Horizontal scroll — only meaningful in ChatLog.
@ -1197,6 +1200,10 @@ mod tests {
#[test] #[test]
fn space_scrolls_down_by_page() { fn space_scrolls_down_by_page() {
let mut state = transcript_state(); let mut state = transcript_state();
// Populate enough entries that clamping won't prevent scrolling by the full page.
state.transcript_entries = (0..100)
.map(|i| user_text_entry(&format!("line {i}")))
.collect();
state.transcript_page_height = 20; state.transcript_page_height = 20;
handle_transcript_event(press(KeyCode::Char(' ')), &mut state); handle_transcript_event(press(KeyCode::Char(' ')), &mut state);
assert_eq!(state.transcript_scroll, 20); assert_eq!(state.transcript_scroll, 20);
@ -1205,6 +1212,10 @@ mod tests {
#[test] #[test]
fn pagedown_scrolls_down_by_page() { fn pagedown_scrolls_down_by_page() {
let mut state = transcript_state(); let mut state = transcript_state();
// Populate enough entries that clamping won't prevent scrolling by the full page.
state.transcript_entries = (0..100)
.map(|i| user_text_entry(&format!("line {i}")))
.collect();
state.transcript_page_height = 15; state.transcript_page_height = 15;
handle_transcript_event(press(KeyCode::PageDown), &mut state); handle_transcript_event(press(KeyCode::PageDown), &mut state);
assert_eq!(state.transcript_scroll, 15); assert_eq!(state.transcript_scroll, 15);
@ -1392,6 +1403,8 @@ mod tests {
#[test] #[test]
fn page_scroll_uses_one_when_height_is_zero() { fn page_scroll_uses_one_when_height_is_zero() {
let mut state = transcript_state(); let mut state = transcript_state();
// Need at least one line of content so clamping allows scroll=1.
state.transcript_entries = vec![user_text_entry("hello")];
state.transcript_page_height = 0; state.transcript_page_height = 0;
handle_transcript_event(press(KeyCode::Char(' ')), &mut state); handle_transcript_event(press(KeyCode::Char(' ')), &mut state);
assert_eq!(state.transcript_scroll, 1); assert_eq!(state.transcript_scroll, 1);

Loading…
Cancel
Save