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:
@@ -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]
|
||||||
|
|||||||
@@ -11,4 +11,4 @@ expression: terminal.backend()
|
|||||||
"▌ "
|
"▌ "
|
||||||
"▌ "
|
"▌ "
|
||||||
"▌ "
|
"▌ "
|
||||||
" ⏎ send Ctrl+J newline Ctrl+T transcript Ctrl+C quit "
|
" ⏎ send ⌃J newline ⌃T transcript ⌃C quit "
|
||||||
|
|||||||
@@ -11,4 +11,4 @@ expression: terminal.backend()
|
|||||||
"▌ "
|
"▌ "
|
||||||
"▌ "
|
"▌ "
|
||||||
"▌ "
|
"▌ "
|
||||||
" ⏎ send Ctrl+J newline Ctrl+T transcript Ctrl+C quit "
|
" ⏎ send ⌃J newline ⌃T transcript ⌃C quit "
|
||||||
|
|||||||
@@ -11,4 +11,4 @@ expression: terminal.backend()
|
|||||||
"▌ "
|
"▌ "
|
||||||
"▌ "
|
"▌ "
|
||||||
"▌ "
|
"▌ "
|
||||||
" ⏎ send Ctrl+J newline Ctrl+T transcript Ctrl+C quit "
|
" ⏎ send ⌃J newline ⌃T transcript ⌃C quit "
|
||||||
|
|||||||
@@ -11,4 +11,4 @@ expression: terminal.backend()
|
|||||||
"▌ "
|
"▌ "
|
||||||
"▌ "
|
"▌ "
|
||||||
"▌ "
|
"▌ "
|
||||||
" ⏎ send Ctrl+J newline Ctrl+T transcript Ctrl+C quit "
|
" ⏎ send ⌃J newline ⌃T transcript ⌃C quit "
|
||||||
|
|||||||
@@ -11,4 +11,4 @@ expression: terminal.backend()
|
|||||||
"▌ "
|
"▌ "
|
||||||
"▌ "
|
"▌ "
|
||||||
"▌ "
|
"▌ "
|
||||||
" ⏎ send Ctrl+J newline Ctrl+T transcript Ctrl+C quit "
|
" ⏎ send ⌃J newline ⌃T transcript ⌃C quit "
|
||||||
|
|||||||
@@ -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 ⌃"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 "
|
||||||
" "
|
" "
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
49
codex-rs/tui/src/key_hint.rs
Normal file
49
codex-rs/tui/src/key_hint.rs
Normal 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())
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ expression: terminal.backend()
|
|||||||
" Working (0s • Esc to interrupt) "
|
" Working (0s • Esc to interrupt) "
|
||||||
" ↳ first "
|
" ↳ first "
|
||||||
" ↳ second "
|
" ↳ second "
|
||||||
" Alt+↑ edit "
|
" ⌥↑ edit "
|
||||||
" "
|
" "
|
||||||
" "
|
" "
|
||||||
" "
|
" "
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user