1pub mod diff_view;
2pub mod file_tree;
3pub mod summary;
4
5use crate::app::{App, InputMode};
6use ratatui::layout::{Constraint, Layout, Rect};
7use ratatui::style::{Color, Modifier, Style};
8use ratatui::text::{Line, Span};
9use ratatui::widgets::{Block, Clear, Paragraph};
10use ratatui::Frame;
11
12pub fn draw(app: &App, frame: &mut Frame) {
14 let area = frame.area();
15
16 let bottom_height = 1;
18 let vertical =
19 Layout::vertical([Constraint::Min(1), Constraint::Length(bottom_height)]).split(area);
20
21 let sidebar_width = if area.width < 80 {
24 Constraint::Max(25)
25 } else {
26 Constraint::Max(40)
27 };
28 let horizontal =
29 Layout::horizontal([sidebar_width, Constraint::Min(40)]).split(vertical[0]);
30
31 file_tree::render_tree(app, frame, horizontal[0]);
33
34 diff_view::render_diff(app, frame, horizontal[1]);
36
37 match app.input_mode {
39 InputMode::Search => render_search_bar(app, frame, vertical[1]),
40 InputMode::Normal | InputMode::Help => summary::render_summary(app, frame, vertical[1]),
41 }
42
43 if app.input_mode == InputMode::Help {
45 render_help_overlay(frame, area);
46 }
47}
48
49fn render_help_overlay(frame: &mut Frame, area: Rect) {
51 let shortcuts = vec![
52 ("Navigation", vec![
53 ("j/k, ↑/↓", "Move up/down"),
54 ("g/G", "Jump to top/bottom"),
55 ("Ctrl-d/u", "Half-page down/up"),
56 ("Tab", "Switch sidebar/diff focus"),
57 ]),
58 ("Actions", vec![
59 ("Enter", "Sidebar: select file/group | Diff: toggle collapse"),
60 ("/", "Search files"),
61 ("n/N", "Next/prev search match"),
62 ("Esc", "Clear filter / quit"),
63 ("q", "Quit"),
64 ]),
65 ];
66
67 let mut lines: Vec<Line> = vec![Line::raw("")];
68 for (section, keys) in &shortcuts {
69 lines.push(Line::from(Span::styled(
70 format!(" {section}"),
71 Style::default()
72 .fg(Color::Cyan)
73 .add_modifier(Modifier::BOLD),
74 )));
75 for (key, desc) in keys {
76 lines.push(Line::from(vec![
77 Span::styled(
78 format!(" {key:<14}"),
79 Style::default()
80 .fg(Color::Yellow)
81 .add_modifier(Modifier::BOLD),
82 ),
83 Span::styled(*desc, Style::default().fg(Color::White)),
84 ]));
85 }
86 lines.push(Line::raw(""));
87 }
88 lines.push(Line::from(Span::styled(
89 " Press any key to close",
90 Style::default().fg(Color::DarkGray),
91 )));
92
93 let height = (lines.len() + 2).min(area.height as usize) as u16;
94 let width = 50u16.min(area.width.saturating_sub(4));
95 let x = (area.width.saturating_sub(width)) / 2;
96 let y = (area.height.saturating_sub(height)) / 2;
97 let popup_area = Rect::new(x, y, width, height);
98
99 frame.render_widget(Clear, popup_area);
100 let block = Block::bordered()
101 .title(" Shortcuts ")
102 .border_style(Style::default().fg(Color::Cyan));
103 let paragraph = Paragraph::new(lines).block(block);
104 frame.render_widget(paragraph, popup_area);
105}
106
107fn render_search_bar(app: &App, frame: &mut Frame, area: ratatui::layout::Rect) {
109 let line = Line::from(vec![
110 Span::styled(
111 "/ ",
112 Style::default()
113 .fg(Color::Yellow)
114 .add_modifier(Modifier::BOLD),
115 ),
116 Span::styled(
117 app.search_query.clone(),
118 Style::default().fg(Color::White),
119 ),
120 Span::styled(
121 "_",
122 Style::default()
123 .fg(Color::White)
124 .add_modifier(Modifier::SLOW_BLINK),
125 ),
126 ]);
127 let paragraph = ratatui::widgets::Paragraph::new(line);
128 frame.render_widget(paragraph, area);
129}