Skip to repository content

tenant.openagents/omega

No repository description is available.

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

ui_prompt.rs

215 lines · 7.3 KB · rust
1use gpui::{
2    App, Decorations, Entity, EventEmitter, FocusHandle, Focusable, PromptButton, PromptHandle,
3    PromptLevel, PromptResponse, RenderablePromptHandle, SharedString, TextStyleRefinement, Window,
4    div, prelude::*,
5};
6use markdown::{Markdown, MarkdownElement, MarkdownStyle};
7use settings::{Settings, SettingsStore};
8use theme::ClientDecorationsExt;
9use theme_settings::ThemeSettings;
10use ui::{FluentBuilder, TintColor, prelude::*};
11use workspace::WorkspaceSettings;
12
13pub fn init(cx: &mut App) {
14    process_settings(cx);
15
16    cx.observe_global::<SettingsStore>(process_settings)
17        .detach();
18}
19
20fn process_settings(cx: &mut App) {
21    let settings = WorkspaceSettings::get_global(cx);
22    if settings.use_system_prompts && cfg!(not(any(target_os = "linux", target_os = "freebsd"))) {
23        cx.reset_prompt_builder();
24    } else {
25        cx.set_prompt_builder(zed_prompt_renderer);
26    }
27}
28
29/// Use this function in conjunction with [App::set_prompt_builder] to force
30/// GPUI to use the internal prompt system.
31fn zed_prompt_renderer(
32    level: PromptLevel,
33    message: &str,
34    detail: Option<&str>,
35    actions: &[PromptButton],
36    handle: PromptHandle,
37    window: &mut Window,
38    cx: &mut App,
39) -> RenderablePromptHandle {
40    let renderer = cx.new({
41        |cx| ZedPromptRenderer {
42            _level: level,
43            message: cx.new(|cx| Markdown::new(SharedString::new(message), None, None, cx)),
44            actions: actions.iter().map(|a| a.label().to_string()).collect(),
45            focus: cx.focus_handle(),
46            active_action_id: 0,
47            detail: detail
48                .filter(|text| !text.is_empty())
49                .map(|text| cx.new(|cx| Markdown::new(SharedString::new(text), None, None, cx))),
50        }
51    });
52
53    handle.with_view(renderer, window, cx)
54}
55
56pub struct ZedPromptRenderer {
57    _level: PromptLevel,
58    message: Entity<Markdown>,
59    actions: Vec<String>,
60    focus: FocusHandle,
61    active_action_id: usize,
62    detail: Option<Entity<Markdown>>,
63}
64
65impl ZedPromptRenderer {
66    fn confirm(&mut self, _: &menu::Confirm, _window: &mut Window, cx: &mut Context<Self>) {
67        cx.emit(PromptResponse(self.active_action_id));
68    }
69
70    fn cancel(&mut self, _: &menu::Cancel, _window: &mut Window, cx: &mut Context<Self>) {
71        if let Some(ix) = self.actions.iter().position(|a| a == "Cancel") {
72            cx.emit(PromptResponse(ix));
73        }
74    }
75
76    fn select_first(
77        &mut self,
78        _: &menu::SelectFirst,
79        _window: &mut Window,
80        cx: &mut Context<Self>,
81    ) {
82        self.active_action_id = self.actions.len().saturating_sub(1);
83        cx.notify();
84    }
85
86    fn select_last(&mut self, _: &menu::SelectLast, _window: &mut Window, cx: &mut Context<Self>) {
87        self.active_action_id = 0;
88        cx.notify();
89    }
90
91    fn select_next(&mut self, _: &menu::SelectNext, _window: &mut Window, cx: &mut Context<Self>) {
92        self.active_action_id = (self.active_action_id + 1) % self.actions.len();
93        cx.notify();
94    }
95
96    fn select_previous(
97        &mut self,
98        _: &menu::SelectPrevious,
99        _window: &mut Window,
100        cx: &mut Context<Self>,
101    ) {
102        if self.active_action_id > 0 {
103            self.active_action_id -= 1;
104        } else {
105            self.active_action_id = self.actions.len().saturating_sub(1);
106        }
107        cx.notify();
108    }
109}
110
111impl Render for ZedPromptRenderer {
112    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
113        let settings = ThemeSettings::get_global(cx);
114
115        let dialog = v_flex()
116            .key_context("Prompt")
117            .cursor_default()
118            .track_focus(&self.focus)
119            .on_action(cx.listener(Self::confirm))
120            .on_action(cx.listener(Self::cancel))
121            .on_action(cx.listener(Self::select_next))
122            .on_action(cx.listener(Self::select_previous))
123            .on_action(cx.listener(Self::select_first))
124            .on_action(cx.listener(Self::select_last))
125            .w_80()
126            .p_4()
127            .gap_4()
128            .elevation_3(cx)
129            .overflow_hidden()
130            .font_family(settings.ui_font.family.clone())
131            .child(div().w_full().child(MarkdownElement::new(
132                self.message.clone(),
133                markdown_style(true, window, cx),
134            )))
135            .children(self.detail.clone().map(|detail| {
136                div().w_full().text_xs().child(MarkdownElement::new(
137                    detail,
138                    markdown_style(false, window, cx),
139                ))
140            }))
141            .child(
142                v_flex()
143                    .gap_1()
144                    .children(self.actions.iter().enumerate().map(|(ix, action)| {
145                        Button::new(ix, action.clone())
146                            .full_width()
147                            .style(ButtonStyle::Outlined)
148                            .when(ix == self.active_action_id, |s| {
149                                s.style(ButtonStyle::Tinted(TintColor::Accent))
150                            })
151                            .tab_index(ix as isize)
152                            .on_click(cx.listener(move |_, _, _window, cx| {
153                                cx.emit(PromptResponse(ix));
154                            }))
155                    })),
156            );
157
158        let decorations = window.window_decorations();
159        let inset = window.client_inset().unwrap_or(Pixels::ZERO);
160
161        div().size_full().child(
162            v_flex()
163                .occlude()
164                .absolute()
165                .inset_0()
166                .bg(gpui::black().opacity(0.2))
167                .map(|this| match decorations {
168                    Decorations::Server => this,
169                    Decorations::Client { tiling } => this
170                        .when(!tiling.top, |this| this.top(inset))
171                        .when(!tiling.bottom, |this| this.bottom(inset))
172                        .when(!tiling.left, |this| this.left(inset))
173                        .when(!tiling.right, |this| this.right(inset))
174                        .rounded_client_corners(tiling),
175                })
176                .items_center()
177                .justify_center()
178                .child(dialog),
179        )
180    }
181}
182
183fn markdown_style(main_message: bool, window: &Window, cx: &App) -> MarkdownStyle {
184    let mut base_text_style = window.text_style();
185    let settings = ThemeSettings::get_global(cx);
186    let font_size = settings.ui_font_size(cx).into();
187
188    let color = if main_message {
189        Color::Default.color(cx)
190    } else {
191        Color::Muted.color(cx)
192    };
193
194    base_text_style.refine(&TextStyleRefinement {
195        font_family: Some(settings.ui_font.family.clone()),
196        font_size: Some(font_size),
197        color: Some(color),
198        ..Default::default()
199    });
200
201    MarkdownStyle {
202        base_text_style,
203        selection_background_color: cx.theme().colors().element_selection_background,
204        ..Default::default()
205    }
206}
207
208impl EventEmitter<PromptResponse> for ZedPromptRenderer {}
209
210impl Focusable for ZedPromptRenderer {
211    fn focus_handle(&self, _: &crate::App) -> FocusHandle {
212        self.focus.clone()
213    }
214}
215
Served at tenant.openagents/omega Member data and write actions are omitted.