Skip to repository content205 lines · 5.3 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T03:53:56.615Z 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
workspace_error.rs
1use std::{sync::Arc, time::Duration};
2
3use gpui::{Action, SharedString};
4use ui::{IconName, IconPosition};
5use zed_actions::OpenBrowser;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum ErrorSeverity {
9 Critical,
10 Error,
11 Warning,
12}
13
14impl ErrorSeverity {
15 pub fn auto_dismiss_delay(&self) -> Option<Duration> {
16 match self {
17 ErrorSeverity::Critical => None,
18 ErrorSeverity::Error => Some(Duration::from_secs(30)),
19 ErrorSeverity::Warning => Some(Duration::from_secs(20)),
20 }
21 }
22}
23
24/// The behavior triggered when the user invokes an [`ErrorAction`].
25pub enum ErrorActionHandler {
26 /// Run the provided callback when the action is invoked.
27 /// The notification is still dismissed afterwards by the button's click handler.
28 Action(Box<dyn Action>),
29 /// Dismiss the notification without running any extra logic.
30 Dismiss,
31}
32
33/// An icon to display on an action button, with its position relative to the label.
34///
35/// Bundled together so the position is only carried around when an icon is actually set
36/// — there's no meaningful "position" without an icon to position.
37#[derive(Debug, Clone, Copy)]
38pub struct ActionIcon {
39 pub name: IconName,
40 pub position: IconPosition,
41}
42
43impl ActionIcon {
44 /// Show `name` at the start (left) of the action button label.
45 pub fn start(name: IconName) -> Self {
46 Self {
47 name,
48 position: IconPosition::Start,
49 }
50 }
51
52 /// Show `name` at the end (right) of the action button label.
53 pub fn end(name: IconName) -> Self {
54 Self {
55 name,
56 position: IconPosition::End,
57 }
58 }
59}
60
61pub struct ErrorAction {
62 pub label: SharedString,
63 pub icon: Option<ActionIcon>,
64 pub tooltip: Option<SharedString>,
65 pub handler: ErrorActionHandler,
66}
67
68impl ErrorAction {
69 pub fn new<A: Action + 'static>(label: impl Into<SharedString>, handler: A) -> Self {
70 Self {
71 label: label.into(),
72 icon: None,
73 tooltip: None,
74 handler: ErrorActionHandler::Action(Box::new(handler)),
75 }
76 }
77
78 /// Creates a dismiss-only action labelled "Dismiss".
79 ///
80 /// Useful as a sensible default for [`WorkspaceError::primary_action`] when the error has no
81 /// recovery affordance beyond closing the notification.
82 pub fn dismiss() -> Self {
83 Self {
84 label: "Dismiss".into(),
85 icon: None,
86 tooltip: None,
87 handler: ErrorActionHandler::Dismiss,
88 }
89 }
90
91 /// Show `icon` at the start (left) of the action button label.
92 pub fn with_icon(mut self, icon: IconName) -> Self {
93 self.icon = Some(ActionIcon::start(icon));
94 self
95 }
96
97 /// Show `icon` at the end (right) of the action button label.
98 ///
99 /// Useful for actions that navigate the user elsewhere — for example the trailing
100 /// `⇗` produced by [`Self::link`].
101 pub fn with_end_icon(mut self, icon: IconName) -> Self {
102 self.icon = Some(ActionIcon::end(icon));
103 self
104 }
105
106 pub fn with_tooltip(mut self, tooltip: impl Into<SharedString>) -> Self {
107 self.tooltip = Some(tooltip.into());
108 self
109 }
110
111 pub fn link(label: impl Into<SharedString>, url: impl Into<Arc<str>>) -> Self {
112 Self::new(label, OpenBrowser { url: url.into() }).with_end_icon(IconName::ArrowUpRight)
113 }
114}
115
116pub trait WorkspaceError {
117 fn primary_message(&self) -> SharedString;
118
119 fn secondary_message(&self) -> Option<SharedString> {
120 None
121 }
122
123 /// The primary action shown in the error notification.
124 ///
125 /// If in doubt, use [`ErrorAction::dismiss`].
126 fn primary_action(&self) -> ErrorAction;
127
128 fn secondary_action(&self) -> Option<ErrorAction> {
129 None
130 }
131
132 fn severity(&self) -> ErrorSeverity;
133}
134
135impl WorkspaceError for &'static str {
136 fn primary_message(&self) -> SharedString {
137 self.to_string().into()
138 }
139
140 fn primary_action(&self) -> ErrorAction {
141 ErrorAction::dismiss()
142 }
143
144 fn severity(&self) -> ErrorSeverity {
145 ErrorSeverity::Critical
146 }
147}
148
149impl WorkspaceError for String {
150 fn primary_message(&self) -> SharedString {
151 self.clone().into()
152 }
153
154 fn primary_action(&self) -> ErrorAction {
155 ErrorAction::dismiss()
156 }
157
158 fn severity(&self) -> ErrorSeverity {
159 ErrorSeverity::Critical
160 }
161}
162
163impl WorkspaceError for anyhow::Error {
164 fn primary_message(&self) -> SharedString {
165 format!("{self}").into()
166 }
167
168 fn primary_action(&self) -> ErrorAction {
169 ErrorAction::dismiss()
170 }
171
172 fn severity(&self) -> ErrorSeverity {
173 ErrorSeverity::Critical
174 }
175}
176
177pub struct PortalError {
178 message: String,
179}
180
181impl PortalError {
182 pub fn new(message: impl Into<String>) -> Self {
183 Self {
184 message: message.into(),
185 }
186 }
187}
188
189impl WorkspaceError for PortalError {
190 fn primary_message(&self) -> SharedString {
191 self.message.clone().into()
192 }
193
194 fn severity(&self) -> ErrorSeverity {
195 ErrorSeverity::Critical
196 }
197
198 fn primary_action(&self) -> ErrorAction {
199 ErrorAction::link(
200 "See docs",
201 "https://github.com/OpenAgentsInc/omega#readme",
202 )
203 }
204}
205