Use ⌥⇧⌃ glyphs for key hints on mac (#3143)

#### Summary
- render the edit queued message shortcut with the ⌥ modifier on macOS
builds
- add a helper for status indicator snapshot suffixes
- record macOS-specific snapshots for the status indicator widget
This commit is contained in:
Jeremy Rose
2025-09-04 10:55:50 -07:00
committed by GitHub
parent aa083b795d
commit 075e385969
14 changed files with 79 additions and 32 deletions

View File

@@ -11,7 +11,6 @@ use ratatui::layout::Rect;
use ratatui::style::Color; use ratatui::style::Color;
use ratatui::style::Modifier; use ratatui::style::Modifier;
use ratatui::style::Style; use ratatui::style::Style;
use ratatui::style::Styled;
use ratatui::style::Stylize; use ratatui::style::Stylize;
use ratatui::text::Line; use ratatui::text::Line;
use ratatui::text::Span; use ratatui::text::Span;
@@ -37,6 +36,7 @@ use crate::bottom_pane::textarea::TextArea;
use crate::bottom_pane::textarea::TextAreaState; use crate::bottom_pane::textarea::TextAreaState;
use crate::clipboard_paste::normalize_pasted_path; use crate::clipboard_paste::normalize_pasted_path;
use crate::clipboard_paste::pasted_image_format; use crate::clipboard_paste::pasted_image_format;
use crate::key_hint;
use codex_file_search::FileMatch; use codex_file_search::FileMatch;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
@@ -1259,35 +1259,35 @@ impl WidgetRef for ChatComposer {
} }
ActivePopup::None => { ActivePopup::None => {
let bottom_line_rect = popup_rect; let bottom_line_rect = popup_rect;
let key_hint_style = Style::default().fg(Color::Cyan); let mut hint: Vec<Span<'static>> = if self.ctrl_c_quit_hint {
let mut hint = if self.ctrl_c_quit_hint {
vec![ vec![
" ".into(), " ".into(),
"Ctrl+C again".set_style(key_hint_style), key_hint::ctrl('C'),
" again".into(),
" to quit".into(), " to quit".into(),
] ]
} else { } else {
let newline_hint_key = if self.use_shift_enter_hint { let newline_hint_key = if self.use_shift_enter_hint {
"Shift+⏎" key_hint::shift('⏎')
} else { } else {
"Ctrl+J" key_hint::ctrl('J')
}; };
vec![ vec![
" ".into(), " ".into(),
"".set_style(key_hint_style), key_hint::plain('⏎'),
" send ".into(), " send ".into(),
newline_hint_key.set_style(key_hint_style), newline_hint_key,
" newline ".into(), " newline ".into(),
"Ctrl+T".set_style(key_hint_style), key_hint::ctrl('T'),
" transcript ".into(), " transcript ".into(),
"Ctrl+C".set_style(key_hint_style), key_hint::ctrl('C'),
" quit".into(), " quit".into(),
] ]
}; };
if !self.ctrl_c_quit_hint && self.esc_backtrack_hint { if !self.ctrl_c_quit_hint && self.esc_backtrack_hint {
hint.push(" ".into()); hint.push(" ".into());
hint.push("Esc".set_style(key_hint_style)); hint.push(key_hint::plain("Esc"));
hint.push(" edit prev".into()); hint.push(" edit prev".into());
} }
@@ -1635,7 +1635,6 @@ mod tests {
use crossterm::event::KeyCode; use crossterm::event::KeyCode;
use crossterm::event::KeyEvent; use crossterm::event::KeyEvent;
use crossterm::event::KeyModifiers; use crossterm::event::KeyModifiers;
use insta::assert_snapshot;
use ratatui::Terminal; use ratatui::Terminal;
use ratatui::backend::TestBackend; use ratatui::backend::TestBackend;
@@ -1687,13 +1686,12 @@ mod tests {
.draw(|f| f.render_widget_ref(composer, f.area())) .draw(|f| f.render_widget_ref(composer, f.area()))
.unwrap_or_else(|e| panic!("Failed to draw {name} composer: {e}")); .unwrap_or_else(|e| panic!("Failed to draw {name} composer: {e}"));
assert_snapshot!(name, terminal.backend()); insta::assert_snapshot!(name, terminal.backend());
} }
} }
#[test] #[test]
fn slash_popup_model_first_for_mo_ui() { fn slash_popup_model_first_for_mo_ui() {
use insta::assert_snapshot;
use ratatui::Terminal; use ratatui::Terminal;
use ratatui::backend::TestBackend; use ratatui::backend::TestBackend;
@@ -1720,7 +1718,7 @@ mod tests {
.unwrap_or_else(|e| panic!("Failed to draw composer: {e}")); .unwrap_or_else(|e| panic!("Failed to draw composer: {e}"));
// Visual snapshot should show the slash popup with /model as the first entry. // Visual snapshot should show the slash popup with /model as the first entry.
assert_snapshot!("slash_popup_mo", terminal.backend()); insta::assert_snapshot!("slash_popup_mo", terminal.backend());
} }
#[test] #[test]

View File

@@ -11,4 +11,4 @@ expression: terminal.backend()
"▌ " "▌ "
"▌ " "▌ "
"▌ " "▌ "
" ⏎ send Ctrl+J newline Ctrl+T transcript Ctrl+C quit " " ⏎ send J newline T transcript C quit "

View File

@@ -11,4 +11,4 @@ expression: terminal.backend()
"▌ " "▌ "
"▌ " "▌ "
"▌ " "▌ "
" ⏎ send Ctrl+J newline Ctrl+T transcript Ctrl+C quit " " ⏎ send J newline T transcript C quit "

View File

@@ -11,4 +11,4 @@ expression: terminal.backend()
"▌ " "▌ "
"▌ " "▌ "
"▌ " "▌ "
" ⏎ send Ctrl+J newline Ctrl+T transcript Ctrl+C quit " " ⏎ send J newline T transcript C quit "

View File

@@ -11,4 +11,4 @@ expression: terminal.backend()
"▌ " "▌ "
"▌ " "▌ "
"▌ " "▌ "
" ⏎ send Ctrl+J newline Ctrl+T transcript Ctrl+C quit " " ⏎ send J newline T transcript C quit "

View File

@@ -11,4 +11,4 @@ expression: terminal.backend()
"▌ " "▌ "
"▌ " "▌ "
"▌ " "▌ "
" ⏎ send Ctrl+J newline Ctrl+T transcript Ctrl+C quit " " ⏎ send J newline T transcript C quit "

View File

@@ -3,4 +3,4 @@ source: tui/src/chatwidget/tests.rs
expression: terminal.backend() expression: terminal.backend()
--- ---
"▌ Ask Codex to do anything " "▌ Ask Codex to do anything "
" ⏎ send Ctrl+J newline Ctrl+T transc" " ⏎ send J newline T transcript ⌃"

View File

@@ -12,4 +12,4 @@ expression: visual
Investigating rendering code (0s • Esc to interrupt) Investigating rendering code (0s • Esc to interrupt)
▌Summarize recent commits ▌Summarize recent commits
⏎ send Ctrl+J newline Ctrl+T transcript Ctrl+C quit ⏎ send J newline T transcript C quit

View File

@@ -6,5 +6,5 @@ expression: terminal.backend()
" Analyzing (0s • Esc to interrupt) " " Analyzing (0s • Esc to interrupt) "
" " " "
"▌ Ask Codex to do anything " "▌ Ask Codex to do anything "
" ⏎ send Ctrl+J newline Ctrl+T transcript Ctrl+C quit " " ⏎ send J newline T transcript C quit "
" " " "

View File

@@ -1075,8 +1075,6 @@ fn apply_patch_manual_approval_adjusts_header() {
#[test] #[test]
fn apply_patch_manual_flow_snapshot() { fn apply_patch_manual_flow_snapshot() {
use insta::assert_snapshot;
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(); let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let mut proposed_changes = HashMap::new(); let mut proposed_changes = HashMap::new();
@@ -1647,5 +1645,5 @@ fn chatwidget_exec_and_status_layout_vt100_snapshot() {
} }
let visual = vt_lines.join("\n"); let visual = vt_lines.join("\n");
insta::assert_snapshot!(visual); assert_snapshot!(visual);
} }

View File

@@ -0,0 +1,49 @@
use ratatui::style::Color;
use ratatui::style::Style;
use ratatui::text::Span;
use std::fmt::Display;
#[cfg(test)]
const ALT_PREFIX: &str = "";
#[cfg(all(not(test), target_os = "macos"))]
const ALT_PREFIX: &str = "";
#[cfg(all(not(test), not(target_os = "macos")))]
const ALT_PREFIX: &str = "Alt+";
#[cfg(test)]
const CTRL_PREFIX: &str = "";
#[cfg(all(not(test), target_os = "macos"))]
const CTRL_PREFIX: &str = "";
#[cfg(all(not(test), not(target_os = "macos")))]
const CTRL_PREFIX: &str = "Ctrl+";
#[cfg(test)]
const SHIFT_PREFIX: &str = "";
#[cfg(all(not(test), target_os = "macos"))]
const SHIFT_PREFIX: &str = "";
#[cfg(all(not(test), not(target_os = "macos")))]
const SHIFT_PREFIX: &str = "Shift+";
fn key_hint_style() -> Style {
Style::default().fg(Color::Cyan)
}
fn modifier_span(prefix: &str, key: impl Display) -> Span<'static> {
Span::styled(format!("{prefix}{key}"), key_hint_style())
}
pub(crate) fn ctrl(key: impl Display) -> Span<'static> {
modifier_span(CTRL_PREFIX, key)
}
pub(crate) fn alt(key: impl Display) -> Span<'static> {
modifier_span(ALT_PREFIX, key)
}
pub(crate) fn shift(key: impl Display) -> Span<'static> {
modifier_span(SHIFT_PREFIX, key)
}
pub(crate) fn plain(key: impl Display) -> Span<'static> {
Span::styled(format!("{key}"), key_hint_style())
}

