-
Notifications
You must be signed in to change notification settings - Fork 3.4k
feat(tui): enhance approval modal button prominence with visual grouping #3437
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d7b2983
1624c07
cd71a51
305b9e3
48600aa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1182,8 +1182,20 @@ impl Renderable for ApprovalWidget<'_> { | |
| lines.push(Line::from("")); | ||
|
|
||
| let options = approval_options_for(risk, locale); | ||
| let separator = locale_separator(locale); | ||
|
|
||
| for (i, opt) in options.iter().enumerate() { | ||
| // Insert a visual separator between the approve group (0-1) | ||
| // and the deny/abort group (2-3) so the decision surface | ||
| // reads as two distinct choice clusters rather than a flat | ||
| // list. | ||
| if i == 2 { | ||
| lines.push(Line::from(vec![Span::styled( | ||
| separator, | ||
| Style::default().fg(palette::TEXT_MUTED), | ||
| )])); | ||
| } | ||
|
|
||
| let is_selected = i == self.view.selected(); | ||
| let label_color = if opt.dangerous { | ||
| palette_colors.accent | ||
|
|
@@ -1194,15 +1206,41 @@ impl Renderable for ApprovalWidget<'_> { | |
| let option_style = approval_option_style(is_selected, label_color); | ||
| let shortcut_style = approval_option_style(is_selected, palette_colors.shortcut); | ||
|
|
||
| let spans = vec![ | ||
| Span::raw(" "), | ||
| Span::styled( | ||
| format!("[{}] ", opt.key_hint), | ||
| shortcut_style.add_modifier(Modifier::BOLD), | ||
| ), | ||
| Span::styled(opt.label.to_string(), option_style), | ||
| ]; | ||
| lines.push(Line::from(spans)); | ||
| if is_selected { | ||
| // Selected option: render as a solid button row with a | ||
| // distinct background strip so the user can instantly see | ||
| // which action will fire on Enter. | ||
| lines.push(Line::from(vec![ | ||
| Span::styled(" ", Style::default().bg(palette::SELECTION_BG)), | ||
| Span::styled( | ||
| format!("[{}] ", opt.key_hint), | ||
| shortcut_style.add_modifier(Modifier::BOLD).bg(palette::SELECTION_BG), | ||
| ), | ||
| Span::styled( | ||
| opt.label.to_string(), | ||
| option_style.add_modifier(Modifier::BOLD).bg(palette::SELECTION_BG), | ||
| ), | ||
| Span::styled(" ", Style::default().bg(palette::SELECTION_BG)), | ||
| Span::styled( | ||
| selected_indicator(locale), | ||
| Style::default() | ||
| .fg(palette::SELECTION_TEXT) | ||
| .bg(palette::SELECTION_BG) | ||
| .add_modifier(Modifier::BOLD), | ||
| ), | ||
| ])); | ||
|
Comment on lines
+1209
to
+1231
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The selected option background is currently applied only to the individual text spans. This means the background highlight will only cover the text itself, rather than forming a solid, full-width button row as described in the PR. Additionally, the We can resolve both issues by calculating the remaining width of the row and inserting a padding span of spaces with the if is_selected {
// Selected option: render as a solid button row with a
// distinct background strip so the user can instantly see
// which action will fire on Enter.
let shortcut_text = format!("[{}] ", opt.key_hint);
let label_text = opt.label.to_string();
let indicator_text = selected_indicator(locale);
let text_width = 2 + shortcut_text.width() + label_text.width() + indicator_text.width();
let padding_width = content_width.saturating_sub(text_width);
lines.push(Line::from(vec![
Span::styled(" ", Style::default().bg(palette::SELECTION_BG)),
Span::styled(
shortcut_text,
shortcut_style.add_modifier(Modifier::BOLD).bg(palette::SELECTION_BG),
),
Span::styled(
label_text,
option_style.add_modifier(Modifier::BOLD).bg(palette::SELECTION_BG),
),
Span::styled(
" ".repeat(padding_width),
Style::default().bg(palette::SELECTION_BG),
),
Span::styled(
indicator_text,
Style::default()
.fg(palette::SELECTION_TEXT)
.bg(palette::SELECTION_BG)
.add_modifier(Modifier::BOLD),
),
]));
} else { |
||
| } else { | ||
| // Unselected option: plain row with subtle shortcut emphasis. | ||
| let spans = vec![ | ||
| Span::raw(" "), | ||
| Span::styled( | ||
| format!("[{}] ", opt.key_hint), | ||
| shortcut_style.add_modifier(Modifier::BOLD), | ||
| ), | ||
| Span::styled(opt.label.to_string(), option_style), | ||
| ]; | ||
| lines.push(Line::from(spans)); | ||
| } | ||
| } | ||
|
|
||
| // Footer: Enter commits the highlighted row; y/a/d remain direct | ||
|
|
@@ -1481,6 +1519,20 @@ fn option_abort(locale: Locale) -> &'static str { | |
| } | ||
| } | ||
|
|
||
| fn locale_separator(locale: Locale) -> &'static str { | ||
| match locale { | ||
| Locale::ZhHans => " ────────────────────────────────────────", | ||
| _ => " ────────────────────────────────────────", | ||
| } | ||
| } | ||
|
Comment on lines
+1522
to
+1527
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| fn selected_indicator(locale: Locale) -> &'static str { | ||
| match locale { | ||
| Locale::ZhHans => " ◄ 执行", | ||
| _ => " ◄ EXECUTE", | ||
| } | ||
| } | ||
|
|
||
| pub struct ElevationWidget<'a> { | ||
| request: &'a ElevationRequest, | ||
| selected: usize, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| The optional `fuzz` parameter was required to attempt the leading-indentation fuzzy fallback when exact search found zero matches. This forced the model to make two calls on every edit that needed fuzzy matching (first without fuzz -> error -> second with fuzz: true), causing a round-trip delay. | ||
|
|
||
| Fix: remove the `fuzz` gate from the count == 0 branch. The tool now automatically retries with indentation-tolerant fuzzy matching when exact search produces no results. The `fuzz` parameter is kept in the schema for backward compatibility but marked deprecated. | ||
|
|
||
| Changes: | ||
| - crates/tui/src/tools/file.rs: `if count == 0 && fuzz` -> `if count == 0` (always retry fuzzy fallback) | ||
| - crates/tui/src/tools/file.rs: removed dead `else if count == 0 { error }` branch | ||
| - crates/tui/src/tools/file.rs: updated description to note automatic fuzzy fallback | ||
| - crates/tui/src/tools/file.rs: marked fuzz parameter as deprecated in schema |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The separator line is currently hardcoded to 40 characters. However, the approval card width is dynamic and can be as small as
APPROVAL_CARD_MIN_WIDTH(40), which results in a content width of 36. In such cases, the 40-character separator line will wrap to the next line, breaking the visual layout.We can dynamically construct the separator line using
card_area.widthto ensure it perfectly spans the available content width without wrapping.