Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T05:03:44.264Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

model_selector_components.rs

298 lines · 9.5 KB · rust
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
Served at tenant.openagents/omega Member data and write actions are omitted.