1use gpui::{Action, FocusHandle, prelude::*};
2use ui::{KeyBinding, ListItem, ListItemSpacing, prelude::*};
3
4#[derive(IntoElement)]
5pub struct ModelSelectorHeader {
6 title: SharedString,
7 has_border: bool,
8}
9
10impl ModelSelectorHeader {
11 pub fn new(title: impl Into<SharedString>, has_border: bool) -> Self {
12 Self {
13 title: title.into(),
14 has_border,
15 }
16 }
17}
18
19impl RenderOnce for ModelSelectorHeader {
20 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
21 div()
22 .px_2()
23 .pb_1()
24 .when(self.has_border, |this| {
25 this.mt_1()
26 .pt_2()
27 .border_t_1()
28 .border_color(cx.theme().colors().border_variant)
29 })
30 .child(
31 Label::new(self.title)
32 .size(LabelSize::XSmall)
33 .color(Color::Muted),
34 )
35 }
36}
37
38#[derive(IntoElement)]
39pub struct ModelSelectorListItem {
40 index: usize,
41 title: SharedString,
42 icon: Option<IconName>,
43 is_selected: bool,
44 is_focused: bool,
45}
46
47impl ModelSelectorListItem {
48 pub fn new(index: usize, title: impl Into<SharedString>) -> Self {
49 Self {
50 index,
51 title: title.into(),
52 icon: None,
53 is_selected: false,
54 is_focused: false,
55 }
56 }
57
58 pub fn icon(mut self, icon: IconName) -> Self {
59 self.icon = Some(icon);
60 self
61 }
62
63 pub fn is_selected(mut self, is_selected: bool) -> Self {
64 self.is_selected = is_selected;
65 self
66 }
67
68 pub fn is_focused(mut self, is_focused: bool) -> Self {
69 self.is_focused = is_focused;
70 self
71 }
72}
73
74impl RenderOnce for ModelSelectorListItem {
75 fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
76 let model_icon_color = if self.is_selected {
77 Color::Accent
78 } else {
79 Color::Muted
80 };
81
82 ListItem::new(self.index)
83 .inset(true)
84 .spacing(ListItemSpacing::Sparse)
85 .toggle_state(self.is_focused)
86 .child(
87 h_flex()
88 .w_full()
89 .gap_1p5()
90 .when_some(self.icon, |this, icon| {
91 this.child(
92 Icon::new(icon)
93 .color(model_icon_color)
94 .size(IconSize::Small),
95 )
96 })
97 .child(Label::new(self.title).truncate()),
98 )
99 .end_slot(div().pr_2().when(self.is_selected, |this| {
100 this.child(
101 Icon::new(IconName::Check)
102 .color(Color::Accent)
103 .size(IconSize::Small),
104 )
105 }))
106 }
107}
108
109#[derive(IntoElement)]
110pub struct ModelSelectorFooter {
111 action: Box<dyn Action>,
112 focus_handle: FocusHandle,
113}
114
115impl ModelSelectorFooter {
116 pub fn new(action: Box<dyn Action>, focus_handle: FocusHandle) -> Self {
117 Self {
118 action,
119 focus_handle,
120 }
121 }
122}
123
124impl RenderOnce for ModelSelectorFooter {
125 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
126 let action = self.action;
127 let focus_handle = self.focus_handle;
128
129 h_flex()
130 .w_full()
131 .p_1p5()
132 .border_t_1()
133 .border_color(cx.theme().colors().border_variant)
134 .child(
135 Button::new("configure", "Configure")
136 .full_width()
137 .style(ButtonStyle::Outlined)
138 .key_binding(
139 KeyBinding::for_action_in(action.as_ref(), &focus_handle, cx)
140 .map(|kb| kb.size(rems_from_px(12.))),
141 )
142 .on_click(move |_, window, cx| {
143 window.dispatch_action(action.boxed_clone(), cx);
144 }),
145 )
146 }
147}