validate_widgets/
validate_widgets.rs

1//! Outputs header and footer widgets to files for TUI validation.
2//!
3//! Run with: cargo run -p ralph-tui --example validate_widgets
4
5use ralph_proto::{Event, HatId};
6use ralph_tui::TuiState;
7use ratatui::backend::TestBackend;
8use ratatui::layout::{Constraint, Direction, Layout};
9use ratatui::Terminal;
10use std::fs;
11use std::time::Duration;
12
13fn render_to_string(terminal: &Terminal<TestBackend>) -> String {
14    let buffer = terminal.backend().buffer();
15    let mut lines = Vec::new();
16    for y in 0..buffer.area.height {
17        let mut line = String::new();
18        for x in 0..buffer.area.width {
19            let cell = buffer.cell((x, y)).unwrap();
20            line.push_str(cell.symbol());
21        }
22        lines.push(line.trim_end().to_string());
23    }
24    lines.join("\n")
25}
26
27fn main() {
28    let output_dir = std::env::current_dir().unwrap().join("tui-validation");
29    fs::create_dir_all(&output_dir).unwrap();
30
31    // Create a fully populated state for validation
32    let mut state = TuiState::new();
33    let event = Event::new("task.start", "");
34    state.update(&event);
35
36    state.iteration = 2;
37    state.max_iterations = Some(10);
38    state.loop_started = Some(std::time::Instant::now().checked_sub(Duration::from_secs(272)).unwrap());
39    state.pending_hat = Some((HatId::new("builder"), "🔨Builder".to_string()));
40    state.loop_mode = ralph_tui::LoopMode::Auto;
41    state.last_event = Some("build.task".to_string());
42    state.last_event_at = Some(std::time::Instant::now()); // Active
43
44    // Render header
45    let backend = TestBackend::new(80, 3);
46    let mut terminal = Terminal::new(backend).unwrap();
47    terminal
48        .draw(|f| {
49            let widget = ralph_tui::header::render(&state);
50            f.render_widget(widget, f.area());
51        })
52        .unwrap();
53    let header_output = render_to_string(&terminal);
54    fs::write(output_dir.join("header.txt"), &header_output).unwrap();
55    println!("Header output written to tui-validation/header.txt");
56    println!("{}", header_output);
57    println!();
58
59    // Render header with scroll mode
60    state.in_scroll_mode = true;
61    let backend = TestBackend::new(80, 3);
62    let mut terminal = Terminal::new(backend).unwrap();
63    terminal
64        .draw(|f| {
65            let widget = ralph_tui::header::render(&state);
66            f.render_widget(widget, f.area());
67        })
68        .unwrap();
69    let header_scroll_output = render_to_string(&terminal);
70    fs::write(output_dir.join("header_scroll.txt"), &header_scroll_output).unwrap();
71    println!("Header (scroll mode) output written to tui-validation/header_scroll.txt");
72    println!("{}", header_scroll_output);
73    println!();
74    state.in_scroll_mode = false;
75
76    // Render header with paused mode
77    state.loop_mode = ralph_tui::LoopMode::Paused;
78    let backend = TestBackend::new(80, 3);
79    let mut terminal = Terminal::new(backend).unwrap();
80    terminal
81        .draw(|f| {
82            let widget = ralph_tui::header::render(&state);
83            f.render_widget(widget, f.area());
84        })
85        .unwrap();
86    let header_paused_output = render_to_string(&terminal);
87    fs::write(output_dir.join("header_paused.txt"), &header_paused_output).unwrap();
88    println!("Header (paused mode) output written to tui-validation/header_paused.txt");
89    println!("{}", header_paused_output);
90    println!();
91    state.loop_mode = ralph_tui::LoopMode::Auto;
92
93    // Render header with idle countdown
94    state.idle_timeout_remaining = Some(Duration::from_secs(25));
95    let backend = TestBackend::new(80, 3);
96    let mut terminal = Terminal::new(backend).unwrap();
97    terminal
98        .draw(|f| {
99            let widget = ralph_tui::header::render(&state);
100            f.render_widget(widget, f.area());
101        })
102        .unwrap();
103    let header_idle_output = render_to_string(&terminal);
104    fs::write(output_dir.join("header_idle.txt"), &header_idle_output).unwrap();
105    println!("Header (idle countdown) output written to tui-validation/header_idle.txt");
106    println!("{}", header_idle_output);
107    println!();
108    state.idle_timeout_remaining = None;
109
110    // Render footer (default)
111    let scroll_manager = ralph_tui::scroll::ScrollManager::new();
112    let backend = TestBackend::new(80, 3);
113    let mut terminal = Terminal::new(backend).unwrap();
114    terminal
115        .draw(|f| {
116            let widget = ralph_tui::footer::render(&state, &scroll_manager);
117            f.render_widget(widget, f.area());
118        })
119        .unwrap();
120    let footer_output = render_to_string(&terminal);
121    fs::write(output_dir.join("footer_active.txt"), &footer_output).unwrap();
122    println!("Footer (active) output written to tui-validation/footer_active.txt");
123    println!("{}", footer_output);
124    println!();
125
126    // Render footer (idle state)
127    state.last_event_at = Some(std::time::Instant::now().checked_sub(Duration::from_secs(10)).unwrap());
128    let backend = TestBackend::new(80, 3);
129    let mut terminal = Terminal::new(backend).unwrap();
130    terminal
131        .draw(|f| {
132            let widget = ralph_tui::footer::render(&state, &scroll_manager);
133            f.render_widget(widget, f.area());
134        })
135        .unwrap();
136    let footer_idle_output = render_to_string(&terminal);
137    fs::write(output_dir.join("footer_idle.txt"), &footer_idle_output).unwrap();
138    println!("Footer (idle) output written to tui-validation/footer_idle.txt");
139    println!("{}", footer_idle_output);
140    println!();
141
142    // Render footer (done state)
143    state.pending_hat = None;
144    let backend = TestBackend::new(80, 3);
145    let mut terminal = Terminal::new(backend).unwrap();
146    terminal
147        .draw(|f| {
148            let widget = ralph_tui::footer::render(&state, &scroll_manager);
149            f.render_widget(widget, f.area());
150        })
151        .unwrap();
152    let footer_done_output = render_to_string(&terminal);
153    fs::write(output_dir.join("footer_done.txt"), &footer_done_output).unwrap();
154    println!("Footer (done) output written to tui-validation/footer_done.txt");
155    println!("{}", footer_done_output);
156    println!();
157
158    // Render full layout simulation
159    state.pending_hat = Some((HatId::new("builder"), "🔨Builder".to_string()));
160    state.last_event_at = Some(std::time::Instant::now());
161    let backend = TestBackend::new(100, 24);
162    let mut terminal = Terminal::new(backend).unwrap();
163    terminal
164        .draw(|f| {
165            let chunks = Layout::default()
166                .direction(Direction::Vertical)
167                .constraints([
168                    Constraint::Length(3),
169                    Constraint::Min(0),
170                    Constraint::Length(3),
171                ])
172                .split(f.area());
173
174            f.render_widget(ralph_tui::header::render(&state), chunks[0]);
175            // Middle content area (just empty for this test)
176            f.render_widget(
177                ratatui::widgets::Block::default()
178                    .borders(ratatui::widgets::Borders::ALL)
179                    .title(" Terminal Output "),
180                chunks[1],
181            );
182            f.render_widget(ralph_tui::footer::render(&state, &scroll_manager), chunks[2]);
183        })
184        .unwrap();
185    let full_output = render_to_string(&terminal);
186    fs::write(output_dir.join("full_layout.txt"), &full_output).unwrap();
187    println!("Full layout output written to tui-validation/full_layout.txt");
188    println!("{}", full_output);
189
190    println!("\n=== All validation outputs written to tui-validation/ ===");
191}