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