From a6a0b9a0db293b839d2e8827cff0a81319806a0f Mon Sep 17 00:00:00 2001 From: Elijah Voigt Date: Tue, 31 Mar 2026 22:53:54 -0700 Subject: [PATCH] feat(claudbg-6t38,claudbg-trk3): add gg/G vim-style jump navigation in transcript gg (double-g): jump to top of transcript via pending_g chord detection. G (Shift+G): jump to bottom of transcript. Co-Authored-By: Claude Sonnet 4.6 --- ...6t38--tui-gg-jumps-to-top-of-transcript.md | 9 ++++++-- ...ui-shiftg-jumps-to-bottom-of-transcript.md | 9 ++++++-- src/tui/screens/transcript.rs | 22 +++++++++++++++++++ src/tui/state.rs | 12 +++++++++- 4 files changed, 47 insertions(+), 5 deletions(-) diff --git a/.beans/claudbg-6t38--tui-gg-jumps-to-top-of-transcript.md b/.beans/claudbg-6t38--tui-gg-jumps-to-top-of-transcript.md index 0b89e1d..b5805b3 100644 --- a/.beans/claudbg-6t38--tui-gg-jumps-to-top-of-transcript.md +++ b/.beans/claudbg-6t38--tui-gg-jumps-to-top-of-transcript.md @@ -1,10 +1,15 @@ --- # claudbg-6t38 title: 'TUI: gg jumps to top of transcript' -status: todo +status: completed type: feature +priority: normal created_at: 2026-03-31T23:45:20Z -updated_at: 2026-03-31T23:45:20Z +updated_at: 2026-04-01T05:53:50Z --- Add gg (double-g) keybinding in the transcript view to jump to the top of the transcript (vim-style gg navigation). Requires buffering single 'g' keypress to detect the chord. + +## Summary of Changes + +Added `pending_g: bool` field to `AppState` (init to false). In `handle_transcript_event`, clear `pending_g` on any key that isn't 'g', then handle 'g': if `pending_g` was already set, scroll to top (gg); otherwise set `pending_g = true`. diff --git a/.beans/claudbg-trk3--tui-shiftg-jumps-to-bottom-of-transcript.md b/.beans/claudbg-trk3--tui-shiftg-jumps-to-bottom-of-transcript.md index 97bc100..5e6231c 100644 --- a/.beans/claudbg-trk3--tui-shiftg-jumps-to-bottom-of-transcript.md +++ b/.beans/claudbg-trk3--tui-shiftg-jumps-to-bottom-of-transcript.md @@ -1,10 +1,15 @@ --- # claudbg-trk3 title: 'TUI: Shift+G jumps to bottom of transcript' -status: todo +status: completed type: feature +priority: normal created_at: 2026-03-31T23:45:17Z -updated_at: 2026-03-31T23:45:17Z +updated_at: 2026-04-01T05:53:50Z --- Add Shift+G keybinding in the transcript view to jump to the bottom of the transcript (vim-style G navigation). + +## Summary of Changes + +Added `KeyCode::Char('G')` arm in `handle_transcript_event`: computes total rendered lines via `build_chat_lines` and sets `transcript_scroll` to `total_lines.saturating_sub(page_height)` — same clamping as PageDown, but jumps directly to bottom. diff --git a/src/tui/screens/transcript.rs b/src/tui/screens/transcript.rs index 1f1ee14..49eb324 100644 --- a/src/tui/screens/transcript.rs +++ b/src/tui/screens/transcript.rs @@ -536,6 +536,11 @@ pub fn handle_transcript_event(event: Event, state: &mut AppState) -> bool { return handle_search_input_event(key.code, state); } + // Clear the pending-g chord state on any key that is not 'g' itself. + if !matches!(key.code, KeyCode::Char('g')) { + state.pending_g = false; + } + match key.code { // Toggle focus: ChatLog → SubagentsPanel → SearchInput → ChatLog. KeyCode::Tab => { @@ -655,6 +660,23 @@ pub fn handle_transcript_event(event: Event, state: &mut AppState) -> bool { false } } + // gg → jump to top of transcript (vim-style). + KeyCode::Char('g') => { + if std::mem::take(&mut state.pending_g) { + state.transcript_scroll = 0; + } else { + state.pending_g = true; + } + true + } + // Shift+G → jump to bottom of transcript (vim-style). + KeyCode::Char('G') => { + let total_lines = + build_chat_lines(&state.transcript_entries, state.color_enabled).len(); + state.transcript_scroll = + total_lines.saturating_sub(state.transcript_page_height as usize); + true + } _ => false, } } diff --git a/src/tui/state.rs b/src/tui/state.rs index 54fc087..770ba98 100644 --- a/src/tui/state.rs +++ b/src/tui/state.rs @@ -157,6 +157,11 @@ pub struct AppState { /// `None` means the user is not currently browsing history. pub filter_history_pos: Option, + // ── Keyboard chord state ───────────────────────────────────────────────── + /// Tracks whether a leading `g` keypress was seen, awaiting a second `g` + /// to complete the `gg` (jump-to-top) chord. Cleared on any other key. + pub pending_g: bool, + // ── Display ───────────────────────────────────────────────────────────── /// Whether color coding is enabled in transcript views. /// @@ -174,7 +179,11 @@ impl AppState { /// Return the path to the filter history file: `~/.claude/claudbg.tui.history`. fn history_file_path() -> Option { let home = std::env::var("HOME").ok()?; - Some(PathBuf::from(home).join(".claude").join("claudbg.tui.history")) + Some( + PathBuf::from(home) + .join(".claude") + .join("claudbg.tui.history"), + ) } /// Load filter history lines from disk. @@ -240,6 +249,7 @@ impl AppState { filter_active: String::new(), filter_history, filter_history_pos: None, + pending_g: false, color_enabled, should_quit: false, }