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