Skip to repository content253 lines · 8.3 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T04:48:39.257Z 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
status_toast.rs
1use std::rc::Rc;
2
3use gpui::{DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, IntoElement};
4use ui::{Tooltip, prelude::*};
5use workspace::{ToastAction, ToastView};
6use zed_actions::toast;
7
8#[derive(RegisterComponent)]
9pub struct StatusToast {
10 icon: Option<Icon>,
11 text: SharedString,
12 action: Option<ToastAction>,
13 show_dismiss: bool,
14 auto_dismiss: bool,
15 this_handle: Entity<Self>,
16 focus_handle: FocusHandle,
17}
18
19impl StatusToast {
20 pub fn new(
21 text: impl Into<SharedString>,
22 cx: &mut App,
23 f: impl FnOnce(Self, &mut Context<Self>) -> Self,
24 ) -> Entity<Self> {
25 cx.new(|cx| {
26 let focus_handle = cx.focus_handle();
27
28 f(
29 Self {
30 text: text.into(),
31 icon: None,
32 action: None,
33 show_dismiss: false,
34 auto_dismiss: true,
35 this_handle: cx.entity(),
36 focus_handle,
37 },
38 cx,
39 )
40 })
41 }
42
43 pub fn icon(mut self, icon: Icon) -> Self {
44 self.icon = Some(icon);
45 self
46 }
47
48 pub fn auto_dismiss(mut self, auto_dismiss: bool) -> Self {
49 self.auto_dismiss = auto_dismiss;
50 self
51 }
52
53 pub fn action(
54 mut self,
55 label: impl Into<SharedString>,
56 f: impl Fn(&mut Window, &mut App) + 'static,
57 ) -> Self {
58 let this_handle = self.this_handle.clone();
59 self.action = Some(ToastAction::new(
60 label.into(),
61 Some(Rc::new(move |window, cx| {
62 this_handle.update(cx, |_, cx| {
63 cx.emit(DismissEvent);
64 });
65 f(window, cx);
66 })),
67 ));
68 self
69 }
70
71 pub fn dismiss_button(mut self, show: bool) -> Self {
72 self.show_dismiss = show;
73 self
74 }
75}
76
77impl Render for StatusToast {
78 fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
79 let has_action_or_dismiss = self.action.is_some() || self.show_dismiss;
80
81 h_flex()
82 .id("status-toast")
83 .elevation_3(cx)
84 .gap_2()
85 .py_1p5()
86 .pl_2p5()
87 .map(|this| {
88 if has_action_or_dismiss {
89 this.pr_1p5()
90 } else {
91 this.pr_2p5()
92 }
93 })
94 .flex_none()
95 .bg(cx.theme().colors().surface_background)
96 .shadow_lg()
97 .when_some(self.icon.clone(), |this, icon| this.child(icon))
98 .child(Label::new(self.text.clone()).color(Color::Default))
99 .when_some(self.action.as_ref(), |this, action| {
100 this.child(
101 Button::new(action.id.clone(), action.label.clone())
102 .tooltip(Tooltip::for_action_title(
103 action.label.clone(),
104 &toast::RunAction,
105 ))
106 .color(Color::Muted)
107 .when_some(action.on_click.clone(), |el, handler| {
108 el.on_click(move |_click_event, window, cx| handler(window, cx))
109 }),
110 )
111 })
112 .when(self.show_dismiss, |this| {
113 let handle = self.this_handle.clone();
114 this.child(
115 IconButton::new("dismiss", IconName::Close)
116 .shape(ui::IconButtonShape::Square)
117 .icon_size(IconSize::Small)
118 .icon_color(Color::Muted)
119 .tooltip(Tooltip::text("Dismiss"))
120 .on_click(move |_click_event, _window, cx| {
121 handle.update(cx, |_, cx| {
122 cx.emit(DismissEvent);
123 });
124 }),
125 )
126 })
127 }
128}
129
130impl ToastView for StatusToast {
131 fn action(&self) -> Option<ToastAction> {
132 self.action.clone()
133 }
134
135 fn auto_dismiss(&self) -> bool {
136 self.auto_dismiss
137 }
138}
139
140impl Focusable for StatusToast {
141 fn focus_handle(&self, _cx: &App) -> gpui::FocusHandle {
142 self.focus_handle.clone()
143 }
144}
145
146impl EventEmitter<DismissEvent> for StatusToast {}
147
148impl Component for StatusToast {
149 fn scope() -> ComponentScope {
150 ComponentScope::Notification
151 }
152
153 fn description() -> &'static str {
154 "A compact, transient toast used to surface status updates \
155 such as completed operations or pending updates, with optional icon, \
156 action, and dismiss affordances."
157 }
158
159 fn preview(_window: &mut Window, cx: &mut App) -> AnyElement {
160 let text_example = StatusToast::new("Operation completed", cx, |this, _| this);
161
162 let action_example = StatusToast::new("Update ready to install", cx, |this, _cx| {
163 this.action("Restart", |_, _| {})
164 });
165
166 let dismiss_button_example =
167 StatusToast::new("Dismiss Button", cx, |this, _| this.dismiss_button(true));
168
169 let icon_example = StatusToast::new(
170 "Nathan Sobo accepted your contact request",
171 cx,
172 |this, _| {
173 this.icon(
174 Icon::new(IconName::Check)
175 .size(IconSize::Small)
176 .color(Color::Muted),
177 )
178 },
179 );
180
181 let success_example = StatusToast::new("Pushed 4 changes to `omega/main`", cx, |this, _| {
182 this.icon(
183 Icon::new(IconName::Check)
184 .size(IconSize::Small)
185 .color(Color::Success),
186 )
187 });
188
189 let error_example = StatusToast::new(
190 "git push: Couldn't find remote origin `iamnbutler/omega`",
191 cx,
192 |this, _cx| {
193 this.icon(
194 Icon::new(IconName::XCircle)
195 .size(IconSize::Small)
196 .color(Color::Error),
197 )
198 .action("More Info", |_, _| {})
199 },
200 );
201
202 let warning_example = StatusToast::new("You have outdated settings", cx, |this, _cx| {
203 this.icon(
204 Icon::new(IconName::Warning)
205 .size(IconSize::Small)
206 .color(Color::Warning),
207 )
208 .action("More Info", |_, _| {})
209 });
210
211 let pr_example =
212 StatusToast::new("`omega/new-notification-system` created!", cx, |this, _cx| {
213 this.icon(
214 Icon::new(IconName::GitBranch)
215 .size(IconSize::Small)
216 .color(Color::Muted),
217 )
218 .action("Open Pull Request", |_, cx| {
219 cx.open_url("https://github.com/")
220 })
221 });
222
223 v_flex()
224 .gap_6()
225 .p_4()
226 .children(vec![
227 example_group_with_title(
228 "Basic Toast",
229 vec![
230 single_example("Text", div().child(text_example).into_any_element()),
231 single_example("Action", div().child(action_example).into_any_element()),
232 single_example("Icon", div().child(icon_example).into_any_element()),
233 single_example(
234 "Dismiss Button",
235 div().child(dismiss_button_example).into_any_element(),
236 ),
237 ],
238 ),
239 example_group_with_title(
240 "Examples",
241 vec![
242 single_example("Success", div().child(success_example).into_any_element()),
243 single_example("Error", div().child(error_example).into_any_element()),
244 single_example("Warning", div().child(warning_example).into_any_element()),
245 single_example("Create PR", div().child(pr_example).into_any_element()),
246 ],
247 )
248 .vertical(),
249 ])
250 .into_any_element()
251 }
252}
253