Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T04:35:32.049Z 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

theme_picker.rs

185 lines · 5.5 KB · rust
1use std::sync::Arc;
2
3use fuzzy::{StringMatch, StringMatchCandidate};
4use gpui::{AnyElement, App, Context, DismissEvent, SharedString, Task, Window};
5use picker::{Picker, PickerDelegate};
6use theme::ThemeRegistry;
7use ui::{ListItem, ListItemSpacing, prelude::*};
8
9type ThemePicker = Picker<ThemePickerDelegate>;
10
11pub struct ThemePickerDelegate {
12    themes: Vec<SharedString>,
13    filtered_themes: Vec<StringMatch>,
14    selected_index: usize,
15    current_theme: SharedString,
16    on_theme_changed: Arc<dyn Fn(SharedString, &mut Window, &mut App) + 'static>,
17}
18
19impl ThemePickerDelegate {
20    fn new(
21        current_theme: SharedString,
22        on_theme_changed: impl Fn(SharedString, &mut Window, &mut App) + 'static,
23        cx: &mut Context<ThemePicker>,
24    ) -> Self {
25        let theme_registry = ThemeRegistry::global(cx);
26
27        let themes = theme_registry.list_names();
28        let selected_index = themes
29            .iter()
30            .position(|theme| *theme == current_theme)
31            .unwrap_or(0);
32
33        let filtered_themes = themes
34            .iter()
35            .enumerate()
36            .map(|(index, theme)| StringMatch {
37                candidate_id: index,
38                string: theme.to_string(),
39                positions: Vec::new(),
40                score: 0.0,
41            })
42            .collect();
43
44        Self {
45            themes,
46            filtered_themes,
47            selected_index,
48            current_theme,
49            on_theme_changed: Arc::new(on_theme_changed),
50        }
51    }
52}
53
54impl PickerDelegate for ThemePickerDelegate {
55    type ListItem = AnyElement;
56
57    fn name() -> &'static str {
58        "theme picker"
59    }
60
61    fn match_count(&self) -> usize {
62        self.filtered_themes.len()
63    }
64
65    fn selected_index(&self) -> usize {
66        self.selected_index
67    }
68
69    fn set_selected_index(&mut self, ix: usize, _: &mut Window, cx: &mut Context<ThemePicker>) {
70        self.selected_index = ix.min(self.filtered_themes.len().saturating_sub(1));
71        cx.notify();
72    }
73
74    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
75        "Search theme…".into()
76    }
77
78    fn update_matches(
79        &mut self,
80        query: String,
81        _window: &mut Window,
82        cx: &mut Context<ThemePicker>,
83    ) -> Task<()> {
84        let themes = self.themes.clone();
85        let current_theme = self.current_theme.clone();
86
87        let matches: Vec<StringMatch> = if query.is_empty() {
88            themes
89                .iter()
90                .enumerate()
91                .map(|(index, theme)| StringMatch {
92                    candidate_id: index,
93                    string: theme.to_string(),
94                    positions: Vec::new(),
95                    score: 0.0,
96                })
97                .collect()
98        } else {
99            let _candidates: Vec<StringMatchCandidate> = themes
100                .iter()
101                .enumerate()
102                .map(|(id, theme)| StringMatchCandidate::new(id, theme.as_ref()))
103                .collect();
104
105            themes
106                .iter()
107                .enumerate()
108                .filter(|(_, theme)| theme.to_lowercase().contains(&query.to_lowercase()))
109                .map(|(index, theme)| StringMatch {
110                    candidate_id: index,
111                    string: theme.to_string(),
112                    positions: Vec::new(),
113                    score: 0.0,
114                })
115                .collect()
116        };
117
118        let selected_index = if query.is_empty() {
119            themes
120                .iter()
121                .position(|theme| *theme == current_theme)
122                .unwrap_or(0)
123        } else {
124            matches
125                .iter()
126                .position(|m| themes[m.candidate_id] == current_theme)
127                .unwrap_or(0)
128        };
129
130        self.filtered_themes = matches;
131        self.selected_index = selected_index;
132        cx.notify();
133
134        Task::ready(())
135    }
136
137    fn confirm(&mut self, _secondary: bool, window: &mut Window, cx: &mut Context<ThemePicker>) {
138        if let Some(theme_match) = self.filtered_themes.get(self.selected_index) {
139            let theme = theme_match.string.clone();
140            (self.on_theme_changed)(theme.into(), window, cx);
141        }
142    }
143
144    fn dismissed(&mut self, window: &mut Window, cx: &mut Context<ThemePicker>) {
145        cx.defer_in(window, |picker, window, cx| {
146            picker.set_query("", window, cx);
147        });
148        cx.emit(DismissEvent);
149    }
150
151    fn render_match(
152        &self,
153        ix: usize,
154        selected: bool,
155        _window: &mut Window,
156        _cx: &mut Context<ThemePicker>,
157    ) -> Option<Self::ListItem> {
158        let theme_match = self.filtered_themes.get(ix)?;
159
160        Some(
161            ListItem::new(ix)
162                .inset(true)
163                .spacing(ListItemSpacing::Sparse)
164                .toggle_state(selected)
165                .child(Label::new(theme_match.string.clone()))
166                .into_any_element(),
167        )
168    }
169}
170
171pub fn theme_picker(
172    current_theme: SharedString,
173    on_theme_changed: impl Fn(SharedString, &mut Window, &mut App) + 'static,
174    window: &mut Window,
175    cx: &mut Context<ThemePicker>,
176) -> ThemePicker {
177    let delegate = ThemePickerDelegate::new(current_theme, on_theme_changed, cx);
178
179    Picker::uniform_list(delegate, window, cx)
180        .show_scrollbar(true)
181        .initial_width(rems_from_px(210.))
182        .max_height(rems(18.))
183        .popover()
184}
185
Served at tenant.openagents/omega Member data and write actions are omitted.