Skip to main content

mermaid_cli/render/widgets/
attachment.rs

1//! Attachment indicator widget
2//!
3//! Renders [Image #N] indicators above the input box when images are attached.
4//! Shows help text for navigation: "(up to select)" normally, or navigation hints when focused.
5
6use ratatui::{
7    buffer::Buffer,
8    layout::Rect,
9    style::{Modifier, Style},
10    text::{Line, Span},
11    widgets::Widget,
12};
13
14use crate::domain::Attachment;
15use crate::render::theme::Theme;
16
17/// Widget that displays attached image indicators
18pub struct AttachmentWidget<'a> {
19    pub attachments: &'a [Attachment],
20    pub theme: &'a Theme,
21    /// Whether the attachment area is focused (for selection highlight)
22    pub focused: bool,
23    /// Which attachment is selected (only relevant when focused)
24    pub selected: usize,
25}
26
27impl<'a> Widget for AttachmentWidget<'a> {
28    fn render(self, area: Rect, buf: &mut Buffer) {
29        if self.attachments.is_empty() || area.height == 0 {
30            return;
31        }
32
33        let hint_style = Style::new().fg(self.theme.colors.text_secondary.to_color());
34
35        // Render all attachments on a single line with help text
36        let mut spans = vec![Span::raw("  ")];
37
38        for (i, attachment) in self.attachments.iter().enumerate() {
39            let is_selected = self.focused && i == self.selected;
40            let size_display = format_size(attachment.size_bytes);
41
42            let label_style = if is_selected {
43                Style::new()
44                    .fg(self.theme.colors.background.to_color())
45                    .bg(self.theme.colors.info.to_color())
46                    .add_modifier(Modifier::BOLD)
47            } else {
48                Style::new()
49                    .fg(self.theme.colors.info.to_color())
50                    .add_modifier(Modifier::BOLD)
51            };
52
53            spans.push(Span::styled(format!("[Image #{}]", i + 1), label_style));
54            spans.push(Span::styled(
55                format!(
56                    " ({}, {})  ",
57                    attachment.format.to_uppercase(),
58                    size_display
59                ),
60                hint_style,
61            ));
62        }
63
64        // Add help text after the images
65        if self.focused {
66            spans.push(Span::styled(
67                "→ to next · ← to prev · Delete to remove · Esc to cancel",
68                hint_style,
69            ));
70        } else {
71            spans.push(Span::styled("(↑ to select)", hint_style));
72        }
73
74        let line = Line::from(spans);
75        buf.set_line(area.x, area.y, &line, area.width);
76    }
77}
78
79fn format_size(bytes: usize) -> String {
80    if bytes < 1024 {
81        format!("{}B", bytes)
82    } else if bytes < 1024 * 1024 {
83        format!("{:.0}KB", bytes as f64 / 1024.0)
84    } else {
85        format!("{:.1}MB", bytes as f64 / (1024.0 * 1024.0))
86    }
87}