use ratatui::prelude::*;
use ratatui::widgets::Paragraph;
use unicode_width::UnicodeWidthStr;
use crate::tui::app::{App, AuthMethod, AuthState};
#[derive(Default)]
pub struct Header {
pub left: Vec<Span<'static>>,
pub right: Vec<Span<'static>>,
}
pub fn build_header(app: &App) -> Header {
let dim = Style::default().fg(Color::DarkGray);
let gray = Style::default().fg(Color::Gray);
let sep = || Span::styled(" · ", dim);
let left: Vec<Span<'static>> = match &app.auth.state {
AuthState::SignedOut => {
let cred = shorten_home(&app.auth.credentials_path.display().to_string());
vec![
Span::styled("● ", Style::default().fg(Color::DarkGray)),
Span::styled("Not signed in".to_string(), gray),
sep(),
Span::styled(cred, Style::default().fg(Color::Rgb(110, 180, 255))),
]
}
AuthState::SignedIn {
method,
user,
email,
org,
region,
masked_api_key,
..
} => {
let dot = Span::styled("● ", Style::default().fg(Color::Green));
let mut spans = vec![dot];
match method {
AuthMethod::Auth0 => {
let identity = email
.clone()
.or_else(|| user.clone())
.unwrap_or_else(|| "signed in".to_string());
spans.push(Span::styled(identity, gray));
if let Some(org_name) = org.as_ref().filter(|s| !s.is_empty()) {
spans.push(sep());
spans.push(Span::styled(org_name.clone(), gray));
}
spans.push(sep());
spans.push(Span::styled(region.slug(), gray));
}
AuthMethod::ApiKey => {
spans.push(Span::styled("API key".to_string(), gray));
if let Some(mk) = masked_api_key.as_ref() {
spans.push(sep());
spans.push(Span::styled(mk.clone(), gray));
}
spans.push(sep());
spans.push(Span::styled(region.slug(), gray));
}
}
spans
}
};
let version = format!("zilliz v{}", env!("CARGO_PKG_VERSION"));
let right = vec![Span::styled(version, dim)];
Header { left, right }
}
pub fn render(frame: &mut Frame, area: Rect, header: &Header) {
let total = area.width as usize;
let right_width: usize = header
.right
.iter()
.map(|s| UnicodeWidthStr::width(s.content.as_ref()))
.sum();
let left_budget = total.saturating_sub(right_width + 2);
let mut left: Vec<Span<'static>> = Vec::new();
let mut used = 0usize;
for span in header.left.iter().cloned() {
let w = UnicodeWidthStr::width(span.content.as_ref());
if used + w > left_budget {
if left_budget > used + 1 {
left.push(Span::styled("…", Style::default().fg(Color::DarkGray)));
}
break;
}
used += w;
left.push(span);
}
let pad = total.saturating_sub(used + right_width).max(1);
let mut all = left;
all.push(Span::raw(" ".repeat(pad)));
all.extend(header.right.iter().cloned());
let para = Paragraph::new(Line::from(all));
frame.render_widget(para, area);
}
fn shorten_home(path: &str) -> String {
if let Some(home) = dirs::home_dir() {
let home_s = home.display().to_string();
if let Some(rest) = path.strip_prefix(&home_s) {
return format!("~{}", rest);
}
}
path.to_string()
}