Skip to main content

fresh/view/
event_debug.rs

1//! Event debug dialog rendering
2//!
3//! Renders the event debug dialog modal overlay.
4
5use crate::app::event_debug::EventDebug;
6use crate::view::theme::Theme;
7use ratatui::{
8    layout::{Constraint, Layout, Rect},
9    style::{Modifier, Style},
10    text::{Line, Span},
11    widgets::{Block, Borders, Clear, Paragraph, Wrap},
12    Frame,
13};
14use rust_i18n::t;
15
16/// Width of the dialog in characters
17const DIALOG_WIDTH: u16 = 70;
18/// Height of the dialog
19const DIALOG_HEIGHT: u16 = 18;
20
21/// Render the event debug dialog overlay
22pub fn render_event_debug(frame: &mut Frame, area: Rect, debug: &EventDebug, theme: &Theme) {
23    // Calculate dialog dimensions
24    let dialog_height = DIALOG_HEIGHT.min(area.height.saturating_sub(4));
25    let dialog_width = DIALOG_WIDTH.min(area.width.saturating_sub(4));
26
27    // Center the dialog
28    let dialog_x = (area.width.saturating_sub(dialog_width)) / 2;
29    let dialog_y = (area.height.saturating_sub(dialog_height)) / 2;
30
31    let dialog_area = Rect {
32        x: dialog_x,
33        y: dialog_y,
34        width: dialog_width,
35        height: dialog_height,
36    };
37
38    // Clear the area behind the dialog
39    frame.render_widget(Clear, dialog_area);
40
41    // Create the outer block
42    let block = Block::default()
43        .title(t!("event_debug.title").to_string())
44        .borders(Borders::ALL)
45        .border_style(Style::default().fg(theme.editor_fg))
46        .style(Style::default().bg(theme.editor_bg).fg(theme.editor_fg));
47
48    let inner_area = block.inner(dialog_area);
49    frame.render_widget(block, dialog_area);
50
51    // Layout: instructions at top, event history in middle, controls at bottom
52    let chunks = Layout::vertical([
53        Constraint::Length(3), // Instructions
54        Constraint::Min(8),    // Event history
55        Constraint::Length(4), // Controls/details
56    ])
57    .split(inner_area);
58
59    // Instructions
60    let instructions = vec![
61        Line::from(vec![Span::styled(
62            t!("event_debug.instructions").to_string(),
63            Style::default().add_modifier(Modifier::BOLD),
64        )]),
65        Line::from(t!("event_debug.help_text").to_string()),
66    ];
67
68    let instructions_para = Paragraph::new(instructions)
69        .style(Style::default().fg(theme.editor_fg))
70        .wrap(Wrap { trim: true });
71    frame.render_widget(instructions_para, chunks[0]);
72
73    // Event history
74    let mut history_lines: Vec<Line> = Vec::new();
75
76    if debug.history.is_empty() {
77        history_lines.push(Line::from(vec![Span::styled(
78            t!("event_debug.no_events").to_string(),
79            Style::default().fg(theme.line_number_fg),
80        )]));
81    } else {
82        history_lines.push(Line::from(vec![
83            Span::styled(
84                t!("event_debug.recent_events").to_string(),
85                Style::default()
86                    .fg(theme.help_key_fg)
87                    .add_modifier(Modifier::BOLD),
88            ),
89            Span::raw(format!(" ({})", debug.history.len())),
90        ]));
91        history_lines.push(Line::from(""));
92
93        for (i, recorded) in debug.history.iter().enumerate() {
94            let style = if i == 0 {
95                Style::default()
96                    .fg(theme.diagnostic_info_fg)
97                    .add_modifier(Modifier::BOLD)
98            } else {
99                Style::default().fg(theme.editor_fg)
100            };
101
102            let prefix = if i == 0 { "> " } else { "  " };
103            history_lines.push(Line::from(vec![
104                Span::styled(prefix, style),
105                Span::styled(&recorded.description, style),
106            ]));
107        }
108    }
109
110    let history_para = Paragraph::new(history_lines).style(Style::default().fg(theme.editor_fg));
111    frame.render_widget(history_para, chunks[1]);
112
113    // Controls and last event details
114    let mut control_lines = vec![Line::from(vec![
115        Span::styled("[q]", Style::default().fg(theme.help_key_fg)),
116        Span::raw(" "),
117        Span::raw(t!("event_debug.close").to_string()),
118        Span::raw("  "),
119        Span::styled("[Esc]", Style::default().fg(theme.help_key_fg)),
120        Span::raw(" "),
121        Span::raw(t!("event_debug.close").to_string()),
122        Span::raw("  "),
123        Span::styled("[c]", Style::default().fg(theme.help_key_fg)),
124        Span::raw(" "),
125        Span::raw(t!("event_debug.clear").to_string()),
126    ])];
127
128    // Show raw details of last event
129    if let Some(details) = debug.last_event_details() {
130        control_lines.push(Line::from(""));
131        control_lines.push(Line::from(vec![Span::styled(
132            details,
133            Style::default().fg(theme.line_number_fg),
134        )]));
135    }
136
137    let controls_para = Paragraph::new(control_lines)
138        .style(Style::default().fg(theme.editor_fg))
139        .wrap(Wrap { trim: true });
140    frame.render_widget(controls_para, chunks[2]);
141}