use ratatui::{
Frame,
layout::Rect,
style::{Modifier, Style},
text::{Line, Span},
widgets::Paragraph,
};
use unicode_width::UnicodeWidthStr;
use crate::{
cli::DiffRange,
state::App,
theme::{ChromeStyle, Theme},
};
pub fn render(f: &mut Frame, app: &App, area: Rect, theme: &Theme) {
let width = area.width as usize;
if width < 6 {
return;
}
let bar_bg = theme.bg;
let corner_fg = Style::default().fg(theme.shuire).bg(bar_bg);
let rule_fg = Style::default().fg(theme.border_unfocused).bg(bar_bg);
let fg_dim = Style::default().fg(theme.dim_fg).bg(bar_bg);
let brand_style = Style::default()
.fg(theme.shuire)
.bg(bar_bg)
.add_modifier(Modifier::BOLD);
let mut spans: Vec<Span> = Vec::new();
let sparse = matches!(theme.chrome, ChromeStyle::Sparse);
if !sparse {
spans.push(Span::styled(theme.chrome.top_left().to_string(), corner_fg));
spans.push(Span::styled("━".to_string(), rule_fg));
} else {
spans.push(Span::styled(" ", fg_dim));
}
spans.push(Span::styled(" 朱入レ ", brand_style));
let range_spans = range_spans(&app.range, theme);
let range_w: usize = range_spans.iter().map(|s| s.content.width()).sum();
let used = spans.iter().map(|s| s.content.width()).sum::<usize>() + range_w + 2;
let fill = width.saturating_sub(used);
let fill_char = if sparse { ' ' } else { '─' };
spans.push(Span::styled(
fill_char.to_string().repeat(fill),
if sparse { fg_dim } else { rule_fg },
));
for s in range_spans {
spans.push(s);
}
if !sparse {
spans.push(Span::styled("━".to_string(), rule_fg));
spans.push(Span::styled(
theme.chrome.top_right().to_string(),
corner_fg,
));
} else {
spans.push(Span::styled(" ", fg_dim));
}
let line = Line::from(spans).style(Style::default().bg(bar_bg));
f.render_widget(Paragraph::new(line), area);
}
fn range_spans(range: &DiffRange, theme: &Theme) -> Vec<Span<'static>> {
let bg = theme.bg;
let fg_dim = Style::default().fg(theme.dim_fg).bg(bg);
let shuire = Style::default()
.fg(theme.shuire)
.bg(bg)
.add_modifier(Modifier::BOLD);
let pair = |base: String, target: String| -> Vec<Span<'static>> {
vec![
Span::styled(" ", fg_dim),
Span::styled(base, fg_dim),
Span::styled(" → ", shuire),
Span::styled(target, fg_dim),
Span::styled(" ", fg_dim),
]
};
let single = |s: String| -> Vec<Span<'static>> { vec![Span::styled(format!(" {s} "), fg_dim)] };
match range {
DiffRange::Working => single("working tree".to_string()),
DiffRange::Staged => single("staged".to_string()),
DiffRange::StagedAgainst { base } => pair(base.clone(), "staged".to_string()),
DiffRange::Uncommitted => single("HEAD..".to_string()),
DiffRange::UncommittedAgainst { base } => pair(base.clone(), "uncommitted".to_string()),
DiffRange::Range { base, target } => pair(base.clone(), target.clone()),
DiffRange::MergeBase { base, target } => {
pair(format!("merge-base({base})"), target.clone())
}
DiffRange::PullRequest { url, .. } => single(format!("PR: {url}")),
DiffRange::Stdin => single("stdin".to_string()),
}
}