Skip to main content

mermaid_cli/render/widgets/
approval.rs

1//! Bottom-zone modal prompt: a title, a body (the command / path / question),
2//! and numbered options. Used for inline tool-approval prompts (`ask` mode +
3//! Auto-mode escalations) and the `/clear`-style confirmation.
4
5use ratatui::buffer::Buffer;
6use ratatui::layout::Rect;
7use ratatui::style::{Color, Modifier, Style};
8use ratatui::text::{Line, Span};
9use ratatui::widgets::{Block, Borders, Paragraph, Widget};
10
11use super::truncate_to_cells;
12use crate::render::theme::Theme;
13
14pub struct ApprovalModalWidget<'a> {
15    pub theme: &'a Theme,
16    pub title: String,
17    /// Body text — may be multi-line (the command, plus any classifier reason).
18    pub body: &'a str,
19    /// The numbered option lines, e.g. `"1. Yes"`.
20    pub options: Vec<String>,
21    /// Highlighted option for arrow-key navigation, if the modal supports it
22    /// (`None` for plain confirmations that only take y/n).
23    pub selected_index: Option<usize>,
24    /// Border + title accent.
25    pub accent: Color,
26}
27
28impl<'a> Widget for ApprovalModalWidget<'a> {
29    fn render(self, area: Rect, buf: &mut Buffer) {
30        let block = Block::default()
31            .borders(Borders::ALL)
32            .title(self.title)
33            .border_style(Style::default().fg(self.accent));
34
35        let inner_width = area.width.saturating_sub(4).max(8) as usize;
36        let mut lines: Vec<Line<'_>> = Vec::new();
37        for raw in self.body.lines() {
38            lines.push(Line::from(Span::styled(
39                truncate_to_cells(raw, inner_width),
40                Style::default().fg(Color::White),
41            )));
42        }
43        lines.push(Line::from(""));
44        for (idx, opt) in self.options.iter().enumerate() {
45            let style = if self.selected_index == Some(idx) {
46                // Highlighted row — mirrors the slash palette's selection style.
47                Style::default()
48                    .fg(self.theme.colors.text_highlight.to_color())
49                    .add_modifier(Modifier::BOLD | Modifier::REVERSED)
50            } else {
51                Style::default()
52                    .fg(Color::White)
53                    .add_modifier(Modifier::BOLD)
54            };
55            lines.push(Line::from(Span::styled(format!("  {}", opt), style)));
56        }
57        Paragraph::new(lines).block(block).render(area, buf);
58    }
59}