feat(tui): Display keyboard shortcuts inline for approval options (#5889)
Shows single-key shortcuts (y, a, n) next to approval options to make them more discoverable. Previously these shortcuts worked but were hidden, making the feature hard to discover. Changes: - "Yes, proceed" now shows "y" shortcut - "Yes, and don't ask again" now shows "a" shortcut - "No, and tell Codex..." continues to show "esc" shortcut This improves UX by surfacing the quick keyboard shortcuts that were already functional but undiscoverable in the UI. --- Update: added parentheses for better visual clarity <img width="1540" height="486" alt="CleanShot 2025-11-05 at 11 47 07@2x" src="https://github.com/user-attachments/assets/f951c34a-9ec8-4b81-b151-7b2ccba94658" /> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Eric Traut <etraut@openai.com>
This commit is contained in:
@@ -117,7 +117,9 @@ impl ApprovalOverlay {
|
||||
.iter()
|
||||
.map(|opt| SelectionItem {
|
||||
name: opt.label.clone(),
|
||||
display_shortcut: opt.display_shortcut,
|
||||
display_shortcut: opt
|
||||
.display_shortcut
|
||||
.or_else(|| opt.additional_shortcuts.first().copied()),
|
||||
dismiss_on_select: false,
|
||||
..Default::default()
|
||||
})
|
||||
|
||||
@@ -3,6 +3,7 @@ use ratatui::layout::Rect;
|
||||
// Note: Table-based layout previously used Constraint; the manual renderer
|
||||
// below no longer requires it.
|
||||
use ratatui::style::Color;
|
||||
use ratatui::style::Style;
|
||||
use ratatui::style::Stylize;
|
||||
use ratatui::text::Line;
|
||||
use ratatui::text::Span;
|
||||
@@ -96,8 +97,9 @@ fn build_full_line(row: &GenericDisplayRow, desc_col: usize) -> Line<'static> {
|
||||
let this_name_width = Line::from(name_spans.clone()).width();
|
||||
let mut full_spans: Vec<Span> = name_spans;
|
||||
if let Some(display_shortcut) = row.display_shortcut {
|
||||
full_spans.push(" ".into());
|
||||
full_spans.push(" (".into());
|
||||
full_spans.push(display_shortcut.into());
|
||||
full_spans.push(")".into());
|
||||
}
|
||||
if let Some(desc) = row.description.as_ref() {
|
||||
let gap = desc_col.saturating_sub(this_name_width);
|
||||
@@ -179,8 +181,9 @@ pub(crate) fn render_rows(
|
||||
);
|
||||
if Some(i) == state.selected_idx {
|
||||
// Match previous behavior: cyan + bold for the selected row.
|
||||
// Reset the style first to avoid inheriting dim from keyboard shortcuts.
|
||||
full_line.spans.iter_mut().for_each(|span| {
|
||||
span.style = span.style.fg(Color::Cyan).bold();
|
||||
span.style = Style::default().fg(Color::Cyan).bold();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ expression: terminal.backend().vt100().screen().contents()
|
||||
|
||||
$ echo hello world
|
||||
|
||||
› 1. Yes, proceed
|
||||
2. Yes, and don't ask again for this command
|
||||
3. No, and tell Codex what to do differently esc
|
||||
› 1. Yes, proceed (y)
|
||||
2. Yes, and don't ask again for this command (a)
|
||||
3. No, and tell Codex what to do differently (esc)
|
||||
|
||||
Press enter to confirm or esc to cancel
|
||||
|
||||
@@ -6,8 +6,8 @@ expression: terminal.backend().vt100().screen().contents()
|
||||
|
||||
$ echo hello world
|
||||
|
||||
› 1. Yes, proceed
|
||||
2. Yes, and don't ask again for this command
|
||||
3. No, and tell Codex what to do differently esc
|
||||
› 1. Yes, proceed (y)
|
||||
2. Yes, and don't ask again for this command (a)
|
||||
3. No, and tell Codex what to do differently (esc)
|
||||
|
||||
Press enter to confirm or esc to cancel
|
||||
|
||||
@@ -11,7 +11,7 @@ expression: terminal.backend().vt100().screen().contents()
|
||||
1 +hello
|
||||
2 +world
|
||||
|
||||
› 1. Yes, proceed
|
||||
2. No, and tell Codex what to do differently esc
|
||||
› 1. Yes, proceed (y)
|
||||
2. No, and tell Codex what to do differently (esc)
|
||||
|
||||
Press enter to confirm or esc to cancel
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
source: tui/src/chatwidget/tests.rs
|
||||
assertion_line: 409
|
||||
expression: "format!(\"{buf:?}\")"
|
||||
---
|
||||
Buffer {
|
||||
@@ -15,9 +14,9 @@ Buffer {
|
||||
" ",
|
||||
" $ echo hello world ",
|
||||
" ",
|
||||
"› 1. Yes, proceed ",
|
||||
" 2. Yes, and don't ask again for this command ",
|
||||
" 3. No, and tell Codex what to do differently esc ",
|
||||
"› 1. Yes, proceed (y) ",
|
||||
" 2. Yes, and don't ask again for this command (a) ",
|
||||
" 3. No, and tell Codex what to do differently (esc) ",
|
||||
" ",
|
||||
" Press enter to confirm or esc to cancel ",
|
||||
],
|
||||
@@ -30,9 +29,11 @@ Buffer {
|
||||
x: 2, y: 5, fg: Reset, bg: Reset, underline: Reset, modifier: ITALIC,
|
||||
x: 7, y: 5, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
|
||||
x: 0, y: 9, fg: Cyan, bg: Reset, underline: Reset, modifier: BOLD,
|
||||
x: 17, y: 9, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
|
||||
x: 47, y: 11, fg: Reset, bg: Reset, underline: Reset, modifier: DIM,
|
||||
x: 50, y: 11, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
|
||||
x: 21, y: 9, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
|
||||
x: 48, y: 10, fg: Reset, bg: Reset, underline: Reset, modifier: DIM,
|
||||
x: 49, y: 10, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
|
||||
x: 48, y: 11, fg: Reset, bg: Reset, underline: Reset, modifier: DIM,
|
||||
x: 51, y: 11, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
|
||||
x: 2, y: 13, fg: Reset, bg: Reset, underline: Reset, modifier: DIM,
|
||||
]
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ expression: terminal.backend()
|
||||
" "
|
||||
" $ echo 'hello world' "
|
||||
" "
|
||||
"› 1. Yes, proceed "
|
||||
" 2. Yes, and don't ask again for this command "
|
||||
" 3. No, and tell Codex what to do differently esc "
|
||||
"› 1. Yes, proceed (y) "
|
||||
" 2. Yes, and don't ask again for this command (a) "
|
||||
" 3. No, and tell Codex what to do differently (esc) "
|
||||
" "
|
||||
" Press enter to confirm or esc to cancel "
|
||||
|
||||
Reference in New Issue
Block a user