1use ratatui::layout::{Constraint, Direction, Layout, Rect};
2use ratatui::style::Style;
3use ratatui::widgets::Block;
4use ratatui::Frame;
5
6use crate::app::{App, AppScreen};
7use crate::features;
8use crate::widgets;
9
10pub fn render(app: &mut App, frame: &mut Frame) {
12 let bg = app.theme().bg;
16 frame.render_widget(
17 Block::default().style(Style::default().bg(bg)),
18 frame.area(),
19 );
20
21 match app.screen {
22 AppScreen::Welcome => {
23 features::repo::view::render(&*app, frame, frame.area());
24 }
25 AppScreen::DirBrowser => {
26 features::repo::view::render_browser(app, frame, frame.area());
27 }
28 AppScreen::Main => {
29 render_main(app, frame);
30 }
31 }
32}
33
34fn render_main(app: &mut App, frame: &mut Frame) {
37 let initial_load = app.tab().is_loading && app.tab().commits.is_empty();
41 if initial_load {
42 features::skeleton::render(app, frame);
43 return;
44 }
45 let outer = Layout::default()
51 .direction(Direction::Vertical)
52 .constraints([
53 Constraint::Length(3),
54 Constraint::Percentage(60),
55 Constraint::Percentage(40),
56 Constraint::Length(1),
57 ])
58 .split(frame.area());
59
60 widgets::header::render(app, frame, outer[0]);
62
63 let longest_branch = app
71 .tab()
72 .branches
73 .iter()
74 .map(|b| b.name.chars().count())
75 .max()
76 .unwrap_or(10);
77 let ideal_sidebar = (longest_branch + 6) as u16;
79 let term_width = outer[1].width;
80 let max_sidebar = (term_width * 30 / 100).clamp(22, 50);
82 let sidebar_width = ideal_sidebar.min(max_sidebar).max(22);
83
84 let main_cols = Layout::default()
85 .direction(Direction::Horizontal)
86 .constraints([
87 Constraint::Length(sidebar_width),
88 Constraint::Percentage(40),
89 Constraint::Min(20),
90 ])
91 .split(outer[1]);
92
93 let sidebar = Layout::default()
96 .direction(Direction::Vertical)
97 .constraints([
98 Constraint::Min(6), Constraint::Length(5), Constraint::Length(5), ])
102 .split(main_cols[0]);
103
104 features::branches::view::render(app, frame, sidebar[0]);
105 features::stash::view::render(app, frame, sidebar[1]);
106 features::remotes::view::render(app, frame, sidebar[2]);
107
108 features::commits::view::render(app, frame, main_cols[1]);
110
111 let overlay_rect = Rect {
114 x: main_cols[2].x,
115 y: main_cols[2].y,
116 width: main_cols[2].width,
117 height: main_cols[2].height + outer[2].height,
118 };
119
120 if app.show_theme_panel {
122 features::theme::view::render(app, frame, overlay_rect);
123 } else if app.show_options_panel {
124 features::options::view::render(app, frame, overlay_rect);
125 } else if app.show_editor_panel {
126 features::editor::view::render(app, frame, main_cols[2]);
127 } else if app.tab().file_history_path.is_some() {
128 features::diff::view::render_file_history(app, frame, main_cols[2]);
129 } else if app.tab().blame_path.is_some() {
130 features::diff::view::render_blame(app, frame, main_cols[2]);
131 } else {
132 features::diff::view::render(app, frame, main_cols[2]);
133 }
134
135 if !app.show_theme_panel && !app.show_options_panel {
137 features::staging::view::render(app, frame, outer[2]);
138 } else {
139 let staging_partial = Rect {
141 x: outer[2].x,
142 y: outer[2].y,
143 width: main_cols[2].x.saturating_sub(outer[2].x),
144 height: outer[2].height,
145 };
146 features::staging::view::render(app, frame, staging_partial);
147 }
148
149 widgets::status_bar::render(app, frame, outer[3]);
151}