From 373cd6f231cd8122218406af6c0aa50c7c6285e9 Mon Sep 17 00:00:00 2001 From: Rock Date: Mon, 11 May 2026 12:15:05 +0800 Subject: [PATCH] feat: add Shift+Up/Down shortcuts to scroll chat history --- .gitignore | 2 +- crates/tui/src/localization.rs | 8 +++++++- crates/tui/src/tui/keybindings.rs | 7 ++++++- crates/tui/src/tui/ui.rs | 8 +++++++- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 90595039..6715c5b0 100644 --- a/.gitignore +++ b/.gitignore @@ -80,4 +80,4 @@ apps/ .claude/*.local.json # Maintainer-internal design notes (trade-secret material, never published) -.private/ +.private/ \ No newline at end of file diff --git a/crates/tui/src/localization.rs b/crates/tui/src/localization.rs index 5fd8d1ca..ee603dad 100644 --- a/crates/tui/src/localization.rs +++ b/crates/tui/src/localization.rs @@ -299,6 +299,7 @@ pub enum MessageId { KbScrollTranscript, KbNavigateHistory, KbScrollTranscriptAlt, + KbBrowseHistory, KbScrollPage, KbJumpTopBottom, KbJumpTopBottomEmpty, @@ -520,6 +521,7 @@ pub const ALL_MESSAGE_IDS: &[MessageId] = &[ MessageId::KbScrollTranscript, MessageId::KbNavigateHistory, MessageId::KbScrollTranscriptAlt, + MessageId::KbBrowseHistory, MessageId::KbScrollPage, MessageId::KbJumpTopBottom, MessageId::KbJumpTopBottomEmpty, @@ -922,6 +924,7 @@ fn english(id: MessageId) -> &'static str { "Scroll transcript, navigate input history, or select composer attachments" } MessageId::KbNavigateHistory => "Navigate input history", + MessageId::KbBrowseHistory => "Browse conversation history", MessageId::KbScrollTranscriptAlt => "Scroll transcript", MessageId::KbScrollPage => "Scroll transcript by page", MessageId::KbJumpTopBottom => "Jump to top / bottom of transcript", @@ -1262,6 +1265,7 @@ fn japanese(id: MessageId) -> Option<&'static str> { "会話履歴をスクロール、入力履歴を移動、または添付ファイルを選択" } MessageId::KbNavigateHistory => "入力履歴を移動", + MessageId::KbBrowseHistory => "会話履歴を閲覧", MessageId::KbScrollTranscriptAlt => "会話履歴をスクロール", MessageId::KbScrollPage => "ページ単位で会話履歴をスクロール", MessageId::KbJumpTopBottom => "会話履歴の先頭/末尾へジャンプ", @@ -1569,6 +1573,7 @@ fn chinese_simplified(id: MessageId) -> Option<&'static str> { } MessageId::KbScrollTranscript => "滚动对话记录、浏览输入历史或选择附件", MessageId::KbNavigateHistory => "浏览输入历史", + MessageId::KbBrowseHistory => "浏览对话历史", MessageId::KbScrollTranscriptAlt => "滚动对话记录", MessageId::KbScrollPage => "按页滚动对话记录", MessageId::KbJumpTopBottom => "跳转到对话顶部/底部", @@ -1890,6 +1895,7 @@ fn portuguese_brazil(id: MessageId) -> Option<&'static str> { "Rolar transcrição, navegar histórico de entrada ou selecionar anexos do compositor" } MessageId::KbNavigateHistory => "Navegar histórico de entrada", + MessageId::KbBrowseHistory => "Navegar histórico da conversa", MessageId::KbScrollTranscriptAlt => "Rolar transcrição", MessageId::KbScrollPage => "Rolar transcrição por página", MessageId::KbJumpTopBottom => "Pular para topo / fim da transcrição", @@ -2161,4 +2167,4 @@ mod tests { } out } -} +} \ No newline at end of file diff --git a/crates/tui/src/tui/keybindings.rs b/crates/tui/src/tui/keybindings.rs index 10f0ad7b..8322149c 100644 --- a/crates/tui/src/tui/keybindings.rs +++ b/crates/tui/src/tui/keybindings.rs @@ -93,6 +93,11 @@ pub const KEYBINDINGS: &[KeybindingEntry] = &[ description_id: crate::localization::MessageId::KbScrollTranscriptAlt, section: KeybindingSection::Navigation, }, + KeybindingEntry { + chord: "Shift+↑ / Shift+↓", + description_id: crate::localization::MessageId::KbBrowseHistory, + section: KeybindingSection::Navigation, + }, KeybindingEntry { chord: "PgUp / PgDn", description_id: crate::localization::MessageId::KbScrollPage, @@ -351,4 +356,4 @@ mod tests { ranks.dedup(); assert_eq!(ranks.len(), sections.len(), "ranks must be unique"); } -} +} \ No newline at end of file diff --git a/crates/tui/src/tui/ui.rs b/crates/tui/src/tui/ui.rs index 32808bca..606fe13d 100644 --- a/crates/tui/src/tui/ui.rs +++ b/crates/tui/src/tui/ui.rs @@ -2439,6 +2439,9 @@ async fn run_event_loop( KeyCode::Up if key.modifiers.contains(KeyModifiers::ALT) => { app.scroll_up(3); } + KeyCode::Up if key.modifiers.contains(KeyModifiers::SHIFT) => { + app.scroll_up(3); + } KeyCode::Up if key.modifiers.is_empty() && mention_menu_open @@ -2485,6 +2488,9 @@ async fn run_event_loop( KeyCode::Down if key.modifiers.contains(KeyModifiers::ALT) => { app.scroll_down(3); } + KeyCode::Down if key.modifiers.contains(KeyModifiers::SHIFT) => { + app.scroll_down(3); + } KeyCode::Down if key.modifiers.is_empty() && mention_menu_open => { app.mention_menu_selected = (app.mention_menu_selected + 1) .min(mention_menu_entries.len().saturating_sub(1)); @@ -8840,4 +8846,4 @@ fn extract_reasoning_header(text: &str) -> Option { } #[cfg(test)] -mod tests; +mod tests; \ No newline at end of file