use super::App;
use super::{Span, Style};
use crate::tui::app::{ActivePanel, ModelsMode};
use crate::tui::colors::*;
use ratatui::text::Line;
fn hint_nav() -> &'static str {
crate::t!("hints.nav")
}
fn hint_panels() -> &'static str {
crate::t!("hints.panels")
}
fn hint_about() -> &'static str {
crate::t!("hints.about")
}
const HINT_SEP: &str = " ";
pub fn render_hints(app: &App) -> Vec<Span<'static>> {
let y = Style::default().fg(ACCENT);
let c = Style::default().fg(CYAN);
let r = Style::default().fg(RED);
if matches!(
app.ui.global_mode,
crate::tui::app::GlobalMode::BackendPicker { .. }
) {
return vec![
Span::styled(crate::t!("hints.del"), r),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.exit"), c),
];
}
match &app.models_mode {
ModelsMode::Search {
sort_by: _,
show_readme: _,
loading,
..
} => {
let mut parts = vec![
Span::styled(crate::t!("hints.exit"), c),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.files"), y),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.readme"), y),
];
if *loading {
parts.push(Span::raw(HINT_SEP));
parts.push(Span::styled(
crate::t!("hints.loading"),
Style::default().fg(ACCENT),
));
}
parts
}
ModelsMode::Files { .. } => {
vec![
Span::styled(crate::t!("hints.gguf"), c),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.download"), y),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.back"), c),
]
}
ModelsMode::List { sort_by: _ } => {
if app.ui.active_panel == ActivePanel::LlmSettings {
let mut parts = vec![
Span::styled(hint_nav(), c),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.save"), y),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.reset"), y),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.toggle"), y),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.toggle_field"), y),
Span::raw(HINT_SEP),
];
parts.push(Span::styled(crate::t!("hints.expert"), y));
parts.push(Span::raw(HINT_SEP));
parts.push(Span::styled(crate::t!("hints.profiles"), y));
parts.push(Span::raw(HINT_SEP));
parts.push(Span::styled(hint_panels(), c));
parts.push(Span::raw(HINT_SEP));
parts.push(Span::styled(hint_about(), c));
parts
} else {
match app.ui.active_panel {
ActivePanel::Models => {
let parts = vec![
Span::styled(crate::t!("hints.gguf"), c),
Span::raw(HINT_SEP),
Span::styled(hint_panels(), c),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.filter"), y),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.load_unload"), y),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.delete"), y),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.help"), c),
Span::raw(HINT_SEP),
Span::styled(hint_about(), c),
];
parts
}
ActivePanel::Log => {
if app.log.log_expanded {
vec![
Span::styled(crate::t!("hints.scroll"), c),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.follow"), c),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.collapse"), c),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.top_bottom"), c),
]
} else {
vec![
Span::styled(crate::t!("hints.follow"), c),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.collapse"), c),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.top_bottom"), c),
Span::raw(HINT_SEP),
Span::styled(hint_panels(), c),
]
}
}
ActivePanel::ServerSettings => {
let parts = vec![
Span::styled(hint_nav(), c),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.save"), y),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.toggle"), y),
Span::raw(HINT_SEP),
Span::styled(hint_panels(), c),
Span::raw(HINT_SEP),
Span::styled(hint_about(), c),
];
parts
}
ActivePanel::Profiles => {
vec![
Span::styled(hint_nav(), c),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.apply"), y),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.save_profile"), c),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.done"), c),
Span::raw(HINT_SEP),
Span::styled(hint_panels(), c),
Span::raw(HINT_SEP),
Span::styled(hint_about(), c),
]
}
ActivePanel::SystemPromptPresets => {
vec![
Span::styled(hint_nav(), c),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.apply"), y),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.edit"), c),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.new"), c),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.done"), c),
Span::raw(HINT_SEP),
Span::styled(hint_panels(), c),
Span::raw(HINT_SEP),
Span::styled(hint_about(), c),
]
}
ActivePanel::SearchReadme => {
vec![
Span::styled(hint_nav(), c),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.collapse"), c),
Span::raw(HINT_SEP),
Span::styled(hint_panels(), c),
Span::raw(HINT_SEP),
Span::styled(hint_about(), c),
]
}
ActivePanel::Downloads => {
vec![
Span::styled(hint_nav(), c),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.pause"), y),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.cancel_download"), y),
Span::raw(HINT_SEP),
Span::styled(hint_panels(), c),
]
}
_ => {
vec![
Span::styled(hint_panels(), c),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.filter"), y),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.help"), c),
Span::raw(HINT_SEP),
Span::styled(hint_about(), c),
]
}
}
}
}
ModelsMode::BenchTune => {
if app.bench_tune.bench_tune_progress.is_some()
&& matches!(
app.bench_tune.bench_tune_progress.as_ref().unwrap(),
crate::models::BenchTuneProgress::Running { .. }
)
{
vec![
Span::styled(crate::t!("hints.stop"), r),
Span::raw(HINT_SEP),
Span::styled(hint_panels(), c),
]
} else if !app.bench_tune.bench_tune_results.is_empty() {
vec![
Span::styled(crate::t!("hints.view_output"), y),
Span::raw(HINT_SEP),
Span::styled(crate::t!("hints.stop"), r),
Span::raw(HINT_SEP),
Span::styled(hint_panels(), c),
]
} else {
vec![
Span::styled(crate::t!("hints.stop"), r),
Span::raw(HINT_SEP),
Span::styled(hint_panels(), c),
]
}
}
}
}
pub fn render_hints_line(app: &App, panel_area: ratatui::layout::Rect) -> Line<'static> {
let hints = render_hints(app);
let hints_width: usize = hints.iter().map(|s| s.width()).sum();
let available_width = panel_area.width.saturating_sub(10) as usize;
if hints_width <= available_width {
return Line::from(hints);
}
let mut truncated: Vec<Span> = Vec::new();
let mut total_width: usize = 0;
for span in &hints {
let span_width = span.width();
if total_width + span_width <= available_width {
total_width += span_width;
truncated.push(span.clone());
} else if total_width > 0 {
truncated.push(Span::styled(" ...", Style::default().fg(DIM_GRAY)));
break;
}
}
if truncated.is_empty() {
truncated.push(Span::styled("...", Style::default().fg(DIM_GRAY)));
}
Line::from(truncated)
}