1use ratatui::prelude::*;
13
14use crate::tui::app::{DiffHunk, DiffLineKind};
15
16pub fn header_line(path: &str) -> Line<'static> {
18 Line::from(vec![
19 Span::raw(" "),
20 Span::styled("📝", Style::default().fg(Color::Magenta)),
21 Span::raw(" "),
22 Span::styled(path.to_string(), Style::default().fg(Color::White)),
23 ])
24}
25
26pub fn body_lines(hunks: &[DiffHunk]) -> Vec<Line<'static>> {
28 let mut out = Vec::new();
29 for hunk in hunks {
30 for dl in &hunk.lines {
31 let (sigil, color) = match dl.kind {
32 DiffLineKind::Add => ("+", Color::Green),
33 DiffLineKind::Remove => ("-", Color::Red),
34 DiffLineKind::Context => (" ", Color::Gray),
35 };
36 out.push(Line::from(vec![
37 Span::styled(" │ ".to_string(), Style::default().fg(Color::Gray)),
38 Span::styled(sigil.to_string(), Style::default().fg(color)),
39 Span::raw(" "),
40 Span::styled(dl.text.clone(), Style::default().fg(color)),
41 ]));
42 }
43 }
44 out
45}
46
47pub fn empty_stub_line(path: &str) -> Line<'static> {
50 Line::from(vec![
51 Span::styled(" │ ".to_string(), Style::default().fg(Color::Gray)),
52 Span::styled(
53 format!("Updated {path}"),
54 Style::default()
55 .fg(Color::Gray)
56 .add_modifier(Modifier::ITALIC),
57 ),
58 ])
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64 use crate::tui::app::DiffLine;
65
66 fn span_style_colors(line: &Line) -> Vec<Color> {
67 line.spans.iter().filter_map(|s| s.style.fg).collect()
68 }
69
70 #[test]
71 fn diff_renders_plus_minus_with_colors() {
72 let hunks = vec![DiffHunk {
73 lines: vec![
74 DiffLine {
75 kind: DiffLineKind::Add,
76 text: "new line".into(),
77 },
78 DiffLine {
79 kind: DiffLineKind::Remove,
80 text: "old line".into(),
81 },
82 DiffLine {
83 kind: DiffLineKind::Context,
84 text: "ctx".into(),
85 },
86 ],
87 }];
88 let lines = body_lines(&hunks);
89 assert_eq!(lines.len(), 3);
90 assert!(span_style_colors(&lines[0]).contains(&Color::Green));
91 assert!(span_style_colors(&lines[1]).contains(&Color::Red));
92 assert!(span_style_colors(&lines[2]).contains(&Color::Gray));
94 }
95
96 #[test]
97 fn header_line_contains_path() {
98 let line = header_line("src/agent.rs");
99 let text: String = line.spans.iter().map(|s| s.content.as_ref()).collect();
100 assert!(text.contains("src/agent.rs"));
101 assert!(text.contains("📝"));
102 }
103}