ui/components/ai/
mode_selector.rs1use std::sync::Arc;
2
3use gpui::SharedString;
4
5use crate::prelude::*;
6use crate::{ContextMenu, DropdownMenu, DropdownStyle};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum SessionModeChoice {
13 Persistent,
15 Oneshot,
17}
18
19impl SessionModeChoice {
20 const ALL: [SessionModeChoice; 2] = [SessionModeChoice::Persistent, SessionModeChoice::Oneshot];
21
22 fn label(self) -> &'static str {
23 match self {
24 SessionModeChoice::Persistent => "Persistent",
25 SessionModeChoice::Oneshot => "One-shot",
26 }
27 }
28}
29
30#[derive(IntoElement, RegisterComponent)]
37pub struct ModeSelector {
38 id: ElementId,
39 current: Option<SessionModeChoice>,
40 on_select: Option<Arc<dyn Fn(SessionModeChoice, &mut Window, &mut App) + 'static>>,
41}
42
43impl ModeSelector {
44 pub fn new(id: impl Into<ElementId>, current: Option<SessionModeChoice>) -> Self {
45 Self {
46 id: id.into(),
47 current,
48 on_select: None,
49 }
50 }
51
52 pub fn on_select(
53 mut self,
54 handler: impl Fn(SessionModeChoice, &mut Window, &mut App) + 'static,
55 ) -> Self {
56 self.on_select = Some(Arc::new(handler));
57 self
58 }
59}
60
61impl RenderOnce for ModeSelector {
62 fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
63 let label: SharedString = self
64 .current
65 .map(SessionModeChoice::label)
66 .unwrap_or("Select mode")
67 .into();
68 let current = self.current;
69 let on_select = self.on_select.clone();
70
71 let menu = ContextMenu::build(window, cx, move |mut menu, _window, _cx| {
72 for mode in SessionModeChoice::ALL {
73 let is_current = current == Some(mode);
74 let on_select = on_select.clone();
75 menu = menu.toggleable_entry(
76 mode.label(),
77 is_current,
78 IconPosition::End,
79 None,
80 move |window, cx| {
81 if let Some(on_select) = &on_select {
82 on_select(mode, window, cx);
83 }
84 },
85 );
86 }
87 menu
88 });
89
90 DropdownMenu::new(self.id, label, menu).style(DropdownStyle::Subtle)
91 }
92}
93
94impl Component for ModeSelector {
95 fn scope() -> ComponentScope {
96 ComponentScope::Agent
97 }
98
99 fn name() -> &'static str {
100 "ModeSelector"
101 }
102
103 fn description() -> Option<&'static str> {
104 Some(
105 "A dropdown for picking whether an agent thread's session \
106 persists across requests or runs as a single one-shot exchange.",
107 )
108 }
109
110 fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
111 Some(
112 v_flex()
113 .gap_4()
114 .child(single_example(
115 "Persistent selected",
116 ModeSelector::new("mode-selector-1", Some(SessionModeChoice::Persistent))
117 .into_any_element(),
118 ))
119 .child(single_example(
120 "One-shot selected",
121 ModeSelector::new("mode-selector-2", Some(SessionModeChoice::Oneshot))
122 .into_any_element(),
123 ))
124 .child(single_example(
125 "No selection",
126 ModeSelector::new("mode-selector-3", None).into_any_element(),
127 ))
128 .into_any_element(),
129 )
130 }
131}