feat(kebab-tui): p9-fb-24 task 3 — Ask PgUp/PgDn page scroll
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -439,6 +439,24 @@ pub fn handle_key_ask(state: &mut App, key: KeyEvent) -> KeyOutcome {
|
|||||||
s.input.delete_after();
|
s.input.delete_after();
|
||||||
KeyOutcome::Continue
|
KeyOutcome::Continue
|
||||||
}
|
}
|
||||||
|
// p9-fb-24: PgUp / PgDn page-scroll the transcript by
|
||||||
|
// `pager::PAGE_STEP` rows. Mode-agnostic (physical keys, no
|
||||||
|
// typing ambiguity). Both flip `follow_tail` to false so the
|
||||||
|
// user pinning the view via paging doesn't get yanked back to
|
||||||
|
// the bottom on the next streamed token (same contract as
|
||||||
|
// `j` / `k` from p9-fb-22).
|
||||||
|
(KeyCode::PageDown, _) => {
|
||||||
|
let s = state.ask.as_mut().unwrap();
|
||||||
|
s.follow_tail = false;
|
||||||
|
s.scroll = s.scroll.saturating_add(crate::pager::PAGE_STEP);
|
||||||
|
KeyOutcome::Continue
|
||||||
|
}
|
||||||
|
(KeyCode::PageUp, _) => {
|
||||||
|
let s = state.ask.as_mut().unwrap();
|
||||||
|
s.follow_tail = false;
|
||||||
|
s.scroll = s.scroll.saturating_sub(crate::pager::PAGE_STEP);
|
||||||
|
KeyOutcome::Continue
|
||||||
|
}
|
||||||
// Insert mode: every non-chord Char (incl. e/j/k) types into
|
// Insert mode: every non-chord Char (incl. e/j/k) types into
|
||||||
// input. CTRL/ALT chords stay reserved.
|
// input. CTRL/ALT chords stay reserved.
|
||||||
(KeyCode::Char(c), m)
|
(KeyCode::Char(c), m)
|
||||||
|
|||||||
@@ -784,6 +784,60 @@ fn ctrl_l_resets_follow_tail_in_ask() {
|
|||||||
assert!(app.ask.as_ref().unwrap().follow_tail);
|
assert!(app.ask.as_ref().unwrap().follow_tail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// p9-fb-24: PgDn advances Ask scroll by `PAGE_STEP` (= 10) and
|
||||||
|
/// disengages follow-tail (matches `j` semantics — manual scroll =
|
||||||
|
/// freeze).
|
||||||
|
#[test]
|
||||||
|
fn page_down_advances_scroll_and_freezes_follow_tail_in_ask() {
|
||||||
|
let mut app = fresh_app();
|
||||||
|
app.mode = kebab_tui::Mode::Normal;
|
||||||
|
let outcome = handle_key_ask(
|
||||||
|
&mut app,
|
||||||
|
KeyEvent::new(KeyCode::PageDown, KeyModifiers::NONE),
|
||||||
|
);
|
||||||
|
assert_eq!(outcome, KeyOutcome::Continue);
|
||||||
|
let s = app.ask.as_ref().unwrap();
|
||||||
|
assert_eq!(s.scroll, 10, "PgDn shifts scroll by PAGE_STEP");
|
||||||
|
assert!(!s.follow_tail, "PgDn freezes follow_tail like j/k");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// p9-fb-24: PgUp rewinds Ask scroll by `PAGE_STEP` (saturating at 0)
|
||||||
|
/// and disengages follow-tail.
|
||||||
|
#[test]
|
||||||
|
fn page_up_rewinds_scroll_saturating_and_freezes_follow_tail_in_ask() {
|
||||||
|
let mut app = fresh_app();
|
||||||
|
app.mode = kebab_tui::Mode::Normal;
|
||||||
|
app.ask.as_mut().unwrap().scroll = 25;
|
||||||
|
app.ask.as_mut().unwrap().follow_tail = true;
|
||||||
|
handle_key_ask(
|
||||||
|
&mut app,
|
||||||
|
KeyEvent::new(KeyCode::PageUp, KeyModifiers::NONE),
|
||||||
|
);
|
||||||
|
let s = app.ask.as_ref().unwrap();
|
||||||
|
assert_eq!(s.scroll, 15);
|
||||||
|
assert!(!s.follow_tail);
|
||||||
|
app.ask.as_mut().unwrap().scroll = 3;
|
||||||
|
handle_key_ask(
|
||||||
|
&mut app,
|
||||||
|
KeyEvent::new(KeyCode::PageUp, KeyModifiers::NONE),
|
||||||
|
);
|
||||||
|
assert_eq!(app.ask.as_ref().unwrap().scroll, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// p9-fb-24: PgUp / PgDn fire from BOTH Insert and Normal modes
|
||||||
|
/// (physical keys, no typing ambiguity — same as Left/Right/Home/End
|
||||||
|
/// from p9-fb-22).
|
||||||
|
#[test]
|
||||||
|
fn page_keys_fire_from_insert_mode_in_ask() {
|
||||||
|
let mut app = fresh_app();
|
||||||
|
app.mode = kebab_tui::Mode::Insert;
|
||||||
|
handle_key_ask(
|
||||||
|
&mut app,
|
||||||
|
KeyEvent::new(KeyCode::PageDown, KeyModifiers::NONE),
|
||||||
|
);
|
||||||
|
assert_eq!(app.ask.as_ref().unwrap().scroll, 10);
|
||||||
|
}
|
||||||
|
|
||||||
/// p9-fb-22 (issue #95): when follow_tail is on and the transcript
|
/// p9-fb-22 (issue #95): when follow_tail is on and the transcript
|
||||||
/// has many lines, the rendered buffer's last visible line includes
|
/// has many lines, the rendered buffer's last visible line includes
|
||||||
/// content from the tail of the answer (not the head).
|
/// content from the tail of the answer (not the head).
|
||||||
|
|||||||
Reference in New Issue
Block a user