use std::ops::Range;
use ratatui::{
style::Modifier,
text::{Line, Span},
};
use super::{display_width, side_width, FieldKey, StatusField, Theme, FIELD_SEP};
#[derive(Clone, Debug, PartialEq, Eq)]
pub(in crate::tui) struct StatusFieldHit {
pub(super) key: FieldKey,
pub(in crate::tui) columns: Range<usize>,
pub(in crate::tui) action: StatusClick,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(in crate::tui) enum StatusClick {
Command(&'static str),
PermissionMode,
}
pub(super) fn click_action(key: FieldKey) -> Option<StatusClick> {
match key {
FieldKey::Context | FieldKey::Cost | FieldKey::Rate => Some(StatusClick::Command("/info")),
FieldKey::Permission => Some(StatusClick::PermissionMode),
FieldKey::Computer => Some(StatusClick::Command("/computer")),
FieldKey::Provider | FieldKey::Model | FieldKey::Reasoning => {
Some(StatusClick::Command("/model"))
}
FieldKey::SignedOut | FieldKey::LoginHint => Some(StatusClick::Command("/login")),
FieldKey::Zen | FieldKey::NotSaved => None,
}
}
pub(super) fn status_fields_line(
left: &[StatusField],
right: &[StatusField],
width: usize,
hovered: Option<FieldKey>,
) -> (Line<'static>, Vec<StatusFieldHit>) {
let mut row = FieldRow {
hovered,
..FieldRow::default()
};
row.push_fields(left);
if !right.is_empty() {
let gap = width.saturating_sub(side_width(left) + side_width(right));
row.push(Span::styled(" ".repeat(gap), Theme::dim()));
row.push_fields(right);
}
let hits = row
.hits
.into_iter()
.filter_map(|mut hit| {
hit.columns = hit.columns.start.min(width)..hit.columns.end.min(width);
(!hit.columns.is_empty()).then_some(hit)
})
.collect();
(Line::from(row.spans), hits)
}
#[derive(Default)]
struct FieldRow {
hovered: Option<FieldKey>,
spans: Vec<Span<'static>>,
column: usize,
hits: Vec<StatusFieldHit>,
}
impl FieldRow {
fn push(&mut self, span: Span<'static>) {
self.column += display_width(span.content.as_ref());
self.spans.push(span);
}
fn push_fields(&mut self, fields: &[StatusField]) {
for (index, field) in fields.iter().enumerate() {
if index > 0 {
self.push(Span::styled(FIELD_SEP.to_string(), Theme::dim()));
}
let start = self.column;
let style = if field.action.is_some() && self.hovered == Some(field.key) {
field
.style
.add_modifier(Modifier::BOLD | Modifier::UNDERLINED)
} else {
field.style
};
self.push(Span::styled(field.text.clone(), style));
if let Some(action) = field.action.filter(|_| self.column > start) {
self.hits.push(StatusFieldHit {
key: field.key,
columns: start..self.column,
action,
});
}
}
}
}