mirror of
https://github.com/openai/codex.git
synced 2026-10-19 16:17:49 +00:00
Move the activity inspection hint into keyboard shortcut help (#47338)
## What changed - Add “Inspect activity” to the shortcut help’s Session section, using the runtime `focus_activity` binding (`F4` by default) and hiding it when unbound. - Remove the passive activity footer hint so composer hints remain available. Keep previous, next, details, and back controls while activity is focused, shortening them to fit the available width. ## Testing Add snapshot coverage for default, remapped, and disabled activity shortcuts. Update footer coverage to verify the absence of passive hints and interactive controls at 20, 32, and 80 columns. GitOrigin-RevId: 52ca6f4992778cd3cf3eebe163d533e69a8dabeb
This commit is contained in:
@@ -747,6 +747,8 @@ impl ChatComposer {
|
||||
.primary_hint(KeymapContext::Global, "open_transcript"),
|
||||
find_transcript_key: default_keymap
|
||||
.primary_hint(KeymapContext::Global, "find_transcript"),
|
||||
focus_activity_key: default_keymap
|
||||
.primary_hint(KeymapContext::Global, "focus_activity"),
|
||||
insert_newline_key: footer_insert_newline_key(
|
||||
&default_keymap.editor.insert_newline,
|
||||
use_shift_enter_hint,
|
||||
@@ -1031,6 +1033,8 @@ impl ChatComposer {
|
||||
keymap.primary_hint(KeymapContext::Global, "open_transcript");
|
||||
self.footer.find_transcript_key =
|
||||
keymap.primary_hint(KeymapContext::Global, "find_transcript");
|
||||
self.footer.focus_activity_key =
|
||||
keymap.primary_hint(KeymapContext::Global, "focus_activity");
|
||||
self.footer.insert_newline_key =
|
||||
match keymap.primary_hint(KeymapContext::Editor, "insert_newline") {
|
||||
hint @ Some(ShortcutHint::Chord { .. }) => hint,
|
||||
@@ -3864,6 +3868,7 @@ impl ChatComposer {
|
||||
edit_previous: Some(key_hint::plain(KeyCode::Esc).into()),
|
||||
show_transcript: self.footer.show_transcript_key,
|
||||
find_transcript: self.footer.find_transcript_key,
|
||||
focus_activity: self.footer.focus_activity_key,
|
||||
history_search: self.footer.history_search_key,
|
||||
reasoning_down: self.footer.reasoning_down_key,
|
||||
reasoning_up: self.footer.reasoning_up_key,
|
||||
|
||||
@@ -222,6 +222,7 @@ pub(super) struct FooterState {
|
||||
pub(super) show_warnings_key: Option<ShortcutHint>,
|
||||
pub(super) warning_notice_area: std::cell::Cell<Option<Rect>>,
|
||||
pub(super) find_transcript_key: Option<ShortcutHint>,
|
||||
pub(super) focus_activity_key: Option<ShortcutHint>,
|
||||
pub(super) insert_newline_key: Option<ShortcutHint>,
|
||||
pub(super) queue_key: Option<ShortcutHint>,
|
||||
pub(super) toggle_shortcuts_key: Option<ShortcutHint>,
|
||||
|
||||
+12
-11
@@ -1,6 +1,5 @@
|
||||
---
|
||||
source: tui/src/bottom_pane/chat_composer/sparkle_tests.rs
|
||||
assertion_line: 452
|
||||
expression: "snapshots.join(\"\\n\\n\")"
|
||||
---
|
||||
expires during help: false
|
||||
@@ -16,11 +15,12 @@ shortcut help:
|
||||
/ Commands tab Send message
|
||||
@ Mention files ⌥+, Less reasoning
|
||||
! Shell command ⌥+. More reasoning
|
||||
ctrl+j New line ctrl+c Quit
|
||||
ctrl+v Paste image
|
||||
ctrl+g External editor Transcript (open first)
|
||||
ctrl+r Search history ctrl+t Open transcript
|
||||
esc esc Edit last message f3 Find text
|
||||
ctrl+j New line f4 Inspect activity
|
||||
ctrl+v Paste image ctrl+c Quit
|
||||
ctrl+g External editor
|
||||
ctrl+r Search history Transcript (open first)
|
||||
esc esc Edit last message ctrl+t Open transcript
|
||||
f3 Find text
|
||||
pgup / pgdn Scroll
|
||||
ctrl+space Start selection
|
||||
ctrl+home / ctrl+end Top / latest
|
||||
@@ -50,11 +50,12 @@ shortcut help:
|
||||
/ Commands tab Send message
|
||||
@ Mention files ⌥+, Less reasoning
|
||||
! Shell command ⌥+. More reasoning
|
||||
ctrl+j New line ctrl+c Quit
|
||||
ctrl+v Paste image
|
||||
ctrl+g External editor Transcript (open first)
|
||||
ctrl+r Search history ctrl+t Open transcript
|
||||
esc esc Edit last message f3 Find text
|
||||
ctrl+j New line f4 Inspect activity
|
||||
ctrl+v Paste image ctrl+c Quit
|
||||
ctrl+g External editor
|
||||
ctrl+r Search history Transcript (open first)
|
||||
esc esc Edit last message ctrl+t Open transcript
|
||||
f3 Find text
|
||||
pgup / pgdn Scroll
|
||||
ctrl+space Start selection
|
||||
ctrl+home / ctrl+end Top / latest
|
||||
|
||||
+6
-6
@@ -1,6 +1,5 @@
|
||||
---
|
||||
source: tui/src/bottom_pane/chat_composer/sparkle_tests.rs
|
||||
assertion_line: 365
|
||||
expression: "snapshots.join(\"\\n\\n\")"
|
||||
---
|
||||
shortcut help:
|
||||
@@ -10,11 +9,12 @@ shortcut help:
|
||||
/ Commands tab Send message
|
||||
@ Mention files ⌥+, Less reasoning
|
||||
! Shell command ⌥+. More reasoning
|
||||
ctrl+j New line ctrl+c Quit
|
||||
ctrl+v Paste image
|
||||
ctrl+g External editor Transcript (open first)
|
||||
ctrl+r Search history ctrl+t Open transcript
|
||||
esc esc Edit last message f3 Find text
|
||||
ctrl+j New line f4 Inspect activity
|
||||
ctrl+v Paste image ctrl+c Quit
|
||||
ctrl+g External editor
|
||||
ctrl+r Search history Transcript (open first)
|
||||
esc esc Edit last message ctrl+t Open transcript
|
||||
f3 Find text
|
||||
pgup / pgdn Scroll
|
||||
ctrl+space Start selection
|
||||
ctrl+home / ctrl+end Top / latest
|
||||
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
---
|
||||
source: tui/src/bottom_pane/chat_composer/status_surface_tests.rs
|
||||
expression: "snapshots.join(\"\\n---\\n\")"
|
||||
---
|
||||
"f4"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Keyboard shortcuts
|
||||
|
||||
Compose Session Transcript (open first)
|
||||
/ Commands tab Send message ctrl+t Open transcript
|
||||
@ Mention files ⌥+, Less reasoning f3 Find text
|
||||
! Shell command ⌥+. More reasoning pgup / pgdn Scroll
|
||||
ctrl+j New line f4 Inspect activity ctrl+space Start selection
|
||||
ctrl+v Paste image ctrl+c Quit ctrl+home / ctrl+end Top / latest
|
||||
ctrl+g External editor shift+⌥+, / shift+⌥+. Top / latest
|
||||
ctrl+r Search history
|
||||
esc esc Edit last message
|
||||
|
||||
/keymap customize
|
||||
|
||||
› Ask Codex
|
||||
|
||||
MODEL · ~/project · Context 20% used
|
||||
? / esc close
|
||||
---
|
||||
"ctrl-x t"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Keyboard shortcuts
|
||||
|
||||
Compose Session Transcript (open first)
|
||||
/ Commands tab Send message ctrl+t Open transcript
|
||||
@ Mention files ⌥+, Less reasoning f3 Find text
|
||||
! Shell command ⌥+. More reasoning pgup / pgdn Scroll
|
||||
ctrl+j New line ctrl+x t Inspect activity ctrl+space Start selection
|
||||
ctrl+v Paste image ctrl+c Quit ctrl+home / ctrl+end Top / latest
|
||||
ctrl+g External editor shift+⌥+, / shift+⌥+. Top / latest
|
||||
ctrl+r Search history
|
||||
esc esc Edit last message
|
||||
|
||||
/keymap customize
|
||||
|
||||
› Ask Codex
|
||||
|
||||
MODEL · ~/project · Context 20% used
|
||||
? / esc close
|
||||
---
|
||||
[]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Keyboard shortcuts
|
||||
|
||||
Compose Session Transcript (open first)
|
||||
/ Commands tab Send message ctrl+t Open transcript
|
||||
@ Mention files ⌥+, Less reasoning f3 Find text
|
||||
! Shell command ⌥+. More reasoning pgup / pgdn Scroll
|
||||
ctrl+j New line ctrl+c Quit ctrl+space Start selection
|
||||
ctrl+v Paste image ctrl+home / ctrl+end Top / latest
|
||||
ctrl+g External editor shift+⌥+, / shift+⌥+. Top / latest
|
||||
ctrl+r Search history
|
||||
esc esc Edit last message
|
||||
|
||||
/keymap customize
|
||||
|
||||
› Ask Codex
|
||||
|
||||
MODEL · ~/project · Context 20% used
|
||||
? / esc close
|
||||
+1
@@ -26,6 +26,7 @@ expression: text
|
||||
tab Send message
|
||||
⌥+, Less reasoning
|
||||
⌥+. More reasoning
|
||||
f4 Inspect activity
|
||||
ctrl+c Quit
|
||||
|
||||
Transcript (open first)
|
||||
|
||||
+7
-7
@@ -12,13 +12,13 @@ expression: text
|
||||
|
||||
Keyboard shortcuts
|
||||
|
||||
Compose Session Transcript (open first)
|
||||
/ Commands tab Send message ctrl+t Open transcript
|
||||
@ Mention files ⌥+, Less reasoning f3 Find text
|
||||
! Shell command ⌥+. More reasoning pgup / pgdn Scroll
|
||||
ctrl+j New line ctrl+c Quit ctrl+space Start selection
|
||||
ctrl+v Paste image ctrl+home / ctrl+end Top / latest
|
||||
ctrl+g External editor shift+⌥+, / shift+⌥+. Top / latest
|
||||
Compose Session Transcript (open first)
|
||||
/ Commands tab Send message ctrl+t Open transcript
|
||||
@ Mention files ⌥+, Less reasoning f3 Find text
|
||||
! Shell command ⌥+. More reasoning pgup / pgdn Scroll
|
||||
ctrl+j New line f4 Inspect activity ctrl+space Start selection
|
||||
ctrl+v Paste image ctrl+c Quit ctrl+home / ctrl+end Top / latest
|
||||
ctrl+g External editor shift+⌥+, / shift+⌥+. Top / latest
|
||||
ctrl+r Search history
|
||||
esc esc Edit last message
|
||||
|
||||
|
||||
@@ -362,3 +362,25 @@ fn slash_popup_preserves_footer_and_selection_style() {
|
||||
.collect();
|
||||
assert!(footer_text.contains("transcript status"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn activity_shortcut_help_follows_runtime_binding() {
|
||||
let mut snapshots = Vec::new();
|
||||
for configured in [
|
||||
serde_json::json!("f4"),
|
||||
serde_json::json!("ctrl-x t"),
|
||||
serde_json::json!([]),
|
||||
] {
|
||||
let config = serde_json::from_value(serde_json::json!({
|
||||
"global": {"focus_activity": configured}
|
||||
}))
|
||||
.unwrap();
|
||||
let keymap = crate::keymap::RuntimeKeymap::from_config(&config).unwrap();
|
||||
let mut composer = composer();
|
||||
composer.set_keymap_bindings(&keymap);
|
||||
composer.handle_key_event(KeyEvent::new(KeyCode::Char('?'), KeyModifiers::NONE));
|
||||
let (text, _) = render(&composer, /*width*/ 130, /*footer*/ None);
|
||||
snapshots.push(format!("{configured}\n{text}"));
|
||||
}
|
||||
insta::assert_snapshot!(snapshots.join("\n---\n"));
|
||||
}
|
||||
|
||||
@@ -120,6 +120,7 @@ pub(crate) struct FooterKeyHints {
|
||||
pub(crate) edit_previous: Option<ShortcutHint>,
|
||||
pub(crate) show_transcript: Option<ShortcutHint>,
|
||||
pub(crate) find_transcript: Option<ShortcutHint>,
|
||||
pub(crate) focus_activity: Option<ShortcutHint>,
|
||||
pub(crate) history_search: Option<ShortcutHint>,
|
||||
pub(crate) reasoning_down: Option<ShortcutHint>,
|
||||
pub(crate) reasoning_up: Option<ShortcutHint>,
|
||||
@@ -138,6 +139,7 @@ impl FooterKeyHints {
|
||||
edit_previous: Some(key_hint::plain(KeyCode::Esc).into()),
|
||||
show_transcript: Some(key_hint::ctrl(KeyCode::Char('t')).into()),
|
||||
find_transcript: Some(key_hint::plain(KeyCode::F(3)).into()),
|
||||
focus_activity: Some(key_hint::plain(KeyCode::F(4)).into()),
|
||||
history_search: Some(key_hint::ctrl(KeyCode::Char('r')).into()),
|
||||
reasoning_down: Some(key_hint::alt(KeyCode::Char(',')).into()),
|
||||
reasoning_up: Some(key_hint::alt(KeyCode::Char('.')).into()),
|
||||
|
||||
@@ -75,6 +75,7 @@ pub(super) fn lines(props: &FooterProps, width: u16) -> Vec<Line<'static>> {
|
||||
session.push(hints.reasoning_up, "More reasoning");
|
||||
session.push(hints.toggle_voice, "Voice");
|
||||
session.push(hints.agents, "Agents (empty prompt)");
|
||||
session.push(hints.focus_activity, "Inspect activity");
|
||||
session.entries.push(Shortcut::new(
|
||||
key_hint::ctrl(KeyCode::Char('c')),
|
||||
if props.is_task_running {
|
||||
|
||||
+5
-5
@@ -1,6 +1,5 @@
|
||||
---
|
||||
source: tui/src/bottom_pane/chat_composer.rs
|
||||
assertion_line: 5294
|
||||
expression: terminal.backend()
|
||||
---
|
||||
" Keyboard shortcuts "
|
||||
@@ -10,10 +9,11 @@ expression: terminal.backend()
|
||||
" @ Mention files ⌥+, Less reasoning "
|
||||
" ! Shell command ⌥+. More reasoning "
|
||||
" ctrl+j New line ← Agents (empty prompt) "
|
||||
" ctrl+v Paste image ctrl+c Quit "
|
||||
" ctrl+g External editor "
|
||||
" ctrl+r Search history Transcript (open first) "
|
||||
" esc esc Edit last message ctrl+t Open transcript "
|
||||
" ctrl+v Paste image f4 Inspect activity "
|
||||
" ctrl+g External editor ctrl+c Quit "
|
||||
" ctrl+r Search history "
|
||||
" esc esc Edit last message Transcript (open first) "
|
||||
" ctrl+t Open transcript "
|
||||
" f3 Find text "
|
||||
" pgup / pgdn Scroll "
|
||||
" ctrl+space Start selection "
|
||||
|
||||
+6
-6
@@ -1,6 +1,5 @@
|
||||
---
|
||||
source: tui/src/bottom_pane/chat_composer.rs
|
||||
assertion_line: 5620
|
||||
expression: terminal.backend()
|
||||
---
|
||||
" Keyboard shortcuts "
|
||||
@@ -9,11 +8,12 @@ expression: terminal.backend()
|
||||
" / Commands tab Send message "
|
||||
" @ Mention files ⌥+, Less reasoning "
|
||||
" ! Shell command ⌥+. More reasoning "
|
||||
" ctrl+x enter New line ctrl+c Quit "
|
||||
" ctrl+v Paste image "
|
||||
" ctrl+g ctrl+g External editor Transcript (open first) "
|
||||
" ctrl+r Search history ctrl+t Open transcript "
|
||||
" esc esc Edit last message f3 Find text "
|
||||
" ctrl+x enter New line f4 Inspect activity "
|
||||
" ctrl+v Paste image ctrl+c Quit "
|
||||
" ctrl+g ctrl+g External editor "
|
||||
" ctrl+r Search history Transcript (open first) "
|
||||
" esc esc Edit last message ctrl+t Open transcript "
|
||||
" f3 Find text "
|
||||
" pgup / pgdn Scroll "
|
||||
" ctrl+space Start selection "
|
||||
" ctrl+home / ctrl+end Top / latest "
|
||||
|
||||
+14
-10
@@ -1,19 +1,23 @@
|
||||
---
|
||||
source: tui/src/bottom_pane/chat_composer.rs
|
||||
assertion_line: 5294
|
||||
expression: terminal.backend()
|
||||
---
|
||||
" Keyboard shortcuts "
|
||||
" "
|
||||
" Compose Session Transcript (open first) "
|
||||
" / Commands tab Send message ctrl+t Open transcript"
|
||||
" @ Mention files ⌥+, Less reasoning f3 Find text "
|
||||
" ! Shell command ⌥+. More reasoning pgup / pgdn Scroll "
|
||||
" shift+enter New line ctrl+c Quit ctrl+space Start selection"
|
||||
" ctrl+v Paste image ctrl+home / ctrl+end Top / latest "
|
||||
" ctrl+g External editor shift+⌥+, / shift+⌥+. Top / latest "
|
||||
" ctrl+r Search history "
|
||||
" esc Edit last message "
|
||||
" Compose Session "
|
||||
" / Commands tab Send message "
|
||||
" @ Mention files ⌥+, Less reasoning "
|
||||
" ! Shell command ⌥+. More reasoning "
|
||||
" shift+enter New line f4 Inspect activity "
|
||||
" ctrl+v Paste image ctrl+c Quit "
|
||||
" ctrl+g External editor "
|
||||
" ctrl+r Search history Transcript (open first) "
|
||||
" esc Edit last message ctrl+t Open transcript "
|
||||
" f3 Find text "
|
||||
" pgup / pgdn Scroll "
|
||||
" ctrl+space Start selection "
|
||||
" ctrl+home / ctrl+end Top / latest "
|
||||
" shift+⌥+, / shift+⌥+. Top / latest "
|
||||
" "
|
||||
" /keymap customize "
|
||||
" "
|
||||
|
||||
+14
-10
@@ -1,19 +1,23 @@
|
||||
---
|
||||
source: tui/src/bottom_pane/chat_composer.rs
|
||||
assertion_line: 5294
|
||||
expression: terminal.backend()
|
||||
---
|
||||
" Keyboard shortcuts "
|
||||
" "
|
||||
" Compose Session Transcript (open first) "
|
||||
" / Commands tab Queue message ctrl+t Open transcript"
|
||||
" @ Mention files ⌥+, Less reasoning f3 Find text "
|
||||
" ! Shell command ⌥+. More reasoning pgup / pgdn Scroll "
|
||||
" shift+enter New line ctrl+c Quit ctrl+space Start selection"
|
||||
" ctrl+v Paste image ctrl+home / ctrl+end Top / latest "
|
||||
" ctrl+g External editor shift+⌥+, / shift+⌥+. Top / latest "
|
||||
" ctrl+r Search history "
|
||||
" esc esc Edit last message "
|
||||
" Compose Session "
|
||||
" / Commands tab Queue message "
|
||||
" @ Mention files ⌥+, Less reasoning "
|
||||
" ! Shell command ⌥+. More reasoning "
|
||||
" shift+enter New line f4 Inspect activity "
|
||||
" ctrl+v Paste image ctrl+c Quit "
|
||||
" ctrl+g External editor "
|
||||
" ctrl+r Search history Transcript (open first) "
|
||||
" esc esc Edit last message ctrl+t Open transcript "
|
||||
" f3 Find text "
|
||||
" pgup / pgdn Scroll "
|
||||
" ctrl+space Start selection "
|
||||
" ctrl+home / ctrl+end Top / latest "
|
||||
" shift+⌥+, / shift+⌥+. Top / latest "
|
||||
" "
|
||||
" /keymap customize "
|
||||
" "
|
||||
|
||||
+4
-4
@@ -1,6 +1,5 @@
|
||||
---
|
||||
source: tui/src/bottom_pane/footer.rs
|
||||
assertion_line: 1240
|
||||
expression: terminal.backend()
|
||||
---
|
||||
" Keyboard shortcuts "
|
||||
@@ -11,9 +10,10 @@ expression: terminal.backend()
|
||||
" ! Shell command ⌥+, Less reasoning "
|
||||
" ctrl+j New line ⌥+. More reasoning "
|
||||
" ctrl+v Paste image f8 Voice "
|
||||
" ctrl+g External editor ctrl+c Quit "
|
||||
" ctrl+r Search history "
|
||||
" esc esc Edit last message Transcript (open first) "
|
||||
" ctrl+g External editor f4 Inspect activity "
|
||||
" ctrl+r Search history ctrl+c Quit "
|
||||
" esc esc Edit last message "
|
||||
" Transcript (open first) "
|
||||
" ctrl+t Open transcript "
|
||||
" f3 Find text "
|
||||
" pgup / pgdn Scroll "
|
||||
|
||||
+5
-5
@@ -1,6 +1,5 @@
|
||||
---
|
||||
source: tui/src/bottom_pane/footer.rs
|
||||
assertion_line: 1240
|
||||
expression: terminal.backend()
|
||||
---
|
||||
" Keyboard shortcuts "
|
||||
@@ -10,10 +9,11 @@ expression: terminal.backend()
|
||||
" @ Mention files ⌥+, Less reasoning "
|
||||
" ! Shell command ⌥+. More reasoning "
|
||||
" ctrl+j New line f8 Voice "
|
||||
" ctrl+v Paste image ctrl+c Interrupt "
|
||||
" ctrl+g External editor "
|
||||
" ctrl+r Search history Transcript (open first) "
|
||||
" esc esc Edit last message ctrl+t Open transcript "
|
||||
" ctrl+v Paste image f4 Inspect activity "
|
||||
" ctrl+g External editor ctrl+c Interrupt "
|
||||
" ctrl+r Search history "
|
||||
" esc esc Edit last message Transcript (open first) "
|
||||
" ctrl+t Open transcript "
|
||||
" f3 Find text "
|
||||
" pgup / pgdn Scroll "
|
||||
" ctrl+space Start selection "
|
||||
|
||||
+5
-5
@@ -1,6 +1,5 @@
|
||||
---
|
||||
source: tui/src/bottom_pane/footer.rs
|
||||
assertion_line: 1240
|
||||
expression: terminal.backend()
|
||||
---
|
||||
" Keyboard shortcuts "
|
||||
@@ -10,10 +9,11 @@ expression: terminal.backend()
|
||||
" @ Mention files ⌥+, Less reasoning "
|
||||
" ! Shell command ⌥+. More reasoning "
|
||||
" shift+enter New line f8 Voice "
|
||||
" ctrl+v Paste image ctrl+c Quit "
|
||||
" ctrl+g External editor "
|
||||
" ctrl+r Search history Transcript (open first) "
|
||||
" esc Edit last message ctrl+t Open transcript "
|
||||
" ctrl+v Paste image f4 Inspect activity "
|
||||
" ctrl+g External editor ctrl+c Quit "
|
||||
" ctrl+r Search history "
|
||||
" esc Edit last message Transcript (open first) "
|
||||
" ctrl+t Open transcript "
|
||||
" f3 Find text "
|
||||
" pgup / pgdn Scroll "
|
||||
" ctrl+space Start selection "
|
||||
|
||||
+3
-2
@@ -10,8 +10,9 @@ expression: terminal.backend()
|
||||
" ! Shell command ⌥+. More reasoning "
|
||||
" shift+enter New line f8 Voice "
|
||||
" ctrl+⌥+v Paste image ← Agents (empty prompt) "
|
||||
" ctrl+x ctrl+e External editor ctrl+c Quit "
|
||||
" esc Edit last message "
|
||||
" ctrl+x ctrl+e External editor f4 Inspect activity "
|
||||
" esc Edit last message ctrl+c Quit "
|
||||
" "
|
||||
" Transcript (open first) "
|
||||
" ctrl+t Open transcript "
|
||||
" f3 Find text "
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
//! Local activity disclosure keyed by retained tool identities, independent of text selection.
|
||||
//! Controls follow the activity preview; copied text and full-transcript search remain source based.
|
||||
//! Controls appear only while activity is focused; copied text and full-transcript search remain source based.
|
||||
|
||||
use crate::style::accent_color;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::bottom_pane::TranscriptFooter;
|
||||
use crate::key_hint::KeyBindingListExt;
|
||||
use crate::key_hint::key_label_spans;
|
||||
use crate::keymap::KeymapContext;
|
||||
use crate::keymap::ListAction;
|
||||
use crate::keymap::RuntimeKeymap;
|
||||
use crossterm::event::KeyEvent;
|
||||
@@ -362,74 +360,43 @@ impl TranscriptView {
|
||||
}
|
||||
|
||||
pub(super) fn disclosure_footer(&self, width: u16) -> Option<TranscriptFooter> {
|
||||
if self.detailed || self.mode != HistoryRenderMode::Rich {
|
||||
if !self.is_activity_focused() {
|
||||
return None;
|
||||
}
|
||||
let keymap = &self.disclosure.keymap;
|
||||
let text = if self.is_activity_focused() {
|
||||
let hints: Vec<_> = [
|
||||
(ListAction::MoveUp, "previous"),
|
||||
(ListAction::MoveDown, "next"),
|
||||
(ListAction::Accept, "details"),
|
||||
(ListAction::Cancel, "back"),
|
||||
]
|
||||
.into_iter()
|
||||
.filter_map(|(action, label)| {
|
||||
keymap.list.primary_hint(action).map(|key| {
|
||||
let mut spans = key.spans();
|
||||
spans.push(format!(" {label}").dim());
|
||||
Line::from(spans)
|
||||
})
|
||||
let hints: Vec<_> = [
|
||||
(ListAction::MoveUp, "previous"),
|
||||
(ListAction::MoveDown, "next"),
|
||||
(ListAction::Accept, "details"),
|
||||
(ListAction::Cancel, "back"),
|
||||
]
|
||||
.into_iter()
|
||||
.filter_map(|(action, label)| {
|
||||
keymap.list.primary_hint(action).map(|key| {
|
||||
let mut spans = key.spans();
|
||||
spans.push(format!(" {label}").dim());
|
||||
Line::from(spans)
|
||||
})
|
||||
.collect();
|
||||
// Drop navigation before the details/back controls, retaining whole remapped chords.
|
||||
crate::footer_hint::first_fitting_line(
|
||||
(0..hints.len()).map(|start| {
|
||||
let mut spans = Vec::new();
|
||||
for (index, hint) in hints[start..].iter().enumerate() {
|
||||
if index > 0 {
|
||||
spans.push(" · ".dim());
|
||||
}
|
||||
spans.extend(hint.spans.clone());
|
||||
})
|
||||
.collect();
|
||||
// Drop navigation before the details/back controls, retaining whole remapped chords.
|
||||
let text = crate::footer_hint::first_fitting_line(
|
||||
(0..hints.len()).map(|start| {
|
||||
let mut spans = Vec::new();
|
||||
for (index, hint) in hints[start..].iter().enumerate() {
|
||||
if index > 0 {
|
||||
spans.push(" · ".dim());
|
||||
}
|
||||
Line::from(spans)
|
||||
}),
|
||||
width,
|
||||
)
|
||||
} else {
|
||||
if !self.visible.iter().any(|row| row.layout.disclosure) {
|
||||
return None;
|
||||
}
|
||||
let key = keymap.primary_hint(KeymapContext::Global, "focus_activity")?;
|
||||
let primary = key.display_label();
|
||||
let alternatives = std::iter::once(primary.clone())
|
||||
.chain(
|
||||
crate::keymap::user_bindings(&keymap.app.focus_activity)
|
||||
.iter()
|
||||
.map(crate::key_hint::KeyBinding::display_label)
|
||||
.filter(|label| *label != primary),
|
||||
)
|
||||
.take(/*n*/ 2)
|
||||
.collect::<Vec<_>>()
|
||||
.join("/");
|
||||
crate::footer_hint::first_fitting_line(
|
||||
[
|
||||
(alternatives.as_str(), " inspect activity"),
|
||||
(alternatives.as_str(), " activity"),
|
||||
(primary.as_str(), " activity"),
|
||||
]
|
||||
.map(|(keys, action)| {
|
||||
let mut spans = key_label_spans(keys);
|
||||
spans.push(action.dim());
|
||||
Line::from(spans)
|
||||
}),
|
||||
width,
|
||||
)
|
||||
};
|
||||
spans.extend(hint.spans.clone());
|
||||
}
|
||||
Line::from(spans)
|
||||
}),
|
||||
width,
|
||||
);
|
||||
Some(TranscriptFooter {
|
||||
text: text.into(),
|
||||
cursor_column: None,
|
||||
is_interactive: self.is_activity_focused(),
|
||||
is_interactive: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,67 +63,65 @@ impl TranscriptView {
|
||||
}
|
||||
let pending = self.is_loading_history() || self.history == TranscriptHistoryState::Failed;
|
||||
let can_return = self.selection.is_none() && self.can_return_to_latest();
|
||||
(pending || self.unseen_activity || can_return)
|
||||
.then(|| {
|
||||
let navigation = if self.can_return_to_latest() {
|
||||
latest_navigation
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let mut status = self.status_line_with_navigation(navigation, motion);
|
||||
// Loading must not resize the composer or move its caret. Shorten secondary text
|
||||
// before clipping either shortcut; failed loads prioritize their retry action.
|
||||
if status.width() > usize::from(width) {
|
||||
status = if self.is_loading_history() {
|
||||
let loading = if navigation.is_empty() {
|
||||
if "↑ Loading…".width() <= usize::from(width) {
|
||||
"Loading…"
|
||||
} else {
|
||||
"…"
|
||||
}
|
||||
} else if "↑ Loading… · ".width() + navigation.width() <= usize::from(width)
|
||||
{
|
||||
"Loading… · "
|
||||
(pending || self.unseen_activity || can_return).then(|| {
|
||||
let navigation = if self.can_return_to_latest() {
|
||||
latest_navigation
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let mut status = self.status_line_with_navigation(navigation, motion);
|
||||
// Loading must not resize the composer or move its caret. Shorten secondary text
|
||||
// before clipping either shortcut; failed loads prioritize their retry action.
|
||||
if status.width() > usize::from(width) {
|
||||
status = if self.is_loading_history() {
|
||||
let loading = if navigation.is_empty() {
|
||||
if "↑ Loading…".width() <= usize::from(width) {
|
||||
"Loading…"
|
||||
} else {
|
||||
"… "
|
||||
};
|
||||
let mut spans = vec!["↑ ".fg(accent_color())];
|
||||
spans.extend(shimmer_text(loading, motion));
|
||||
spans.extend(navigation_line(navigation).spans);
|
||||
Line::from(spans)
|
||||
} else if self.history == TranscriptHistoryState::Failed {
|
||||
let mut spans = vec!["Retry: ".dim()];
|
||||
spans.extend(key_label_spans(&JumpTarget::Beginning.hint_label()));
|
||||
Line::from(spans)
|
||||
} else if navigation.is_empty() {
|
||||
Line::from("New activity").dim()
|
||||
} else if self.unseen_activity {
|
||||
let mut spans = vec!["New · ".dim()];
|
||||
spans.extend(navigation_line(navigation).spans);
|
||||
Line::from(spans)
|
||||
"…"
|
||||
}
|
||||
} else if "↑ Loading… · ".width() + navigation.width() <= usize::from(width)
|
||||
{
|
||||
"Loading… · "
|
||||
} else {
|
||||
navigation_line(navigation)
|
||||
"… "
|
||||
};
|
||||
}
|
||||
let retry = if self.history == TranscriptHistoryState::Failed {
|
||||
format!(
|
||||
"{} retry",
|
||||
crate::key_hint::ctrl(KeyCode::Home).display_label()
|
||||
)
|
||||
let mut spans = vec!["↑ ".fg(accent_color())];
|
||||
spans.extend(shimmer_text(loading, motion));
|
||||
spans.extend(navigation_line(navigation).spans);
|
||||
Line::from(spans)
|
||||
} else if self.history == TranscriptHistoryState::Failed {
|
||||
let mut spans = vec!["Retry: ".dim()];
|
||||
spans.extend(key_label_spans(&JumpTarget::Beginning.hint_label()));
|
||||
Line::from(spans)
|
||||
} else if navigation.is_empty() {
|
||||
Line::from("New activity").dim()
|
||||
} else if self.unseen_activity {
|
||||
let mut spans = vec!["New · ".dim()];
|
||||
spans.extend(navigation_line(navigation).spans);
|
||||
Line::from(spans)
|
||||
} else {
|
||||
navigation.to_owned()
|
||||
navigation_line(navigation)
|
||||
};
|
||||
TranscriptFooter {
|
||||
text: first_fitting_line(
|
||||
[status, navigation_line(&retry), navigation_line(navigation)],
|
||||
width,
|
||||
)
|
||||
.into(),
|
||||
cursor_column: None,
|
||||
is_interactive: false,
|
||||
}
|
||||
})
|
||||
.or_else(|| self.disclosure_footer(width))
|
||||
}
|
||||
let retry = if self.history == TranscriptHistoryState::Failed {
|
||||
format!(
|
||||
"{} retry",
|
||||
crate::key_hint::ctrl(KeyCode::Home).display_label()
|
||||
)
|
||||
} else {
|
||||
navigation.to_owned()
|
||||
};
|
||||
TranscriptFooter {
|
||||
text: first_fitting_line(
|
||||
[status, navigation_line(&retry), navigation_line(navigation)],
|
||||
width,
|
||||
)
|
||||
.into(),
|
||||
cursor_column: None,
|
||||
is_interactive: false,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn status_line_with_navigation(
|
||||
|
||||
@@ -290,7 +290,7 @@ impl HistoryCell for ActivityCell {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn activity_alternative_focuses_details_and_hints_follow_configured_chords() {
|
||||
fn activity_focus_keeps_controls_without_passive_hints() {
|
||||
let mut cells: Vec<Arc<dyn HistoryCell>> = Vec::new();
|
||||
let cell = Arc::new(ActivityCell::default());
|
||||
let mut view = TranscriptView::default();
|
||||
@@ -314,12 +314,20 @@ fn activity_alternative_focuses_details_and_hints_follow_configured_chords() {
|
||||
for width in [20, 32, 80] {
|
||||
hints.push(format!(
|
||||
"{width} columns: {}",
|
||||
view.footer(width, MotionMode::Reduced).unwrap().text,
|
||||
view.footer(width, MotionMode::Reduced).map_or_else(
|
||||
|| "<composer hints>".to_owned(),
|
||||
|footer| footer.text.to_string()
|
||||
),
|
||||
));
|
||||
}
|
||||
view.handle_key(KeyEvent::new(KeyCode::F(4), KeyModifiers::NONE), &cells);
|
||||
assert!(view.is_activity_focused());
|
||||
hints.push(format!("focused\n{:?}", render_live(&mut view)));
|
||||
for width in [20, 32, 80] {
|
||||
let footer = view.footer(width, MotionMode::Reduced).unwrap();
|
||||
assert!(footer.is_interactive);
|
||||
hints.push(format!("focused, {width} columns: {}", footer.text));
|
||||
}
|
||||
view.handle_key(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE), &cells);
|
||||
assert!(view.disclosure.is_expanded(&["sample-activity".to_owned()]));
|
||||
let expanded = render_live(&mut view);
|
||||
@@ -352,9 +360,10 @@ fn activity_alternative_focuses_details_and_hints_follow_configured_chords() {
|
||||
view.set_keymap_bindings(&keymap);
|
||||
view.jump_to_latest();
|
||||
render(&mut view, &cells);
|
||||
let hint = view
|
||||
.footer(/*width*/ 80, MotionMode::Reduced)
|
||||
.map_or_else(|| "unbound".to_owned(), |footer| footer.text.to_string());
|
||||
let hint = view.footer(/*width*/ 80, MotionMode::Reduced).map_or_else(
|
||||
|| "<composer hints>".to_owned(),
|
||||
|footer| footer.text.to_string(),
|
||||
);
|
||||
hints.push(format!("{configured}: {hint}"));
|
||||
}
|
||||
insta::assert_snapshot!(hints.join("\n"));
|
||||
|
||||
+9
-6
@@ -16,9 +16,9 @@ Buffer {
|
||||
x: 0, y: 2, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
|
||||
]
|
||||
}
|
||||
20 columns: f4 inspect activity
|
||||
32 columns: f4 inspect activity
|
||||
80 columns: f4 inspect activity
|
||||
20 columns: <composer hints>
|
||||
32 columns: <composer hints>
|
||||
80 columns: <composer hints>
|
||||
focused
|
||||
Buffer {
|
||||
area: Rect { x: 0, y: 0, width: 32, height: 3 },
|
||||
@@ -35,6 +35,9 @@ Buffer {
|
||||
x: 0, y: 2, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
|
||||
]
|
||||
}
|
||||
focused, 20 columns: esc back
|
||||
focused, 32 columns: enter details · esc back
|
||||
focused, 80 columns: ↑ previous · ↓ next · enter details · esc back
|
||||
expanded
|
||||
Buffer {
|
||||
area: Rect { x: 0, y: 0, width: 32, height: 3 },
|
||||
@@ -51,6 +54,6 @@ Buffer {
|
||||
x: 15, y: 2, fg: Reset, bg: Reset, underline: Reset, modifier: DIM,
|
||||
]
|
||||
}
|
||||
"ctrl-x t": ctrl+x t inspect activity
|
||||
["ctrl-x t","f4"]: ctrl+x t/f4 inspect activity
|
||||
[]: unbound
|
||||
"ctrl-x t": <composer hints>
|
||||
["ctrl-x t","f4"]: <composer hints>
|
||||
[]: <composer hints>
|
||||
Reference in New Issue
Block a user