Show the voice toggle in the TUI shortcut overlay (#47082)

## What changed

Add a Voice entry using the configured `toggle_voice` binding (`F8` by default). Show it only when voice commands are enabled and no side conversation is active.

## Testing

Update footer and shortcut overlay snapshots to include the Voice entry.

GitOrigin-RevId: ba92f5af1ce1a06d9700d31dcc90e5acb1613803
This commit is contained in:
Eric Traut
2026-09-21 18:19:34 +00:00
committed by copyberry
parent ac98537678
commit 2fe4a2b4d8
9 changed files with 85 additions and 63 deletions
+6 -44
View File
@@ -761,6 +761,7 @@ impl ChatComposer {
.primary_hint(KeymapContext::Chat, "decrease_reasoning_effort"),
reasoning_up_key: default_keymap
.primary_hint(KeymapContext::Chat, "increase_reasoning_effort"),
toggle_voice_key: default_keymap.primary_hint(KeymapContext::Chat, "toggle_voice"),
},
has_focus: has_input_focus,
frame_requester: None,
@@ -1048,6 +1049,7 @@ impl ChatComposer {
keymap.primary_hint(KeymapContext::Chat, "decrease_reasoning_effort");
self.footer.reasoning_up_key =
keymap.primary_hint(KeymapContext::Chat, "increase_reasoning_effort");
self.footer.toggle_voice_key = keymap.primary_hint(KeymapContext::Chat, "toggle_voice");
}
/// Return the contexts whose handlers can consume the next composer key.
@@ -3865,6 +3867,10 @@ impl ChatComposer {
history_search: self.footer.history_search_key,
reasoning_down: self.footer.reasoning_down_key,
reasoning_up: self.footer.reasoning_up_key,
toggle_voice: self
.footer
.toggle_voice_key
.filter(|_| self.voice_command_enabled && !self.side_conversation_active),
},
active_agent_label: self.footer.active_agent_label.clone(),
}
@@ -5088,50 +5094,6 @@ mod tests {
)
}
#[test]
fn shortcut_footer_displays_configured_chords() {
use codex_config::types::KeybindingSpec;
use codex_config::types::KeybindingsSpec;
use codex_config::types::TuiKeymap;
let mut config = TuiKeymap::default();
config.global.open_external_editor = Some(KeybindingsSpec::One(KeybindingSpec(
"ctrl-g ctrl-g".to_string(),
)));
config.editor.insert_newline = Some(KeybindingsSpec::One(KeybindingSpec(
"ctrl-x enter".to_string(),
)));
let keymap = RuntimeKeymap::from_config(&config).expect("valid composer chords");
let (mut composer, _rx) = new_test_composer();
composer.set_keymap_bindings(&keymap);
composer.footer.mode = FooterMode::ShortcutOverlay;
let hints = composer.footer_props().key_hints;
assert_eq!(
hints.external_editor,
Some(ShortcutHint::Chord {
prefix: key_hint::ctrl(KeyCode::Char('g')),
completion: key_hint::ctrl(KeyCode::Char('g')),
})
);
assert_eq!(
hints.insert_newline,
Some(ShortcutHint::Chord {
prefix: key_hint::ctrl(KeyCode::Char('x')),
completion: key_hint::plain(KeyCode::Enter),
})
);
snapshot_composer_state(
"footer_mode_configured_key_chords",
/*enhanced_keys_supported*/ false,
|composer| {
composer.set_keymap_bindings(&keymap);
composer.footer.mode = FooterMode::ShortcutOverlay;
},
);
}
#[test]
fn parent_owned_thread_placeholder_snapshot() {
snapshot_composer_state(
@@ -226,6 +226,7 @@ pub(super) struct FooterState {
pub(super) history_search_key: Option<ShortcutHint>,
pub(super) reasoning_down_key: Option<ShortcutHint>,
pub(super) reasoning_up_key: Option<ShortcutHint>,
pub(super) toggle_voice_key: Option<ShortcutHint>,
}
#[derive(Clone, Debug)]
@@ -1,10 +1,62 @@
//! Snapshot coverage for draft text and live voice controls in the composer.
//! Snapshot coverage for draft text, shortcuts, and live voice controls in the composer.
use super::tests::new_test_composer;
use super::tests::snapshot_composer_state_with_width;
use crate::bottom_pane::footer::FooterMode;
use crate::key_hint;
use crate::key_hint::ShortcutHint;
use crate::keymap::RuntimeKeymap;
use crate::render::renderable::Renderable;
use crossterm::event::KeyCode;
use pretty_assertions::assert_eq;
use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
#[test]
fn shortcut_footer_displays_configured_chords() {
use codex_config::types::KeybindingSpec;
use codex_config::types::KeybindingsSpec;
use codex_config::types::TuiKeymap;
let mut config = TuiKeymap::default();
config.global.open_external_editor = Some(KeybindingsSpec::One(KeybindingSpec(
"ctrl-g ctrl-g".to_string(),
)));
config.editor.insert_newline = Some(KeybindingsSpec::One(KeybindingSpec(
"ctrl-x enter".to_string(),
)));
let keymap = RuntimeKeymap::from_config(&config).expect("valid composer chords");
let (mut composer, _rx) = new_test_composer();
composer.set_keymap_bindings(&keymap);
composer.footer.mode = FooterMode::ShortcutOverlay;
let hints = composer.footer_props().key_hints;
assert_eq!(
hints.external_editor,
Some(ShortcutHint::Chord {
prefix: key_hint::ctrl(KeyCode::Char('g')),
completion: key_hint::ctrl(KeyCode::Char('g')),
})
);
assert_eq!(
hints.insert_newline,
Some(ShortcutHint::Chord {
prefix: key_hint::ctrl(KeyCode::Char('x')),
completion: key_hint::plain(KeyCode::Enter),
})
);
snapshot_composer_state_with_width(
"footer_mode_configured_key_chords",
/*width*/ 100,
/*enhanced_keys_supported*/ false,
|composer| {
composer.set_keymap_bindings(&keymap);
composer.footer.mode = FooterMode::ShortcutOverlay;
},
);
}
#[test]
fn draft_and_voice_composer_snapshots() {
use crate::bottom_pane::voice_strip::VoiceStripPhase;
+2
View File
@@ -123,6 +123,7 @@ pub(crate) struct FooterKeyHints {
pub(crate) history_search: Option<ShortcutHint>,
pub(crate) reasoning_down: Option<ShortcutHint>,
pub(crate) reasoning_up: Option<ShortcutHint>,
pub(crate) toggle_voice: Option<ShortcutHint>,
}
impl FooterKeyHints {
@@ -140,6 +141,7 @@ impl FooterKeyHints {
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()),
toggle_voice: Some(key_hint::plain(KeyCode::F(8)).into()),
}
}
}
@@ -73,6 +73,7 @@ pub(super) fn lines(props: &FooterProps, width: u16) -> Vec<Line<'static>> {
}
session.push(hints.reasoning_down, "Less reasoning");
session.push(hints.reasoning_up, "More reasoning");
session.push(hints.toggle_voice, "Voice");
session.push(hints.agents, "Agents (empty prompt)");
session.entries.push(Shortcut::new(
key_hint::ctrl(KeyCode::Char('c')),
@@ -10,10 +10,11 @@ expression: terminal.backend()
" @ Mention files shift+tab Change mode "
" ! Shell command ⌥+, Less reasoning "
" ctrl+j New line ⌥+. More reasoning "
" 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 f8 Voice "
" 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 "
@@ -9,11 +9,12 @@ expression: terminal.backend()
" / Commands tab Queue message "
" @ Mention files ⌥+, Less reasoning "
" ! Shell command ⌥+. More reasoning "
" ctrl+j New line ctrl+c Interrupt "
" 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 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 "
" f3 Find text "
" pgup / pgdn Scroll "
" ctrl+space Start selection "
" ctrl+home / ctrl+end Top / latest "
@@ -9,11 +9,12 @@ expression: terminal.backend()
" / Commands tab Send message "
" @ Mention files ⌥+, Less reasoning "
" ! Shell command ⌥+. More reasoning "
" shift+enter 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 Edit last message f3 Find text "
" 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 "
" f3 Find text "
" pgup / pgdn Scroll "
" ctrl+space Start selection "
" ctrl+home / ctrl+end Top / latest "
@@ -8,10 +8,11 @@ expression: terminal.backend()
" / Commands tab Send message "
" @ Mention files shift+tab Change mode "
" ! Shell command ⌥+. More reasoning "
" shift+enter New line ← Agents (empty prompt) "
" ctrl+⌥+v Paste image ctrl+c Quit "
" ctrl+x ctrl+e External editor "
" esc Edit last message Transcript (open first) "
" 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 "
" Transcript (open first) "
" ctrl+t Open transcript "
" f3 Find text "
" pgup / pgdn Scroll "