use super::{
App, Constraint, Direction, Frame, Layout, Mode, Paragraph, Rect, SettingsPopup, Theme,
};
impl App {
pub(super) fn effective_theme(&self) -> Theme {
self.settings
.as_ref()
.and_then(SettingsPopup::preview_theme)
.unwrap_or(self.theme)
}
pub fn cursor_style(&self) -> Option<ratatui::crossterm::cursor::SetCursorStyle> {
if let Some(cl) = &self.command_line {
return cl.cursor_style();
}
if let Some(settings) = &self.settings {
return settings.cursor_style();
}
if let Some(view) = &self.search_view {
return view.cursor_style();
}
self.popup.as_ref().and_then(|p| p.cursor_style())
}
pub fn render(&mut self, frame: &mut Frame, area: Rect) {
let theme = self.effective_theme();
let mode = self.effective_mode();
let overlay_up = self.popup.is_some()
|| self.wizard.is_some()
|| self.settings.is_some()
|| self.help.is_some()
|| self.command_line.is_some()
|| self.refs_popup.is_some()
|| self.consent.is_some()
|| self.search_view.is_some();
let strip = mode == Mode::Leader || (!overlay_up && mode != Mode::Browse);
let rows = Layout::default()
.direction(Direction::Vertical)
.constraints(if strip {
vec![
Constraint::Min(1),
Constraint::Length(1),
Constraint::Length(1),
]
} else {
vec![Constraint::Min(1), Constraint::Length(1)]
})
.split(area);
if let Some(view) = &mut self.search_view {
view.render(frame, rows[0], &theme);
self.modeline.context = view.context();
} else {
let zoomed = matches!(self.mode, Mode::Preview | Mode::History | Mode::Commit)
|| (self.mode == Mode::Find && self.find_return == Some(Mode::Preview));
self.browser.preview.focused = zoomed;
self.browser.render(frame, rows[0], &theme, zoomed);
self.modeline.context = self.browser.context();
}
if strip {
frame.render_widget(
Paragraph::new(crate::components::modeline::hint_strip_line(
mode,
rows[1].width as usize,
&theme,
)),
rows[1],
);
}
let mut status = self.status.clone().or_else(|| self.degraded.clone());
if let Some(note) = &self.trace_failure {
status = Some(match status {
Some(current) => format!("{current} · {note}"),
None => note.clone(),
});
}
self.modeline.status = status;
let modeline_row = rows[rows.len() - 1];
self.modeline.update_tag = self.update_tag.clone();
self.modeline.degraded = self.degraded.is_some();
self.modeline.render(frame, modeline_row, mode, &theme);
if let Some(popup) = &mut self.popup {
popup.render(frame, rows[0], &theme);
}
if let Some(help) = &mut self.help {
help.render(frame, rows[0], &theme);
}
if let Some(settings) = &mut self.settings {
settings.render(frame, rows[0], &theme);
}
if let Some(wizard) = &mut self.wizard {
wizard.render(frame, rows[0], &theme);
}
if let Some(refs) = &mut self.refs_popup {
refs.render(frame, rows[0], &theme);
}
if let Some(consent) = &mut self.consent {
consent.render(frame, rows[0], &theme);
}
if let Some(command_line) = &mut self.command_line {
command_line.render(frame, rows[0], &theme);
}
}
pub fn snapshot(&self) -> serde_json::Value {
serde_json::json!({
"mode": self.effective_mode().chip(),
"context": self.browser.context(),
"ref": self.browser.current_ref(),
"provider": self.provider.name(),
"popup": self.popup.is_some(),
"search_view": self.search_view.is_some(),
"help": self.help.is_some(),
"command_line": self.command_line.is_some(),
"settings": self.settings.is_some(),
"wizard": self.wizard.is_some(),
"refs_popup": self.refs_popup.is_some(),
"consent": self.consent.is_some(),
"status": self.status,
"degraded": self.degraded,
"update_tag": self.update_tag,
"should_quit": self.should_quit,
"surface": self.commit_surface(),
})
}
pub(super) fn commit_surface(&self) -> serde_json::Value {
let Some(view) = self.browser.commit_ref() else {
return serde_json::Value::Null;
};
serde_json::json!({
"sha": view.sha_short(),
"repository": view.request().repository.as_str(),
"request": view.request().generation.to_string(),
"delta": view.open_file(),
"files": view.file_count(),
})
}
pub fn active_commit_request(&self) -> Option<&crate::request::CommitRequest> {
self.browser.commit_ref().map(|view| view.request())
}
}