View File

@@ -42,6 +42,7 @@ mod file_search;
mod get_git_diff; mod get_git_diff;
mod history_cell; mod history_cell;
pub mod insert_history; pub mod insert_history;
mod key_hint;
pub mod live_wrap; pub mod live_wrap;
mod markdown; mod markdown;
mod markdown_stream; mod markdown_stream;

View File

@@ -5,7 +5,7 @@ expression: terminal.backend()
" Working (0s • Esc to interrupt) " " Working (0s • Esc to interrupt) "
" ↳ first " " ↳ first "
" ↳ second " " ↳ second "
" Alt+↑ edit " " ↑ edit "
" " " "
" " " "
" " " "

View File

@@ -14,6 +14,7 @@ use ratatui::widgets::WidgetRef;
use crate::app_event::AppEvent; use crate::app_event::AppEvent;
use crate::app_event_sender::AppEventSender; use crate::app_event_sender::AppEventSender;
use crate::key_hint;
use crate::shimmer::shimmer_spans; use crate::shimmer::shimmer_spans;
use crate::tui::FrameRequester; use crate::tui::FrameRequester;
use textwrap::Options as TwOptions; use textwrap::Options as TwOptions;
@@ -130,7 +131,8 @@ impl WidgetRef for StatusIndicatorWidget {
} }
} }
if !self.queued_messages.is_empty() { if !self.queued_messages.is_empty() {
lines.push(Line::from(vec![" ".into(), "Alt+↑".cyan(), " edit".into()]).dim()); let shortcut = key_hint::alt("");
lines.push(Line::from(vec![" ".into(), shortcut, " edit".into()]).dim());
} }
let paragraph = Paragraph::new(lines); let paragraph = Paragraph::new(lines);
@@ -143,7 +145,6 @@ mod tests {
use super::*; use super::*;
use crate::app_event::AppEvent; use crate::app_event::AppEvent;
use crate::app_event_sender::AppEventSender; use crate::app_event_sender::AppEventSender;
use insta::assert_snapshot;
use ratatui::Terminal; use ratatui::Terminal;
use ratatui::backend::TestBackend; use ratatui::backend::TestBackend;
use tokio::sync::mpsc::unbounded_channel; use tokio::sync::mpsc::unbounded_channel;
@@ -159,7 +160,7 @@ mod tests {
terminal terminal
.draw(|f| w.render_ref(f.area(), f.buffer_mut())) .draw(|f| w.render_ref(f.area(), f.buffer_mut()))
.expect("draw"); .expect("draw");
assert_snapshot!(terminal.backend()); insta::assert_snapshot!(terminal.backend());
} }
#[test] #[test]
@@ -173,7 +174,7 @@ mod tests {
terminal terminal
.draw(|f| w.render_ref(f.area(), f.buffer_mut())) .draw(|f| w.render_ref(f.area(), f.buffer_mut()))
.expect("draw"); .expect("draw");
assert_snapshot!(terminal.backend()); insta::assert_snapshot!(terminal.backend());
} }
#[test] #[test]
@@ -188,6 +189,6 @@ mod tests {
terminal terminal
.draw(|f| w.render_ref(f.area(), f.buffer_mut())) .draw(|f| w.render_ref(f.area(), f.buffer_mut()))
.expect("draw"); .expect("draw");
assert_snapshot!(terminal.backend()); insta::assert_snapshot!(terminal.backend());
} }
} }