Skip to repository content200 lines · 7.4 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T05:04:19.674Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
agent_notification.rs
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