- 1
//! Top persistent navigation tab bar and session status pills. - 2
- 3
use ratatui::buffer::Buffer; - 4
use ratatui::layout::{Alignment, Rect}; - 5
use ratatui::style::Modifier; - 6
use ratatui::text::{Line, Span}; - 7
use ratatui::widgets::{Paragraph, Widget}; - 8
- 9
use crate::theme::Theme; - 10
- 11
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] - 12
pub enum ActiveTab { - 13
#[default] - 14
Studio = 1, - 15
Observability = 2, - 16
Admin = 3, - 17
Inbox = 4, - 18
} - 19
- 20
impl ActiveTab { - 21
pub fn next(&self) -> Self { - 22
match self { - 23
ActiveTab::Studio => ActiveTab::Observability, - 24
ActiveTab::Observability => ActiveTab::Admin, - 25
ActiveTab::Admin => ActiveTab::Inbox, - 26
ActiveTab::Inbox => ActiveTab::Studio, - 27
} - 28
} - 29
- 30
pub fn prev(&self) -> Self { - 31
match self { - 32
ActiveTab::Studio => ActiveTab::Inbox, - 33
ActiveTab::Observability => ActiveTab::Studio, - 34
ActiveTab::Admin => ActiveTab::Observability, - 35
ActiveTab::Inbox => ActiveTab::Admin, - 36
} - 37
} - 38
} - 39
- 40
pub struct HeaderView<'a> { - 41
pub active_tab: ActiveTab, - 42
pub session_name: &'a str, - 43
pub model_name: &'a str, - 44
pub host_endpoint: &'a str, - 45
pub spend_usd: f64, - 46
pub connected: bool, - 47
pub error: Option<String>, - 48
pub theme: &'a Theme, - 49
} - 50
- 51
impl<'a> Widget for HeaderView<'a> { - 52
fn render(self, area: Rect, buf: &mut Buffer) { - 53
let tabs = [ - 54
(ActiveTab::Studio, "1: Studio"), - 55
(ActiveTab::Observability, "2: Observability & Ops"), - 56
(ActiveTab::Admin, "3: Settings & Remote Admin"), - 57
(ActiveTab::Inbox, "4: Inbox, Memory & Tasks"), - 58
]; - 59
- 60
let mut spans = Vec::new(); - 61
spans.push(Span::raw(" ")); - 62
- 63
// Connection status dot - 64
let conn_dot = if self.connected { "🟢 " } else { "🔴 " }; - 65
spans.push(Span::raw(conn_dot)); - 66
- 67
for (tab, label) in tabs { - 68
let is_active = self.active_tab == tab; - 69
let dot = if is_active { "● " } else { "○ " }; - 70
let style = if is_active { - 71
self.theme.style_tab_active() - 72
} else { - 73
self.theme.style_tab_inactive() - 74
}; - 75
- 76
spans.push(Span::styled(format!(" [ {dot}{label} ] "), style)); - 77
spans.push(Span::raw(" ")); - 78
} - 79
- 80
// Right-aligned telemetry badge — all real values from health. - 81
let right_text = if let Some(err) = &self.error { - 82
format!( - 83
"ERROR: {} • {} • {} • {} ", - 84
err, self.session_name, self.model_name, self.host_endpoint - 85
) - 86
} else { - 87
format!( - 88
"{} • {} • {} • ${:.3} ", - 89
self.session_name, self.model_name, self.host_endpoint, self.spend_usd - 90
) - 91
}; - 92
let right_style = if self.connected { - 93
self.theme.style_card() - 94
} else { - 95
self.theme.style_card().add_modifier(Modifier::DIM) - 96
}; - 97
let right_span = Span::styled(right_text, right_style.add_modifier(Modifier::DIM)); - 98
- 99
let left_para = Paragraph::new(Line::from(spans)).alignment(Alignment::Left); - 100
let right_para = Paragraph::new(Line::from(vec![right_span])).alignment(Alignment::Right); - 101
- 102
left_para.render(area, buf); - 103
right_para.render(area, buf); - 104
} - 105
} - 106
Indexing the workspace…
Vakyartha documentation is discovering safe artifacts, anchors, and source references.