Skip to repository content298 lines · 9.5 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T06:06:25.889Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
model_selector_components.rs
1use gpui::{Action, ClickEvent, FocusHandle, prelude::*};
2use language_model::DisabledReason;
3use ui::{Chip, ElevationIndex, KeyBinding, ListItem, ListItemSpacing, Tooltip, prelude::*};
4use zed_actions::agent::ToggleModelSelector;
5
6use crate::CycleFavoriteModels;
7
8enum ModelIcon {
9 Name(IconName),
10 Path(SharedString),
11}
12
13#[derive(IntoElement)]
14pub struct ModelSelectorHeader {
15 title: SharedString,
16 has_border: bool,
17}
18
19impl ModelSelectorHeader {
20 pub fn new(title: impl Into<SharedString>, has_border: bool) -> Self {
21 Self {
22 title: title.into(),
23 has_border,
24 }
25 }
26}
27
28impl RenderOnce for ModelSelectorHeader {
29 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
30 div()
31 .px_2()
32 .pb_1()
33 .when(self.has_border, |this| {
34 this.mt_1()
35 .pt_2()
36 .border_t_1()
37 .border_color(cx.theme().colors().border_variant)
38 })
39 .child(
40 Label::new(self.title)
41 .size(LabelSize::XSmall)
42 .color(Color::Muted),
43 )
44 }
45}
46
47#[derive(IntoElement)]
48pub struct ModelSelectorListItem {
49 index: usize,
50 title: SharedString,
51 icon: Option<ModelIcon>,
52 is_selected: bool,
53 is_focused: bool,
54 is_latest: bool,
55 is_favorite: bool,
56 disabled: Option<DisabledReason>,
57 on_toggle_favorite: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
58 cost_info: Option<SharedString>,
59}
60
61impl ModelSelectorListItem {
62 pub fn new(index: usize, title: impl Into<SharedString>) -> Self {
63 Self {
64 index,
65 title: title.into(),
66 icon: None,
67 is_selected: false,
68 is_focused: false,
69 is_latest: false,
70 is_favorite: false,
71 disabled: None,
72 on_toggle_favorite: None,
73 cost_info: None,
74 }
75 }
76
77 pub fn icon(mut self, icon: IconName) -> Self {
78 self.icon = Some(ModelIcon::Name(icon));
79 self
80 }
81
82 pub fn icon_path(mut self, path: SharedString) -> Self {
83 self.icon = Some(ModelIcon::Path(path));
84 self
85 }
86
87 pub fn is_selected(mut self, is_selected: bool) -> Self {
88 self.is_selected = is_selected;
89 self
90 }
91
92 pub fn disabled(mut self, disabled: Option<DisabledReason>) -> Self {
93 self.disabled = disabled;
94 self
95 }
96
97 pub fn is_focused(mut self, is_focused: bool) -> Self {
98 self.is_focused = is_focused;
99 self
100 }
101
102 pub fn is_latest(mut self, is_latest: bool) -> Self {
103 self.is_latest = is_latest;
104 self
105 }
106
107 pub fn is_favorite(mut self, is_favorite: bool) -> Self {
108 self.is_favorite = is_favorite;
109 self
110 }
111
112 pub fn on_toggle_favorite(
113 mut self,
114 handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
115 ) -> Self {
116 self.on_toggle_favorite = Some(Box::new(handler));
117 self
118 }
119
120 pub fn cost_info(mut self, cost_info: Option<SharedString>) -> Self {
121 self.cost_info = cost_info;
122 self
123 }
124}
125
126impl RenderOnce for ModelSelectorListItem {
127 fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
128 let is_disabled = self.disabled.is_some();
129
130 let model_icon_color = if self.is_selected {
131 Color::Accent
132 } else if is_disabled {
133 Color::Disabled
134 } else {
135 Color::Muted
136 };
137
138 let is_favorite = self.is_favorite;
139
140 ListItem::new(self.index)
141 .inset(true)
142 .spacing(ListItemSpacing::Sparse)
143 .toggle_state(self.is_focused)
144 .when_some(self.disabled, |this, disabled_reason| {
145 this.disabled(true)
146 .tooltip(Tooltip::text(disabled_reason.0))
147 })
148 .child(
149 h_flex()
150 .w_full()
151 .gap_1p5()
152 .when_some(self.icon, |this, icon| {
153 this.child(
154 match icon {
155 ModelIcon::Name(icon_name) => Icon::new(icon_name),
156 ModelIcon::Path(icon_path) => Icon::from_external_svg(icon_path),
157 }
158 .color(model_icon_color)
159 .size(IconSize::Small),
160 )
161 })
162 .child(
163 Label::new(self.title)
164 .when(is_disabled, |this| this.color(Color::Disabled))
165 .truncate(),
166 )
167 .when(self.is_latest, |parent| parent.child(Chip::new("Latest")))
168 .when_some(self.cost_info, |this, cost_info| {
169 let tooltip_text = if cost_info.ends_with('×') {
170 format!("Cost Multiplier: {}", cost_info)
171 } else if cost_info.contains('$') {
172 format!("Cost per Million Tokens: {}", cost_info)
173 } else {
174 format!("Cost: {}", cost_info)
175 };
176
177 this.child(Chip::new(cost_info).tooltip(Tooltip::text(tooltip_text)))
178 }),
179 )
180 .end_slot(
181 h_flex()
182 .pr_2()
183 .gap_1p5()
184 .when(self.is_selected, |this| {
185 this.child(Icon::new(IconName::Check).color(Color::Accent))
186 })
187 .when(is_disabled, |this| {
188 this.child(Icon::new(IconName::Info).color(Color::Muted))
189 }),
190 )
191 .when(!is_disabled, |this| {
192 this.end_slot_on_hover(div().pr_1p5().when_some(self.on_toggle_favorite, {
193 |this, handle_click| {
194 let (icon, color, tooltip) = if is_favorite {
195 (IconName::StarFilled, Color::Accent, "Unfavorite Model")
196 } else {
197 (IconName::Star, Color::Default, "Favorite Model")
198 };
199 this.child(
200 IconButton::new(("toggle-favorite", self.index), icon)
201 .layer(ElevationIndex::ElevatedSurface)
202 .icon_color(color)
203 .icon_size(IconSize::Small)
204 .tooltip(Tooltip::text(tooltip))
205 .on_click(move |event, window, cx| {
206 (handle_click)(event, window, cx)
207 }),
208 )
209 }
210 }))
211 })
212 }
213}
214
215#[derive(IntoElement)]
216pub struct ModelSelectorFooter {
217 action: Box<dyn Action>,
218 focus_handle: FocusHandle,
219}
220
221impl ModelSelectorFooter {
222 pub fn new(action: Box<dyn Action>, focus_handle: FocusHandle) -> Self {
223 Self {
224 action,
225 focus_handle,
226 }
227 }
228}
229
230impl RenderOnce for ModelSelectorFooter {
231 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
232 let action = self.action;
233 let focus_handle = self.focus_handle;
234
235 h_flex()
236 .w_full()
237 .p_1p5()
238 .border_t_1()
239 .border_color(cx.theme().colors().border_variant)
240 .child(
241 Button::new("configure", "Configure")
242 .full_width()
243 .style(ButtonStyle::Outlined)
244 .key_binding(
245 KeyBinding::for_action_in(action.as_ref(), &focus_handle, cx)
246 .map(|kb| kb.size(rems_from_px(12.))),
247 )
248 .on_click(move |_, window, cx| {
249 window.dispatch_action(action.boxed_clone(), cx);
250 }),
251 )
252 }
253}
254
255#[derive(IntoElement)]
256pub struct ModelSelectorTooltip {
257 show_cycle_row: bool,
258}
259
260impl ModelSelectorTooltip {
261 pub fn new() -> Self {
262 Self {
263 show_cycle_row: true,
264 }
265 }
266
267 pub fn show_cycle_row(mut self, show: bool) -> Self {
268 self.show_cycle_row = show;
269 self
270 }
271}
272
273impl RenderOnce for ModelSelectorTooltip {
274 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
275 v_flex()
276 .gap_1()
277 .child(
278 h_flex()
279 .gap_2()
280 .justify_between()
281 .child(Label::new("Change Model"))
282 .child(KeyBinding::for_action(&ToggleModelSelector, cx)),
283 )
284 .when(self.show_cycle_row, |this| {
285 this.child(
286 h_flex()
287 .pt_1()
288 .gap_2()
289 .border_t_1()
290 .border_color(cx.theme().colors().border_variant)
291 .justify_between()
292 .child(Label::new("Cycle Favorite Models"))
293 .child(KeyBinding::for_action(&CycleFavoriteModels, cx)),
294 )
295 })
296 }
297}
298