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:
Felipe Coury
2026-09-22 18:51:23 +00:00
committed by copyberry
parent 49e95cc73f
commit 1b1b58f5a5
22 changed files with 307 additions and 201 deletions
@@ -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>,
@@ -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
@@ -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
@@ -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
@@ -26,6 +26,7 @@ expression: text
tab Send message
⌥+, Less reasoning
⌥+. More reasoning
f4 Inspect activity
ctrl+c Quit
Transcript (open first)
@@ -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"));
}
+2
View File
@@ -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 {
@@ -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 "
@@ -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 "
@@ -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 "
" "
@@ -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 "
" "
@@ -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 "
@@ -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 "
@@ -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 "
@@ -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 "
+30 -63
View File
@@ -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,
})
}
}
+54 -56
View File
@@ -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"));
@@ -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>