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:
Raduan A.
2025-11-08 18:08:42 +01:00
committed by GitHub
Unverified
parent 917f39ec12
commit 5beb6167c8
7 changed files with 27 additions and 21 deletions
@@ -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 "