Skip to repository content

tenant.openagents/omega

No repository description is available.

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

agent_notification.rs

200 lines · 7.4 KB · rust
1use gpui::{
2    App, Context, EventEmitter, IntoElement, PlatformDisplay, Size, Window,
3    WindowBackgroundAppearance, WindowBounds, WindowDecorations, WindowKind, WindowOptions,
4    linear_color_stop, linear_gradient, point,
5};
6use release_channel::ReleaseChannel;
7use std::rc::Rc;
8use ui::{Render, prelude::*};
9
10pub struct AgentNotification {
11    title: SharedString,
12    caption: Option<SharedString>,
13    icon: IconName,
14    project_name: Option<SharedString>,
15}
16
17impl AgentNotification {
18    pub fn new(
19        title: impl Into<SharedString>,
20        caption: Option<SharedString>,
21        icon: IconName,
22        project_name: Option<impl Into<SharedString>>,
23    ) -> Self {
24        Self {
25            title: title.into(),
26            caption: caption,
27            icon,
28            project_name: project_name.map(|name| name.into()),
29        }
30    }
31
32    pub fn window_options(screen: Rc<dyn PlatformDisplay>, cx: &App) -> WindowOptions {
33        let size = Size {
34            width: px(450.),
35            height: px(72.),
36        };
37
38        let notification_margin_width = px(16.);
39        let notification_margin_height = px(-48.);
40
41        let bounds = gpui::Bounds::<Pixels> {
42            origin: screen.bounds().top_right()
43                - point(
44                    size.width + notification_margin_width,
45                    notification_margin_height,
46                ),
47            size,
48        };
49
50        let app_id = ReleaseChannel::global(cx).app_id();
51
52        WindowOptions {
53            window_bounds: Some(WindowBounds::Windowed(bounds)),
54            titlebar: None,
55            focus: false,
56            show: true,
57            kind: WindowKind::PopUp,
58            is_movable: false,
59            display_id: Some(screen.id()),
60            window_background: WindowBackgroundAppearance::Transparent,
61            app_id: Some(app_id.to_owned()),
62            window_min_size: None,
63            window_decorations: Some(WindowDecorations::Client),
64            tabbing_identifier: None,
65            ..Default::default()
66        }
67    }
68}
69
70pub enum AgentNotificationEvent {
71    Accepted,
72    Dismissed,
73}
74
75impl EventEmitter<AgentNotificationEvent> for AgentNotification {}
76
77impl AgentNotification {
78    pub fn accept(&mut self, cx: &mut Context<Self>) {
79        cx.emit(AgentNotificationEvent::Accepted);
80    }
81
82    pub fn dismiss(&mut self, cx: &mut Context<Self>) {
83        cx.emit(AgentNotificationEvent::Dismissed);
84    }
85}
86
87impl Render for AgentNotification {
88    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
89        let ui_font = theme_settings::setup_ui_font(window, cx);
90        let line_height = window.line_height();
91
92        let bg = cx.theme().colors().elevated_surface_background;
93        let gradient_overflow = || {
94            div()
95                .h_full()
96                .absolute()
97                .w_8()
98                .bottom_0()
99                .right_0()
100                .bg(linear_gradient(
101                    90.,
102                    linear_color_stop(bg, 1.),
103                    linear_color_stop(bg.opacity(0.2), 0.),
104                ))
105        };
106
107        h_flex()
108            .id("agent-notification")
109            .size_full()
110            .p_3()
111            .gap_4()
112            .justify_between()
113            .elevation_3(cx)
114            .text_ui(cx)
115            .font(ui_font)
116            .border_color(cx.theme().colors().border)
117            .rounded_xl()
118            .child(
119                h_flex()
120                    .items_start()
121                    .gap_2()
122                    .flex_1()
123                    .child(
124                        h_flex().h(line_height).justify_center().child(
125                            Icon::new(self.icon)
126                                .color(Color::Muted)
127                                .size(IconSize::Small),
128                        ),
129                    )
130                    .child(
131                        v_flex()
132                            .flex_1()
133                            .max_w(px(300.))
134                            .child(
135                                div()
136                                    .relative()
137                                    .text_size(px(14.))
138                                    .text_color(cx.theme().colors().text)
139                                    .truncate()
140                                    .child(self.title.clone())
141                                    .child(gradient_overflow()),
142                            )
143                            .child(
144                                h_flex()
145                                    .relative()
146                                    .gap_1p5()
147                                    .text_size(px(12.))
148                                    .text_color(cx.theme().colors().text_muted)
149                                    .truncate()
150                                    .when_some(
151                                        self.project_name.clone(),
152                                        |description, project_name| {
153                                            let has_caption = self.caption.is_some();
154                                            let project = div()
155                                                .truncate()
156                                                .when(has_caption, |this| this.max_w_16())
157                                                .child(project_name);
158                                            let mut row = h_flex().gap_1p5().child(project);
159                                            if has_caption {
160                                                row = row.child(
161                                                    div().size(px(3.)).rounded_full().bg(cx
162                                                        .theme()
163                                                        .colors()
164                                                        .text
165                                                        .opacity(0.5)),
166                                                );
167                                            }
168                                            description.child(row)
169                                        },
170                                    )
171                                    .when_some(self.caption.clone(), |description, caption| {
172                                        description.child(caption)
173                                    })
174                                    .child(gradient_overflow()),
175                            ),
176                    ),
177            )
178            .child(
179                v_flex()
180                    .gap_1()
181                    .items_center()
182                    .child(
183                        Button::new("open", "View")
184                            .style(ButtonStyle::Tinted(ui::TintColor::Accent))
185                            .full_width()
186                            .on_click({
187                                cx.listener(move |this, _event, _, cx| {
188                                    this.accept(cx);
189                                })
190                            }),
191                    )
192                    .child(Button::new("dismiss", "Dismiss").full_width().on_click({
193                        cx.listener(move |this, _event, _, cx| {
194                            this.dismiss(cx);
195                        })
196                    })),
197            )
198    }
199}
200
Served at tenant.openagents/omega Member data and write actions are omitted.