Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 49 additions & 4 deletions crates/tui/src/tui/widgets/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1384,6 +1384,17 @@ impl Renderable for ApprovalWidget<'_> {
let options = approval_options_for(risk, locale);

for (i, opt) in options.iter().enumerate() {
// Divider between the approve group (0-1) and the deny/abort
// group (2-3) so the two clusters read as distinct decisions and
// an approve is harder to misread as a deny. Sized to fit the
// minimum card inner width without wrapping.
if i == 2 {
lines.push(Line::from(vec![Span::styled(
format!(" {}", "─".repeat(28)),
Style::default().fg(palette::TEXT_MUTED),
)]));
}

let is_selected = i == self.view.selected();
let label_color = if opt.dangerous {
palette_colors.accent
Expand All @@ -1394,15 +1405,22 @@ 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(" "),
// The selected row is already painted as a highlight strip by the
// styles above; give it a leading caret so the action Enter will
// fire is unmistakable, not signalled by the background alone.
let lead = if is_selected {
Span::styled("\u{25b8} ", approval_selected_style())
} else {
Span::raw(" ")
};
lines.push(Line::from(vec![
lead,
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
Expand Down Expand Up @@ -4574,6 +4592,33 @@ mod tests {
);
}

#[test]
fn approval_card_shows_group_divider_and_selected_caret() {
let request = crate::tui::approval::ApprovalRequest::new(
"approval-1",
"exec_shell",
"Run git commit",
&serde_json::json!({ "command": "git commit -m fix" }),
"exec_shell:git commit",
);
let view = crate::tui::approval::ApprovalView::new(request.clone());
let widget = ApprovalWidget::new(&request, &view);
let area = Rect::new(0, 0, 100, 30);
let mut buf = Buffer::empty(area);

widget.render(area, &mut buf);
let rendered = buffer_text(&buf, area);

assert!(
rendered.contains('\u{25b8}'),
"selected row should show a caret:\n{rendered}"
);
assert!(
rendered.contains('\u{2500}'),
"approve and deny groups should be split by a divider:\n{rendered}"
);
}

#[test]
fn approval_shell_command_detects_printf_write_file_preview() {
let request = crate::tui::approval::ApprovalRequest::new(
Expand Down
Loading