rab/agent/ui/
model_selector.rs1use crate::agent::ui::theme::RabTheme;
2use crate::tui::Component;
3use crate::tui::components::select_list::{SelectItem, SelectList, SelectListTheme};
4
5pub struct ModelSelector {
7 select_list: SelectList,
8 theme: RabTheme,
9 pub selected_model: Option<String>,
10}
11
12impl ModelSelector {
13 pub fn new(models: Vec<String>, current_model: &str, theme: &RabTheme) -> Self {
14 let items: Vec<SelectItem> = models
15 .iter()
16 .map(|m| {
17 let display = m.strip_prefix("opencode_go::").unwrap_or(m);
18 SelectItem::new(m.clone(), display.to_string())
19 })
20 .collect();
21
22 let current_index = models
23 .iter()
24 .position(|m| m == current_model || format!("opencode_go::{}", m) == current_model)
25 .unwrap_or(0);
26
27 let list_theme = SelectListTheme {
28 selected_prefix: Box::new(|s| format!("\x1b[38;2;138;190;183m\x1b[1m> {}\x1b[0m", s)),
29 selected_text: Box::new(|s| format!("\x1b[38;2;138;190;183m\x1b[1m{}\x1b[0m", s)),
30 normal_text: Box::new(|s| format!(" \x1b[38;2;212;212;212m{}\x1b[0m", s)),
31 description: Box::new(|s| format!("\x1b[38;2;128;128;128m{}\x1b[0m", s)),
32 scroll_info: crate::tui::Style::new().fg("\x1b[38;2;80;80;80m".to_string()),
33 no_match: crate::tui::Style::new().fg("\x1b[38;2;255;255;0m".to_string()),
34 hint: crate::tui::Style::new().fg("\x1b[38;2;128;128;128m".to_string()),
35 };
36
37 let max_visible = models.len().clamp(5, 20);
38 let mut select_list = SelectList::new(items, max_visible, list_theme, None);
39 select_list = select_list.with_search();
40
41 for _ in 0..current_index {
43 select_list.handle_input(&crossterm::event::KeyEvent::new(
44 crossterm::event::KeyCode::Down,
45 crossterm::event::KeyModifiers::NONE,
46 ));
47 }
48
49 Self {
50 select_list,
51 theme: theme.clone(),
52 selected_model: None,
53 }
54 }
55}
56
57impl Component for ModelSelector {
58 fn render(&mut self, width: usize) -> Vec<String> {
59 let mut lines = vec![
60 self.theme.bold(&self.theme.accent(" Select Model")),
61 String::new(),
62 self.theme.dim(" Type to search…"),
63 String::new(),
64 ];
65
66 lines.extend(self.select_list.render(width));
68
69 lines
70 }
71
72 fn handle_input(&mut self, key: &crossterm::event::KeyEvent) -> bool {
73 let kb = crate::tui::keybindings::get_keybindings();
74
75 if kb.matches(key, crate::tui::keybindings::ACTION_SELECT_CONFIRM) {
76 if let Some(item) = self.select_list.selected_item() {
77 self.selected_model = Some(item.value.clone());
78 }
79 return true;
80 }
81
82 if kb.matches(key, crate::tui::keybindings::ACTION_SELECT_CANCEL) {
83 self.selected_model = None;
84 return true;
85 }
86
87 self.select_list.handle_input(key)
88 }
89}