1use std::fs::{self, File};
2use std::io::Write;
3use std::path::PathBuf;
4
5use ratatui::{Terminal, backend::TestBackend};
6
7use super::app::ChatApp;
8use super::ui;
9
10const DEBUG_WIDTH: u16 = 120;
11const DEBUG_HEIGHT: u16 = 40;
12
13pub struct DebugRenderer {
14 terminal: Terminal<TestBackend>,
15 output_dir: PathBuf,
16 frame_count: usize,
17 last_buffer: Option<String>,
18}
19
20impl DebugRenderer {
21 pub fn new(output_dir: PathBuf) -> anyhow::Result<Self> {
22 fs::create_dir_all(&output_dir)?;
23 let backend = TestBackend::new(DEBUG_WIDTH, DEBUG_HEIGHT);
24 let terminal = Terminal::new(backend)?;
25 Ok(Self {
26 terminal,
27 output_dir,
28 frame_count: 0,
29 last_buffer: None,
30 })
31 }
32
33 pub fn render(&mut self, app: &ChatApp) -> anyhow::Result<()> {
34 self.terminal.draw(|f| {
35 ui::render_app(f, app);
36 })?;
37
38 let current = self.buffer_to_string();
40
41 if self.last_buffer.as_ref() != Some(¤t) {
43 self.dump_screen(¤t)?;
44 self.frame_count += 1;
45 self.last_buffer = Some(current);
46 }
47
48 Ok(())
49 }
50
51 fn buffer_to_string(&self) -> String {
52 let buffer = self.terminal.backend().buffer();
53 let mut result = String::new();
54
55 for y in 0..DEBUG_HEIGHT {
56 let mut line = String::new();
57 for x in 0..DEBUG_WIDTH {
58 let cell = &buffer[(x, y)];
59 line.push_str(cell.symbol());
60 }
61 result.push_str(line.trim_end());
62 result.push('\n');
63 }
64
65 result
66 }
67
68 fn dump_screen(&self, content: &str) -> anyhow::Result<()> {
69 let filename = format!("screen-{:03}.txt", self.frame_count);
70 let path = self.output_dir.join(filename);
71
72 let mut file = File::create(&path)?;
73 write!(file, "{}", content)?;
74
75 Ok(())
76 }
77
78 pub fn output_dir(&self) -> &std::path::Path {
79 &self.output_dir
80 }
81
82 pub fn frame_count(&self) -> usize {
83 self.frame_count
84 }
85}