Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T02:34:52.585Z 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

wsl_picker.rs

297 lines · 8.4 KB · rust
1use std::{path::PathBuf, sync::Arc};
2
3use gpui::{AppContext, DismissEvent, Entity, EventEmitter, Focusable, Subscription, Task};
4use picker::Picker;
5use remote::{RemoteConnectionOptions, WslConnectionOptions};
6use ui::{
7    App, Context, HighlightedLabel, Icon, IconName, InteractiveElement, ListItem, ParentElement,
8    Render, Styled, StyledExt, Toggleable, Window, div, h_flex, rems, v_flex,
9};
10use util::ResultExt as _;
11use workspace::{ModalView, MultiWorkspace};
12
13use crate::open_remote_project;
14
15#[derive(Clone, Debug)]
16pub struct WslDistroSelected {
17    pub secondary: bool,
18    pub distro: String,
19}
20
21#[derive(Clone, Debug)]
22pub struct WslPickerDismissed;
23
24pub(crate) struct WslPickerDelegate {
25    selected_index: usize,
26    distro_list: Option<Vec<String>>,
27    matches: Vec<fuzzy_nucleo::StringMatch>,
28}
29
30impl WslPickerDelegate {
31    pub fn new() -> Self {
32        WslPickerDelegate {
33            selected_index: 0,
34            distro_list: None,
35            matches: Vec::new(),
36        }
37    }
38
39    pub fn selected_distro(&self) -> Option<String> {
40        self.matches
41            .get(self.selected_index)
42            .map(|m| m.string.to_string())
43    }
44}
45
46impl WslPickerDelegate {
47    fn fetch_distros() -> anyhow::Result<Vec<String>> {
48        use anyhow::Context;
49        use windows_registry::CURRENT_USER;
50
51        let lxss_key = CURRENT_USER
52            .open("Software\\Microsoft\\Windows\\CurrentVersion\\Lxss")
53            .context("failed to get lxss wsl key")?;
54
55        let distros = lxss_key
56            .keys()
57            .context("failed to get wsl distros")?
58            .filter_map(|key| {
59                lxss_key
60                    .open(&key)
61                    .context("failed to open subkey for distro")
62                    .log_err()
63            })
64            .filter_map(|distro| distro.get_string("DistributionName").ok())
65            .collect::<Vec<_>>();
66
67        Ok(distros)
68    }
69}
70
71impl EventEmitter<WslDistroSelected> for Picker<WslPickerDelegate> {}
72
73impl EventEmitter<WslPickerDismissed> for Picker<WslPickerDelegate> {}
74
75impl picker::PickerDelegate for WslPickerDelegate {
76    type ListItem = ListItem;
77
78    fn name() -> &'static str {
79        "WSL-distor-picker"
80    }
81
82    fn match_count(&self) -> usize {
83        self.matches.len()
84    }
85
86    fn selected_index(&self) -> usize {
87        self.selected_index
88    }
89
90    fn set_selected_index(
91        &mut self,
92        ix: usize,
93        _window: &mut Window,
94        cx: &mut Context<Picker<Self>>,
95    ) {
96        self.selected_index = ix;
97        cx.notify();
98    }
99
100    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
101        Arc::from("Enter WSL distro name")
102    }
103
104    fn update_matches(
105        &mut self,
106        query: String,
107        _window: &mut Window,
108        _cx: &mut Context<Picker<Self>>,
109    ) -> Task<()> {
110        use fuzzy_nucleo::StringMatchCandidate;
111
112        let needs_fetch = self.distro_list.is_none();
113        if needs_fetch {
114            let distros = Self::fetch_distros().log_err();
115            self.distro_list = distros;
116        }
117
118        if let Some(distro_list) = &self.distro_list {
119            use ordered_float::OrderedFloat;
120
121            let candidates = distro_list
122                .iter()
123                .enumerate()
124                .map(|(id, distro)| StringMatchCandidate::new(id, distro))
125                .collect::<Vec<_>>();
126
127            let query = query.trim_start();
128            let case = fuzzy_nucleo::Case::smart_if_uppercase_in(query);
129            self.matches = fuzzy_nucleo::match_strings(
130                &candidates,
131                query,
132                case,
133                fuzzy_nucleo::LengthPenalty::On,
134                100,
135            );
136            self.matches.sort_unstable_by_key(|m| m.candidate_id);
137
138            self.selected_index = self
139                .matches
140                .iter()
141                .enumerate()
142                .rev()
143                .max_by_key(|(_, m)| OrderedFloat(m.score))
144                .map(|(index, _)| index)
145                .unwrap_or(0);
146        }
147
148        Task::ready(())
149    }
150
151    fn confirm(&mut self, secondary: bool, _window: &mut Window, cx: &mut Context<Picker<Self>>) {
152        if let Some(distro) = self.matches.get(self.selected_index) {
153            cx.emit(WslDistroSelected {
154                secondary,
155                distro: distro.string.to_string(),
156            });
157        }
158    }
159
160    fn dismissed(&mut self, _window: &mut Window, cx: &mut Context<Picker<Self>>) {
161        cx.emit(WslPickerDismissed);
162    }
163
164    fn render_match(
165        &self,
166        ix: usize,
167        selected: bool,
168        _: &mut Window,
169        _: &mut Context<Picker<Self>>,
170    ) -> Option<Self::ListItem> {
171        let matched = self.matches.get(ix)?;
172        Some(
173            ListItem::new(ix)
174                .toggle_state(selected)
175                .inset(true)
176                .spacing(ui::ListItemSpacing::Sparse)
177                .child(
178                    h_flex()
179                        .flex_grow_1()
180                        .gap_3()
181                        .child(Icon::new(IconName::Linux))
182                        .child(v_flex().child(HighlightedLabel::new(
183                            matched.string.clone(),
184                            matched.positions.clone(),
185                        ))),
186                ),
187        )
188    }
189}
190
191pub(crate) struct WslOpenModal {
192    paths: Vec<PathBuf>,
193    create_new_window: bool,
194    picker: Entity<Picker<WslPickerDelegate>>,
195    _subscriptions: [Subscription; 2],
196}
197
198impl WslOpenModal {
199    pub fn new(
200        paths: Vec<PathBuf>,
201        create_new_window: bool,
202        window: &mut Window,
203        cx: &mut Context<Self>,
204    ) -> Self {
205        let delegate = WslPickerDelegate::new();
206        let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx).embedded());
207
208        let selected = cx.subscribe_in(
209            &picker,
210            window,
211            |this, _, event: &WslDistroSelected, window, cx| {
212                this.confirm(&event.distro, event.secondary, window, cx);
213            },
214        );
215
216        let dismissed = cx.subscribe_in(
217            &picker,
218            window,
219            |this, _, _: &WslPickerDismissed, window, cx| {
220                this.cancel(&menu::Cancel, window, cx);
221            },
222        );
223
224        WslOpenModal {
225            paths,
226            create_new_window,
227            picker,
228            _subscriptions: [selected, dismissed],
229        }
230    }
231
232    fn confirm(
233        &mut self,
234        distro: &str,
235        secondary: bool,
236        window: &mut Window,
237        cx: &mut Context<Self>,
238    ) {
239        let app_state = workspace::AppState::global(cx);
240
241        let connection_options = RemoteConnectionOptions::Wsl(WslConnectionOptions {
242            distro_name: distro.to_string(),
243            user: None,
244        });
245
246        let replace_current_window = match self.create_new_window {
247            true => secondary,
248            false => !secondary,
249        };
250        let open_mode = if replace_current_window {
251            workspace::OpenMode::Activate
252        } else {
253            workspace::OpenMode::NewWindow
254        };
255
256        let paths = self.paths.clone();
257        let open_options = workspace::OpenOptions {
258            requesting_window: window.window_handle().downcast::<MultiWorkspace>(),
259            open_mode,
260            ..Default::default()
261        };
262
263        cx.emit(DismissEvent);
264        cx.spawn_in(window, async move |_, cx| {
265            open_remote_project(connection_options, paths, app_state, open_options, cx).await
266        })
267        .detach();
268    }
269
270    fn cancel(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context<Self>) {
271        cx.emit(DismissEvent);
272    }
273}
274
275impl ModalView for WslOpenModal {}
276
277impl Focusable for WslOpenModal {
278    fn focus_handle(&self, cx: &App) -> gpui::FocusHandle {
279        self.picker.focus_handle(cx)
280    }
281}
282
283impl EventEmitter<DismissEvent> for WslOpenModal {}
284
285impl Render for WslOpenModal {
286    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl ui::IntoElement {
287        div()
288            .on_mouse_down_out(cx.listener(|_, _, _, cx| cx.emit(DismissEvent)))
289            .on_action(cx.listener(Self::cancel))
290            .elevation_3(cx)
291            .w(rems(34.))
292            .flex_1()
293            .overflow_hidden()
294            .child(self.picker.clone())
295    }
296}
297
Served at tenant.openagents/omega Member data and write actions are omitted.