diff --git a/crates/agent_ui/src/acp/thread_view.rs b/crates/agent_ui/src/acp/thread_view.rs index 94fbff72f780ab5f4a1fa00d53a1b068c8505247..9ba156c8abc615945fc257080e354b8020464104 100644 --- a/crates/agent_ui/src/acp/thread_view.rs +++ b/crates/agent_ui/src/acp/thread_view.rs @@ -57,7 +57,10 @@ use ui::{ }; use util::defer; use util::{ResultExt, size::format_file_size, time::duration_alt_display}; -use workspace::{CollaboratorId, NewTerminal, Toast, Workspace, notifications::NotificationId}; +use workspace::{ + CollaboratorId, NewTerminal, NotificationSource, Toast, Workspace, + notifications::NotificationId, +}; use zed_actions::agent::{Chat, ToggleModelSelector}; use zed_actions::assistant::OpenRulesLibrary; diff --git a/crates/agent_ui/src/acp/thread_view/active_thread.rs b/crates/agent_ui/src/acp/thread_view/active_thread.rs index bde91fbec9a77ca9554ca31c3e3b4d97b7a21c04..764c92798fe0177bcace79fed2571e8607355749 100644 --- a/crates/agent_ui/src/acp/thread_view/active_thread.rs +++ b/crates/agent_ui/src/acp/thread_view/active_thread.rs @@ -1439,6 +1439,7 @@ impl AcpThreadView { )); }, ), + NotificationSource::Agent, cx, ); }); @@ -1512,6 +1513,7 @@ impl AcpThreadView { "Thread synced with latest version", ) .autohide(), + NotificationSource::Agent, cx, ); }); diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs index ccfc0cd7073b08249a9bdc07cf3525f92e689e9a..ec3295e927412d6caec6869b18baa8adab31cbcd 100644 --- a/crates/agent_ui/src/agent_panel.rs +++ b/crates/agent_ui/src/agent_panel.rs @@ -66,7 +66,8 @@ use ui::{ }; use util::ResultExt as _; use workspace::{ - CollaboratorId, DraggedSelection, DraggedTab, ToggleZoom, ToolbarItemView, Workspace, + CollaboratorId, DraggedSelection, DraggedTab, NotificationSource, ToggleZoom, ToolbarItemView, + Workspace, dock::{DockPosition, Panel, PanelEvent}, }; use zed_actions::{ @@ -1216,6 +1217,7 @@ impl AgentPanel { "No active native thread to copy", ) .autohide(), + NotificationSource::Agent, cx, ); }); @@ -1243,6 +1245,7 @@ impl AgentPanel { "Thread copied to clipboard (base64 encoded)", ) .autohide(), + NotificationSource::Agent, cx, ); }); @@ -1265,6 +1268,7 @@ impl AgentPanel { "No clipboard content available", ) .autohide(), + NotificationSource::Agent, cx, ); }); @@ -1282,6 +1286,7 @@ impl AgentPanel { "Clipboard does not contain text", ) .autohide(), + NotificationSource::Agent, cx, ); }); @@ -1302,6 +1307,7 @@ impl AgentPanel { "Failed to decode clipboard content (expected base64)", ) .autohide(), + NotificationSource::Agent, cx, ); }); @@ -1323,6 +1329,7 @@ impl AgentPanel { "Failed to parse thread data from clipboard", ) .autohide(), + NotificationSource::Agent, cx, ); }); @@ -1366,6 +1373,7 @@ impl AgentPanel { "Thread loaded from clipboard", ) .autohide(), + NotificationSource::Agent, cx, ); }); diff --git a/crates/agent_ui/src/inline_assistant.rs b/crates/agent_ui/src/inline_assistant.rs index 3662f5a9f50f57fe29cc9c11344a2c3955b0a22d..24aec9ac6b7b0d57068c3a5b821443dd544eead4 100644 --- a/crates/agent_ui/src/inline_assistant.rs +++ b/crates/agent_ui/src/inline_assistant.rs @@ -52,7 +52,9 @@ use terminal_view::{TerminalView, terminal_panel::TerminalPanel}; use text::{OffsetRangeExt, ToPoint as _}; use ui::prelude::*; use util::{RangeExt, ResultExt, maybe}; -use workspace::{ItemHandle, Toast, Workspace, dock::Panel, notifications::NotificationId}; +use workspace::{ + ItemHandle, NotificationSource, Toast, Workspace, dock::Panel, notifications::NotificationId, +}; use zed_actions::agent::OpenSettings; pub fn init(fs: Arc, prompt_builder: Arc, cx: &mut App) { @@ -1835,7 +1837,11 @@ impl InlineAssist { assist_id.0, ); - workspace.show_toast(Toast::new(id, error), cx); + workspace.show_toast( + Toast::new(id, error), + NotificationSource::Agent, + cx, + ); }) } else { #[cfg(any(test, feature = "test-support"))] diff --git a/crates/agent_ui/src/inline_prompt_editor.rs b/crates/agent_ui/src/inline_prompt_editor.rs index 48c597f0431c480ade5810db99c36a890ec65093..3bab8e0ec6999a272d2bf495a86e09687f1ee562 100644 --- a/crates/agent_ui/src/inline_prompt_editor.rs +++ b/crates/agent_ui/src/inline_prompt_editor.rs @@ -29,7 +29,7 @@ use ui::utils::WithRemSize; use ui::{IconButtonShape, KeyBinding, PopoverMenuHandle, Tooltip, prelude::*}; use uuid::Uuid; use workspace::notifications::NotificationId; -use workspace::{Toast, Workspace}; +use workspace::{NotificationSource, Toast, Workspace}; use zed_actions::{ agent::ToggleModelSelector, editor::{MoveDown, MoveUp}, @@ -725,6 +725,7 @@ impl PromptEditor { toast }, + NotificationSource::Agent, cx, ); }) diff --git a/crates/agent_ui/src/mention_set.rs b/crates/agent_ui/src/mention_set.rs index ee796323e28c64fb4162bbb05f6f6f9555a12d38..d86425487a970189b35d8a51eeea5d1659b4ce02 100644 --- a/crates/agent_ui/src/mention_set.rs +++ b/crates/agent_ui/src/mention_set.rs @@ -36,7 +36,10 @@ use std::{ use text::OffsetRangeExt; use ui::{Disclosure, Toggleable, prelude::*}; use util::{ResultExt, debug_panic, rel_path::RelPath}; -use workspace::{Workspace, notifications::NotifyResultExt as _}; +use workspace::{ + Workspace, + notifications::{NotificationSource, NotifyResultExt as _}, +}; use crate::ui::MentionCrease; @@ -298,7 +301,7 @@ impl MentionSet { // Notify the user if we failed to load the mentioned context cx.spawn_in(window, async move |this, cx| { - let result = task.await.notify_async_err(cx); + let result = task.await.notify_async_err(NotificationSource::Agent, cx); drop(tx); if result.is_none() { this.update(cx, |this, cx| { @@ -718,7 +721,11 @@ pub(crate) async fn insert_images_as_context( mention_set.insert_mention(crease_id, MentionUri::PastedImage, task.clone()) }); - if task.await.notify_async_err(cx).is_none() { + if task + .await + .notify_async_err(NotificationSource::Agent, cx) + .is_none() + { editor.update(cx, |editor, cx| { editor.edit([(start_anchor..end_anchor, "")], cx); }); diff --git a/crates/agent_ui/src/terminal_inline_assistant.rs b/crates/agent_ui/src/terminal_inline_assistant.rs index 8ee59a0a096172ad9a81983f3517226e824c43e7..c96837164dc1e75d73dc7b0976b0c67a89ad423e 100644 --- a/crates/agent_ui/src/terminal_inline_assistant.rs +++ b/crates/agent_ui/src/terminal_inline_assistant.rs @@ -27,7 +27,7 @@ use terminal_view::TerminalView; use ui::prelude::*; use util::ResultExt; use uuid::Uuid; -use workspace::{Toast, Workspace, notifications::NotificationId}; +use workspace::{NotificationSource, Toast, Workspace, notifications::NotificationId}; pub fn init(fs: Arc, prompt_builder: Arc, cx: &mut App) { cx.set_global(TerminalInlineAssistant::new(fs, prompt_builder)); @@ -452,7 +452,11 @@ impl TerminalInlineAssist { assist_id.0, ); - workspace.show_toast(Toast::new(id, error), cx); + workspace.show_toast( + Toast::new(id, error), + NotificationSource::Agent, + cx, + ); }) } diff --git a/crates/agent_ui/src/text_thread_editor.rs b/crates/agent_ui/src/text_thread_editor.rs index 447449fe72fee89b0c6775bbbcf8836141efb2b9..19204949ab6795b0e9748c3edbbdda309e96a1b2 100644 --- a/crates/agent_ui/src/text_thread_editor.rs +++ b/crates/agent_ui/src/text_thread_editor.rs @@ -60,7 +60,7 @@ use ui::{ }; use util::{ResultExt, maybe}; use workspace::{ - CollaboratorId, + CollaboratorId, NotificationSource, searchable::{Direction, SearchToken, SearchableItemHandle}, }; @@ -1389,6 +1389,7 @@ impl TextThreadEditor { ), ) .autohide(), + NotificationSource::Agent, cx, ); } diff --git a/crates/auto_update_ui/src/auto_update_ui.rs b/crates/auto_update_ui/src/auto_update_ui.rs index bb300efac102172e8a36e32f06aea76f84aff0e3..f4a1fdbe61927258b2f5d7b386e09cd42675b0a3 100644 --- a/crates/auto_update_ui/src/auto_update_ui.rs +++ b/crates/auto_update_ui/src/auto_update_ui.rs @@ -9,7 +9,7 @@ use util::{ResultExt as _, maybe}; use workspace::Workspace; use workspace::notifications::ErrorMessagePrompt; use workspace::notifications::simple_message_notification::MessageNotification; -use workspace::notifications::{NotificationId, show_app_notification}; +use workspace::notifications::{NotificationId, NotificationSource, show_app_notification}; actions!( auto_update, @@ -47,6 +47,7 @@ fn notify_release_notes_failed_to_show( struct ViewReleaseNotesError; workspace.show_notification( NotificationId::unique::(), + NotificationSource::Update, cx, |cx| { cx.new(move |cx| { @@ -183,6 +184,7 @@ pub fn notify_if_app_was_updated(cx: &mut App) { let app_name = ReleaseChannel::global(cx).display_name(); show_app_notification( NotificationId::unique::(), + NotificationSource::Update, cx, move |cx| { let workspace_handle = cx.entity().downgrade(); diff --git a/crates/collab_ui/src/channel_view.rs b/crates/collab_ui/src/channel_view.rs index cbfa06142e2e8a520653d031dfe8c4b69c08667a..97ce2460b986cf63d601d6e06589d39c4329d9ac 100644 --- a/crates/collab_ui/src/channel_view.rs +++ b/crates/collab_ui/src/channel_view.rs @@ -22,7 +22,7 @@ use std::{ }; use ui::prelude::*; use util::ResultExt; -use workspace::{CollaboratorId, item::TabContentParams}; +use workspace::{CollaboratorId, NotificationSource, item::TabContentParams}; use workspace::{ ItemNavHistory, Pane, SaveIntent, Toast, ViewId, Workspace, WorkspaceId, item::{FollowableItem, Item, ItemEvent}, @@ -332,6 +332,7 @@ impl ChannelView { NotificationId::unique::(), "Link copied to clipboard", ), + NotificationSource::Collab, cx, ); }) diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index 60262951ef916183bdaf72df90ab39f2edd83f27..cfb306d488c39cf48d41ef2e64d1b7232b33323c 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -36,7 +36,8 @@ use ui::{ }; use util::{ResultExt, TryFutureExt, maybe}; use workspace::{ - CopyRoomId, Deafen, LeaveCall, Mute, OpenChannelNotes, ScreenShare, ShareProject, Workspace, + CopyRoomId, Deafen, LeaveCall, Mute, NotificationSource, OpenChannelNotes, ScreenShare, + ShareProject, Workspace, dock::{DockPosition, Panel, PanelEvent}, notifications::{DetachAndPromptErr, NotifyResultExt}, }; @@ -130,13 +131,18 @@ pub fn init(cx: &mut App) { "Room ID copied to clipboard", ) .autohide(), + NotificationSource::Collab, cx, ); }) }) - .detach_and_notify_err(window, cx); + .detach_and_notify_err(NotificationSource::Collab, window, cx); } else { - workspace.show_error(&"There’s no active call; join one first.", cx); + workspace.show_error( + &"There's no active call; join one first.", + NotificationSource::Collab, + cx, + ); } }); workspace.register_action(|workspace, _: &ShareProject, window, cx| { @@ -2194,7 +2200,7 @@ impl CollabPanel { channel_store .update(cx, |channels, _| channels.remove_channel(channel_id)) .await - .notify_async_err(cx); + .notify_async_err(NotificationSource::Collab, cx); this.update_in(cx, |_, window, cx| cx.focus_self(window)) .ok(); } @@ -2228,7 +2234,7 @@ impl CollabPanel { user_store .update(cx, |store, cx| store.remove_contact(user_id, cx)) .await - .notify_async_err(cx); + .notify_async_err(NotificationSource::Collab, cx); } anyhow::Ok(()) }) @@ -2333,7 +2339,7 @@ impl CollabPanel { .connect(true, cx) .await .into_response() - .notify_async_err(cx); + .notify_async_err(NotificationSource::Collab, cx); }) .detach() })), diff --git a/crates/collab_ui/src/notification_panel.rs b/crates/collab_ui/src/notification_panel.rs index 99203bc867ff7da9e140bc4a886e291252a5153d..575e1746a9d5da543ba7f226c3fbc7d1e29c3d01 100644 --- a/crates/collab_ui/src/notification_panel.rs +++ b/crates/collab_ui/src/notification_panel.rs @@ -26,7 +26,7 @@ use workspace::notifications::{ Notification as WorkspaceNotification, NotificationId, SuppressEvent, }; use workspace::{ - Workspace, + NotificationSource, Workspace, dock::{DockPosition, Panel, PanelEvent}, }; @@ -473,7 +473,7 @@ impl NotificationPanel { let id = NotificationId::unique::(); workspace.dismiss_notification(&id, cx); - workspace.show_notification(id, cx, |cx| { + workspace.show_notification(id, NotificationSource::Collab, cx, |cx| { let workspace = cx.entity().downgrade(); cx.new(|cx| NotificationToast { actor, diff --git a/crates/copilot_ui/src/sign_in.rs b/crates/copilot_ui/src/sign_in.rs index dd48f95e0af6daeaf2a0a15b7b9595cb4c08aba2..c9231c524b7106f51359c2f985f1e0f549d77729 100644 --- a/crates/copilot_ui/src/sign_in.rs +++ b/crates/copilot_ui/src/sign_in.rs @@ -12,7 +12,7 @@ use project::project_settings::ProjectSettings; use settings::Settings as _; use ui::{ButtonLike, CommonAnimationExt, ConfiguredApiCard, Vector, VectorName, prelude::*}; use util::ResultExt as _; -use workspace::{AppState, Toast, Workspace, notifications::NotificationId}; +use workspace::{AppState, NotificationSource, Toast, Workspace, notifications::NotificationId}; const COPILOT_SIGN_UP_URL: &str = "https://github.com/features/copilot"; const ERROR_LABEL: &str = @@ -37,7 +37,7 @@ pub fn initiate_sign_out(copilot: Entity, window: &mut Window, cx: &mut Err(err) => cx.update(|window, cx| { if let Some(workspace) = window.root::().flatten() { workspace.update(cx, |workspace, cx| { - workspace.show_error(&err, cx); + workspace.show_error(&err, NotificationSource::Copilot, cx); }) } else { log::error!("{:?}", err); @@ -88,7 +88,11 @@ fn copilot_toast(message: Option<&'static str>, window: &Window, cx: &mut App) { cx.defer(move |cx| { workspace.update(cx, |workspace, cx| match message { - Some(message) => workspace.show_toast(Toast::new(NOTIFICATION_ID, message), cx), + Some(message) => workspace.show_toast( + Toast::new(NOTIFICATION_ID, message), + NotificationSource::Copilot, + cx, + ), None => workspace.dismiss_toast(&NOTIFICATION_ID, cx), }); }) diff --git a/crates/edit_prediction/src/edit_prediction.rs b/crates/edit_prediction/src/edit_prediction.rs index 1ec3c7ac44fc8f592fa094f668b3bfd84245eb5a..98693749ff7165117c8d7e994d17f5906e76f2ad 100644 --- a/crates/edit_prediction/src/edit_prediction.rs +++ b/crates/edit_prediction/src/edit_prediction.rs @@ -50,7 +50,9 @@ use std::sync::Arc; use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; use thiserror::Error; use util::{RangeExt as _, ResultExt as _}; -use workspace::notifications::{ErrorMessagePrompt, NotificationId, show_app_notification}; +use workspace::notifications::{ + ErrorMessagePrompt, NotificationId, NotificationSource, show_app_notification, +}; pub mod cursor_excerpt; pub mod example_spec; @@ -1991,6 +1993,7 @@ impl EditPredictionStore { let error_message: SharedString = err.to_string().into(); show_app_notification( NotificationId::unique::(), + NotificationSource::Copilot, cx, move |cx| { cx.new(|cx| { diff --git a/crates/edit_prediction/src/zeta1.rs b/crates/edit_prediction/src/zeta1.rs index 43d467950fd388fb5a771e8c101a005df57c6897..0f9fcc4aa366b38d45109e9b9d7b0905f59e7ef5 100644 --- a/crates/edit_prediction/src/zeta1.rs +++ b/crates/edit_prediction/src/zeta1.rs @@ -18,6 +18,7 @@ use language::{ use project::{Project, ProjectPath}; use release_channel::AppVersion; use text::Bias; +use workspace::NotificationSource; use workspace::notifications::{ErrorMessagePrompt, NotificationId, show_app_notification}; use zeta_prompt::{ Event, ZetaPromptInput, @@ -169,6 +170,7 @@ pub(crate) fn request_prediction_with_zeta1( let error_message: SharedString = err.to_string().into(); show_app_notification( NotificationId::unique::(), + NotificationSource::Copilot, cx, move |cx| { cx.new(|cx| { diff --git a/crates/edit_prediction_ui/src/edit_prediction_button.rs b/crates/edit_prediction_ui/src/edit_prediction_button.rs index 8835dd5507dc9deccb57ad4f4ba15d8af017bfd3..adc7538c8b8ede3ae7e0a416cb13c7afc7a7ac6b 100644 --- a/crates/edit_prediction_ui/src/edit_prediction_button.rs +++ b/crates/edit_prediction_ui/src/edit_prediction_button.rs @@ -38,8 +38,8 @@ use ui::{ use util::ResultExt as _; use workspace::{ - StatusItemView, Toast, Workspace, create_and_open_local_file, item::ItemHandle, - notifications::NotificationId, + NotificationSource, StatusItemView, Toast, Workspace, create_and_open_local_file, + item::ItemHandle, notifications::NotificationId, }; use zed_actions::{OpenBrowser, OpenSettingsAt}; @@ -137,6 +137,7 @@ impl Render for EditPredictionButton { ) }, ), + NotificationSource::Copilot, cx, ); }); diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index bf241d01d1910bea710c07b63485c51ea06dcdd8..dc70903c576f47d8ac92c7fb3c762d05cd440f4c 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -211,9 +211,10 @@ use ui::{ use ui_input::ErasedEditor; use util::{RangeExt, ResultExt, TryFutureExt, maybe, post_inc}; use workspace::{ - CollaboratorId, Item as WorkspaceItem, ItemId, ItemNavHistory, NavigationEntry, OpenInTerminal, - OpenTerminal, Pane, RestoreOnStartupBehavior, SERIALIZATION_THROTTLE_TIME, SplitDirection, - TabBarSettings, Toast, ViewId, Workspace, WorkspaceId, WorkspaceSettings, + CollaboratorId, Item as WorkspaceItem, ItemId, ItemNavHistory, NavigationEntry, + NotificationSource, OpenInTerminal, OpenTerminal, Pane, RestoreOnStartupBehavior, + SERIALIZATION_THROTTLE_TIME, SplitDirection, TabBarSettings, Toast, ViewId, Workspace, + WorkspaceId, WorkspaceSettings, item::{BreadcrumbText, ItemBufferKind, ItemHandle, PreviewTabsSettings, SaveOptions}, notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt}, searchable::{CollapseDirection, SearchEvent}, @@ -11459,8 +11460,11 @@ impl Editor { let Some(project) = self.project.clone() else { return; }; - self.reload(project, window, cx) - .detach_and_notify_err(window, cx); + self.reload(project, window, cx).detach_and_notify_err( + NotificationSource::Editor, + window, + cx, + ); } pub fn restore_file( @@ -22968,6 +22972,7 @@ impl Editor { NotificationId::unique::(), message, ), + NotificationSource::Editor, cx, ) }) @@ -23028,6 +23033,7 @@ impl Editor { workspace.show_toast( Toast::new(NotificationId::unique::(), message), + NotificationSource::Editor, cx, ) }); diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index d1fd99f09217dc736c12c3f8902fc2bdb777e03d..d9dcc30acdfd1f9f616ff09d04ced569e45244d2 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -96,8 +96,8 @@ use unicode_segmentation::UnicodeSegmentation; use util::post_inc; use util::{RangeExt, ResultExt, debug_panic}; use workspace::{ - CollaboratorId, ItemHandle, ItemSettings, OpenInTerminal, OpenTerminal, RevealInProjectPanel, - Workspace, + CollaboratorId, ItemHandle, ItemSettings, NotificationSource, OpenInTerminal, OpenTerminal, + RevealInProjectPanel, Workspace, item::{BreadcrumbText, Item, ItemBufferKind}, notifications::NotifyTaskExt, }; @@ -541,21 +541,21 @@ impl EditorElement { register_action(editor, window, |editor, action, window, cx| { if let Some(task) = editor.format(action, window, cx) { - task.detach_and_notify_err(window, cx); + task.detach_and_notify_err(NotificationSource::Editor, window, cx); } else { cx.propagate(); } }); register_action(editor, window, |editor, action, window, cx| { if let Some(task) = editor.format_selections(action, window, cx) { - task.detach_and_notify_err(window, cx); + task.detach_and_notify_err(NotificationSource::Editor, window, cx); } else { cx.propagate(); } }); register_action(editor, window, |editor, action, window, cx| { if let Some(task) = editor.organize_imports(action, window, cx) { - task.detach_and_notify_err(window, cx); + task.detach_and_notify_err(NotificationSource::Editor, window, cx); } else { cx.propagate(); } @@ -565,49 +565,49 @@ impl EditorElement { register_action(editor, window, Editor::show_character_palette); register_action(editor, window, |editor, action, window, cx| { if let Some(task) = editor.confirm_completion(action, window, cx) { - task.detach_and_notify_err(window, cx); + task.detach_and_notify_err(NotificationSource::Editor, window, cx); } else { cx.propagate(); } }); register_action(editor, window, |editor, action, window, cx| { if let Some(task) = editor.confirm_completion_replace(action, window, cx) { - task.detach_and_notify_err(window, cx); + task.detach_and_notify_err(NotificationSource::Editor, window, cx); } else { cx.propagate(); } }); register_action(editor, window, |editor, action, window, cx| { if let Some(task) = editor.confirm_completion_insert(action, window, cx) { - task.detach_and_notify_err(window, cx); + task.detach_and_notify_err(NotificationSource::Editor, window, cx); } else { cx.propagate(); } }); register_action(editor, window, |editor, action, window, cx| { if let Some(task) = editor.compose_completion(action, window, cx) { - task.detach_and_notify_err(window, cx); + task.detach_and_notify_err(NotificationSource::Editor, window, cx); } else { cx.propagate(); } }); register_action(editor, window, |editor, action, window, cx| { if let Some(task) = editor.confirm_code_action(action, window, cx) { - task.detach_and_notify_err(window, cx); + task.detach_and_notify_err(NotificationSource::Editor, window, cx); } else { cx.propagate(); } }); register_action(editor, window, |editor, action, window, cx| { if let Some(task) = editor.rename(action, window, cx) { - task.detach_and_notify_err(window, cx); + task.detach_and_notify_err(NotificationSource::Editor, window, cx); } else { cx.propagate(); } }); register_action(editor, window, |editor, action, window, cx| { if let Some(task) = editor.confirm_rename(action, window, cx) { - task.detach_and_notify_err(window, cx); + task.detach_and_notify_err(NotificationSource::Editor, window, cx); } else { cx.propagate(); } diff --git a/crates/encoding_selector/src/encoding_selector.rs b/crates/encoding_selector/src/encoding_selector.rs index 3954bf29a30a0981c25bee3eb88829a7002881ad..efe91774c2d242ab13e8b944d7e8127112046a32 100644 --- a/crates/encoding_selector/src/encoding_selector.rs +++ b/crates/encoding_selector/src/encoding_selector.rs @@ -13,7 +13,10 @@ use picker::{Picker, PickerDelegate}; use std::sync::Arc; use ui::{HighlightedLabel, ListItem, ListItemSpacing, Toggleable, v_flex}; use util::ResultExt; -use workspace::{ModalView, Toast, Workspace, notifications::NotificationId}; +use workspace::{ + ModalView, Toast, Workspace, + notifications::{NotificationId, NotificationSource}, +}; actions!( encoding_selector, @@ -62,6 +65,7 @@ impl EncodingSelector { NotificationId::unique::(), "Save file to change encoding", ), + NotificationSource::Editor, cx, ); return Some(()); @@ -72,6 +76,7 @@ impl EncodingSelector { NotificationId::unique::(), "Cannot change encoding during collaboration", ), + NotificationSource::Collab, cx, ); return Some(()); @@ -82,6 +87,7 @@ impl EncodingSelector { NotificationId::unique::(), "Cannot change encoding of remote server file", ), + NotificationSource::Remote, cx, ); return Some(()); diff --git a/crates/extensions_ui/src/extension_suggest.rs b/crates/extensions_ui/src/extension_suggest.rs index 7ad4c1540a419f0cdeedb2aeff7661aafac5ef4c..13efd1012f3e104cdc82facde92a62e4b9739d06 100644 --- a/crates/extensions_ui/src/extension_suggest.rs +++ b/crates/extensions_ui/src/extension_suggest.rs @@ -9,7 +9,10 @@ use language::Buffer; use ui::prelude::*; use util::rel_path::RelPath; use workspace::notifications::simple_message_notification::MessageNotification; -use workspace::{Workspace, notifications::NotificationId}; +use workspace::{ + Workspace, + notifications::{NotificationId, NotificationSource}, +}; const SUGGESTIONS_BY_EXTENSION_ID: &[(&str, &[&str])] = &[ ("astro", &["astro"]), @@ -166,7 +169,7 @@ pub(crate) fn suggest(buffer: Entity, window: &mut Window, cx: &mut Cont SharedString::from(extension_id.clone()), ); - workspace.show_notification(notification_id, cx, |cx| { + workspace.show_notification(notification_id, NotificationSource::Extension, cx, |cx| { cx.new(move |cx| { MessageNotification::new( format!( diff --git a/crates/extensions_ui/src/extensions_ui.rs b/crates/extensions_ui/src/extensions_ui.rs index 7c77579c9172e24680e8398645544cd1099dfaff..6e6cb2b5b57e0d64fa5b549e9a6df9d938b43f46 100644 --- a/crates/extensions_ui/src/extensions_ui.rs +++ b/crates/extensions_ui/src/extensions_ui.rs @@ -33,6 +33,7 @@ use vim_mode_setting::VimModeSetting; use workspace::{ Workspace, item::{Item, ItemEvent}, + notifications::NotificationSource, }; use zed_actions::ExtensionCategoryFilter; @@ -159,6 +160,7 @@ pub fn init(cx: &mut App) { // NOTE: using `anyhow::context` here ends up not printing // the error &format!("Failed to install dev extension: {}", err), + NotificationSource::Extension, cx, ); }) diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index 73533c57f156cdfba04ca736eeed5b0d23d2ee8f..58483d68195de5785d48ad0c0fc7226dae9e95e2 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -44,8 +44,10 @@ use util::{ rel_path::RelPath, }; use workspace::{ - ModalView, OpenOptions, OpenVisible, SplitDirection, Workspace, item::PreviewTabsSettings, - notifications::NotifyResultExt, pane, + ModalView, OpenOptions, OpenVisible, SplitDirection, Workspace, + item::PreviewTabsSettings, + notifications::{NotificationSource, NotifyResultExt}, + pane, }; use zed_actions::search::ToggleIncludeIgnored; @@ -1568,7 +1570,9 @@ impl PickerDelegate for FileFinderDelegate { let finder = self.file_finder.clone(); cx.spawn_in(window, async move |_, cx| { - let item = open_task.await.notify_async_err(cx)?; + let item = open_task + .await + .notify_async_err(NotificationSource::File, cx)?; if let Some(row) = row && let Some(active_editor) = item.downcast::() { diff --git a/crates/git_ui/src/commit_view.rs b/crates/git_ui/src/commit_view.rs index 79f581777485b08952b95f2097f2e7083de35c98..5c05eaf1c471658b0757d99c387de15da10c804a 100644 --- a/crates/git_ui/src/commit_view.rs +++ b/crates/git_ui/src/commit_view.rs @@ -34,7 +34,7 @@ use workspace::{ Item, ItemHandle, ItemNavHistory, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace, item::{ItemEvent, TabContentParams}, - notifications::NotifyTaskExt, + notifications::{NotificationSource, NotifyTaskExt}, pane::SaveIntent, searchable::SearchableItemHandle, }; @@ -774,7 +774,7 @@ impl CommitView { callback(repo, &sha, stash, commit_view_entity, workspace_weak, cx).await?; anyhow::Ok(()) }) - .detach_and_notify_err(window, cx); + .detach_and_notify_err(NotificationSource::Git, window, cx); } async fn close_commit_view( diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs index a4d26bc5a5a995f7bbc7af7df1cfef18dfe0b3d8..762c8f268cca85266f0cc13b2cfc7b4995171173 100644 --- a/crates/git_ui/src/git_panel.rs +++ b/crates/git_ui/src/git_panel.rs @@ -76,7 +76,9 @@ use workspace::SERIALIZATION_THROTTLE_TIME; use workspace::{ Workspace, dock::{DockPosition, Panel, PanelEvent}, - notifications::{DetachAndPromptErr, ErrorMessagePrompt, NotificationId, NotifyResultExt}, + notifications::{ + DetachAndPromptErr, ErrorMessagePrompt, NotificationId, NotificationSource, NotifyResultExt, + }, }; actions!( git_panel, @@ -739,7 +741,7 @@ impl GitPanel { GitStoreEvent::IndexWriteError(error) => { this.workspace .update(cx, |workspace, cx| { - workspace.show_error(error, cx); + workspace.show_error(error, NotificationSource::Git, cx); }) .ok(); } @@ -1277,7 +1279,7 @@ impl GitPanel { cx.spawn_in(window, async move |_, mut cx| { let item = open_task .await - .notify_async_err(&mut cx) + .notify_async_err(NotificationSource::Git, &mut cx) .ok_or_else(|| anyhow::anyhow!("Failed to open file"))?; if let Some(active_editor) = item.downcast::() { if let Some(diff_task) = @@ -3752,7 +3754,7 @@ impl GitPanel { let _ = workspace.update(cx, |workspace, cx| { struct CommitMessageError; let notification_id = NotificationId::unique::(); - workspace.show_notification(notification_id, cx, |cx| { + workspace.show_notification(notification_id, NotificationSource::Git, cx, |cx| { cx.new(|cx| { ErrorMessagePrompt::new( format!("Failed to generate commit message: {err}"), diff --git a/crates/git_ui/src/project_diff.rs b/crates/git_ui/src/project_diff.rs index 21a0d41fe099d60436bd80b7f4ee06982735c847..c8257e89c316e67204866449d4944a1dcbc780bc 100644 --- a/crates/git_ui/src/project_diff.rs +++ b/crates/git_ui/src/project_diff.rs @@ -44,7 +44,7 @@ use workspace::{ CloseActiveItem, ItemNavHistory, SerializableItem, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace, item::{Item, ItemEvent, ItemHandle, SaveOptions, TabContentParams}, - notifications::NotifyTaskExt, + notifications::{NotificationSource, NotifyTaskExt}, searchable::SearchableItemHandle, }; use ztracing::instrument; @@ -138,7 +138,7 @@ impl ProjectDiff { .ok(); anyhow::Ok(()) }) - .detach_and_notify_err(window, cx); + .detach_and_notify_err(NotificationSource::Git, window, cx); } pub fn deploy_at( diff --git a/crates/inspector_ui/src/inspector_ui.rs b/crates/inspector_ui/src/inspector_ui.rs index 1342007005586847db6966bcf7f5cdbcbe6444ac..e4cac26e44145a1d29fd70506347f94b577f57ac 100644 --- a/crates/inspector_ui/src/inspector_ui.rs +++ b/crates/inspector_ui/src/inspector_ui.rs @@ -9,13 +9,13 @@ pub use inspector::init; #[cfg(not(debug_assertions))] pub fn init(_app_state: std::sync::Arc, cx: &mut gpui::App) { use std::any::TypeId; - use workspace::notifications::NotifyResultExt as _; + use workspace::notifications::{NotificationSource, NotifyResultExt as _}; cx.on_action(|_: &zed_actions::dev::ToggleInspector, cx| { Err::<(), anyhow::Error>(anyhow::anyhow!( "dev::ToggleInspector is only available in debug builds" )) - .notify_app_err(cx); + .notify_app_err(NotificationSource::System, cx); }); command_palette_hooks::CommandPaletteFilter::update_global(cx, |filter, _cx| { diff --git a/crates/install_cli/src/install_cli_binary.rs b/crates/install_cli/src/install_cli_binary.rs index 095ed3cd315c49d38909a12608cd5607723abb3a..c89c64f050cb99f39b966e330532532847c4314c 100644 --- a/crates/install_cli/src/install_cli_binary.rs +++ b/crates/install_cli/src/install_cli_binary.rs @@ -5,7 +5,7 @@ use release_channel::ReleaseChannel; use std::ops::Deref; use std::path::{Path, PathBuf}; use util::ResultExt; -use workspace::notifications::{DetachAndPromptErr, NotificationId}; +use workspace::notifications::{DetachAndPromptErr, NotificationId, NotificationSource}; use workspace::{Toast, Workspace}; actions!( @@ -91,6 +91,7 @@ pub fn install_cli_binary(window: &mut Window, cx: &mut Context) { ReleaseChannel::global(cx).display_name() ), ), + NotificationSource::Cli, cx, ) })?; diff --git a/crates/keymap_editor/src/keymap_editor.rs b/crates/keymap_editor/src/keymap_editor.rs index de22ae01b503fde9aabdd99be5253d7c4e3f1b71..e7bef554ebeee7a0a0c054b5311608f82f729163 100644 --- a/crates/keymap_editor/src/keymap_editor.rs +++ b/crates/keymap_editor/src/keymap_editor.rs @@ -38,7 +38,8 @@ use ui::{ use ui_input::InputField; use util::ResultExt; use workspace::{ - Item, ModalView, SerializableItem, Workspace, notifications::NotifyTaskExt as _, + Item, ModalView, SerializableItem, Workspace, + notifications::{NotificationSource, NotifyTaskExt as _}, register_serializable_item, }; @@ -1319,7 +1320,7 @@ impl KeymapEditor { cx.spawn(async move |_, _| { remove_keybinding(to_remove, &fs, keyboard_mapper.as_ref()).await }) - .detach_and_notify_err(window, cx); + .detach_and_notify_err(NotificationSource::Settings, window, cx); } fn copy_context_to_clipboard( diff --git a/crates/onboarding/src/onboarding.rs b/crates/onboarding/src/onboarding.rs index 495a55411fc936d476dfa0d443e155d1fa7faecd..43125353d5721c7e1174a1585c16b66c7cb99fe7 100644 --- a/crates/onboarding/src/onboarding.rs +++ b/crates/onboarding/src/onboarding.rs @@ -19,7 +19,7 @@ use ui::{ pub use workspace::welcome::ShowWelcome; use workspace::welcome::WelcomePage; use workspace::{ - AppState, Workspace, WorkspaceId, + AppState, NotificationSource, Workspace, WorkspaceId, dock::DockPosition, item::{Item, ItemEvent}, notifications::NotifyResultExt as _, @@ -246,7 +246,7 @@ impl Onboarding { client .sign_in_with_optional_connect(true, cx) .await - .notify_async_err(cx); + .notify_async_err(NotificationSource::System, cx); }) .detach(); } diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 76dd4abe2f90e285e85ea8778c5ad785e1bbfab5..524c2eedc51112033da8306d26c8453ecbc4eaed 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -72,7 +72,7 @@ use workspace::{ DraggedSelection, OpenInTerminal, OpenOptions, OpenVisible, PreviewTabsSettings, SelectedEntry, SplitDirection, Workspace, dock::{DockPosition, Panel, PanelEvent}, - notifications::{DetachAndPromptErr, NotifyResultExt, NotifyTaskExt}, + notifications::{DetachAndPromptErr, NotificationSource, NotifyResultExt, NotifyTaskExt}, }; use worktree::CreatedEntry; use zed_actions::{project_panel::ToggleFocus, workspace::OpenWithSystem}; @@ -772,7 +772,11 @@ impl ProjectPanel { { match project_panel.confirm_edit(false, window, cx) { Some(task) => { - task.detach_and_notify_err(window, cx); + task.detach_and_notify_err( + NotificationSource::File, + window, + cx, + ); } None => { project_panel.discard_edit_state(window, cx); @@ -1648,7 +1652,7 @@ impl ProjectPanel { fn confirm(&mut self, _: &Confirm, window: &mut Window, cx: &mut Context) { if let Some(task) = self.confirm_edit(true, window, cx) { - task.detach_and_notify_err(window, cx); + task.detach_and_notify_err(NotificationSource::File, window, cx); } } @@ -3040,13 +3044,15 @@ impl ProjectPanel { match task { PasteTask::Rename(task) => { if let Some(CreatedEntry::Included(entry)) = - task.await.notify_async_err(cx) + task.await.notify_async_err(NotificationSource::File, cx) { last_succeed = Some(entry); } } PasteTask::Copy(task) => { - if let Some(Some(entry)) = task.await.notify_async_err(cx) { + if let Some(Some(entry)) = + task.await.notify_async_err(NotificationSource::File, cx) + { last_succeed = Some(entry); } } @@ -3209,6 +3215,7 @@ impl ProjectPanel { notification_id.clone(), format!("Downloading 0/{} files...", total_files), ), + NotificationSource::File, cx, ); }) @@ -3229,6 +3236,7 @@ impl ProjectPanel { total_files ), ), + NotificationSource::File, cx, ); }) @@ -3262,6 +3270,7 @@ impl ProjectPanel { notification_id.clone(), format!("Downloaded {} files", total_files), ), + NotificationSource::File, cx, ); }) diff --git a/crates/recent_projects/src/dev_container_suggest.rs b/crates/recent_projects/src/dev_container_suggest.rs index fd7fe4757a0f629579c5a5fdae7b16f12f1bba7a..9df04e789881aa955f512a0adaf5650c01ce1491 100644 --- a/crates/recent_projects/src/dev_container_suggest.rs +++ b/crates/recent_projects/src/dev_container_suggest.rs @@ -6,8 +6,8 @@ use std::sync::LazyLock; use ui::prelude::*; use util::rel_path::RelPath; use workspace::Workspace; -use workspace::notifications::NotificationId; use workspace::notifications::simple_message_notification::MessageNotification; +use workspace::notifications::{NotificationId, NotificationSource}; use worktree::UpdatedEntriesSet; const DEV_CONTAINER_SUGGEST_KEY: &str = "dev_container_suggest_dismissed"; @@ -78,7 +78,7 @@ pub fn suggest_on_worktree_updated( SharedString::from(project_path.clone()), ); - workspace.show_notification(notification_id, cx, |cx| { + workspace.show_notification(notification_id, NotificationSource::DevContainer, cx, |cx| { cx.new(move |cx| { MessageNotification::new( "This project contains a Dev Container configuration file. Would you like to re-open it in a container?", diff --git a/crates/recent_projects/src/remote_connections.rs b/crates/recent_projects/src/remote_connections.rs index 9aabc635508ea812f89a843cefe840442760c84f..0ac34097c353e82d679d2b28abeb810f97c8c9c8 100644 --- a/crates/recent_projects/src/remote_connections.rs +++ b/crates/recent_projects/src/remote_connections.rs @@ -20,7 +20,8 @@ pub use settings::SshConnection; use settings::{DevContainerConnection, ExtendingVec, RegisterSetting, Settings, WslConnection}; use util::paths::PathWithPosition; use workspace::{ - AppState, OpenOptions, SerializedWorkspaceLocation, Workspace, find_existing_workspace, + AppState, NotificationSource, OpenOptions, SerializedWorkspaceLocation, Workspace, + find_existing_workspace, }; pub use remote_connection::{ @@ -175,7 +176,7 @@ pub async fn open_remote_project( _ = existing.update(cx, |workspace, _, cx| { for item in open_results.iter().flatten() { if let Err(e) = item { - workspace.show_error(&e, cx); + workspace.show_error(&e, NotificationSource::Remote, cx); } } }); diff --git a/crates/recent_projects/src/remote_servers.rs b/crates/recent_projects/src/remote_servers.rs index 5b719940f958ac0e4ecb6e186052e3e09987f80e..da8c469a8e6128c93efd88cd4312ee074116e1c0 100644 --- a/crates/recent_projects/src/remote_servers.rs +++ b/crates/recent_projects/src/remote_servers.rs @@ -9,6 +9,7 @@ use dev_container::{ DevContainerConfig, find_devcontainer_configs, start_dev_container_with_config, }; use editor::Editor; +use workspace::notifications::NotificationSource; use futures::{FutureExt, channel::oneshot, future::Shared}; use gpui::{ @@ -2375,6 +2376,7 @@ impl RemoteServerProjects { notification, ) .autohide(), + NotificationSource::Remote, cx, ); }) diff --git a/crates/repl/src/kernels/native_kernel.rs b/crates/repl/src/kernels/native_kernel.rs index 30e2740fb92c85f9b52e48b6e41593c639350344..1fbda97d762179d194bb9e0b66b170868e17c06f 100644 --- a/crates/repl/src/kernels/native_kernel.rs +++ b/crates/repl/src/kernels/native_kernel.rs @@ -220,6 +220,7 @@ impl NativeRunningKernel { ); }, ), + workspace::notifications::NotificationSource::Repl, cx, ); }) diff --git a/crates/search/src/search.rs b/crates/search/src/search.rs index ac337fb4c8f53e407178d9ccf1be7e91d89fadcb..ea0b1bb90409426b59d5562d54b763916d53b9d0 100644 --- a/crates/search/src/search.rs +++ b/crates/search/src/search.rs @@ -7,7 +7,7 @@ use project::search::SearchQuery; pub use project_search::ProjectSearchView; use ui::{ButtonStyle, IconButton, IconButtonShape}; use ui::{Tooltip, prelude::*}; -use workspace::notifications::NotificationId; +use workspace::notifications::{NotificationId, NotificationSource}; use workspace::{Toast, Workspace}; pub use zed_actions::search::ToggleIncludeIgnored; @@ -197,6 +197,7 @@ pub(crate) fn show_no_more_matches(window: &mut Window, cx: &mut App) { workspace.update(cx, |workspace, cx| { workspace.show_toast( Toast::new(notification_id.clone(), "No more matches").autohide(), + NotificationSource::Editor, cx, ); }) diff --git a/crates/snippets_ui/src/snippets_ui.rs b/crates/snippets_ui/src/snippets_ui.rs index c881d5276e6f96c5fc9b4db802aa3731e843852f..c0a5a10b3e9da006cadb9299cababc337465e9a1 100644 --- a/crates/snippets_ui/src/snippets_ui.rs +++ b/crates/snippets_ui/src/snippets_ui.rs @@ -18,7 +18,10 @@ use std::{ }; use ui::{HighlightedLabel, ListItem, ListItemSpacing, prelude::*}; use util::ResultExt; -use workspace::{ModalView, OpenOptions, OpenVisible, Workspace, notifications::NotifyResultExt}; +use workspace::{ + ModalView, OpenOptions, OpenVisible, Workspace, + notifications::{NotificationSource, NotifyResultExt}, +}; #[derive(Eq, Hash, PartialEq)] struct ScopeName(Cow<'static, str>); @@ -93,7 +96,7 @@ fn open_folder( _: &mut Window, cx: &mut Context, ) { - fs::create_dir_all(snippets_dir()).notify_err(workspace, cx); + fs::create_dir_all(snippets_dir()).notify_err(workspace, NotificationSource::Editor, cx); cx.open_with_system(snippets_dir().borrow()); } diff --git a/crates/title_bar/src/title_bar.rs b/crates/title_bar/src/title_bar.rs index ff14c6fa25bfa3b52bfdd34433548431a042bc2b..d16190a335f5364b3096c56e2bbe2faf48893251 100644 --- a/crates/title_bar/src/title_bar.rs +++ b/crates/title_bar/src/title_bar.rs @@ -43,7 +43,10 @@ use ui::{ }; use update_version::UpdateVersion; use util::ResultExt; -use workspace::{SwitchProject, ToggleWorktreeSecurity, Workspace, notifications::NotifyResultExt}; +use workspace::{ + SwitchProject, ToggleWorktreeSecurity, Workspace, + notifications::{NotificationSource, NotifyResultExt}, +}; use zed_actions::OpenRemote; pub use onboarding_banner::restore_banner; @@ -945,7 +948,7 @@ impl TitleBar { client .sign_in_with_optional_connect(true, cx) .await - .notify_async_err(cx); + .notify_async_err(NotificationSource::Collab, cx); }) .detach(); }) diff --git a/crates/vim/src/command.rs b/crates/vim/src/command.rs index 423c3b387b197edd2d8e86398b09157fdcb7711a..48d6566e5883ff1763774cc98ef2470a240e14da 100644 --- a/crates/vim/src/command.rs +++ b/crates/vim/src/command.rs @@ -36,7 +36,10 @@ use util::{ rel_path::{RelPath, RelPathBuf}, }; use workspace::{Item, SaveIntent, Workspace, notifications::NotifyResultExt}; -use workspace::{SplitDirection, notifications::DetachAndPromptErr}; +use workspace::{ + SplitDirection, + notifications::{DetachAndPromptErr, NotificationSource}, +}; use zed_actions::{OpenDocs, RevealTarget}; use crate::{ @@ -892,7 +895,7 @@ pub fn register(editor: &mut Editor, cx: &mut Context) { return; }; workspace.update(cx, |workspace, cx| { - e.notify_err(workspace, cx); + e.notify_err(workspace, NotificationSource::Editor, cx); }); } }); @@ -936,7 +939,7 @@ pub fn register(editor: &mut Editor, cx: &mut Context) { return; }; workspace.update(cx, |workspace, cx| { - e.notify_err(workspace, cx); + e.notify_err(workspace, NotificationSource::Editor, cx); }); return; } @@ -2136,7 +2139,7 @@ impl OnMatchingLines { return; }; workspace.update(cx, |workspace, cx| { - e.notify_err(workspace, cx); + e.notify_err(workspace, NotificationSource::Editor, cx); }); return; } @@ -2153,7 +2156,7 @@ impl OnMatchingLines { return; }; workspace.update(cx, |workspace, cx| { - e.notify_err(workspace, cx); + e.notify_err(workspace, NotificationSource::Editor, cx); }); return; } diff --git a/crates/vim/src/normal/search.rs b/crates/vim/src/normal/search.rs index c11784d163e18451129656aa92d23dba568bd723..8ea6f0a3b35073c368b5c0d005cd924eea692e8c 100644 --- a/crates/vim/src/normal/search.rs +++ b/crates/vim/src/normal/search.rs @@ -7,7 +7,10 @@ use serde::Deserialize; use settings::Settings; use std::{iter::Peekable, str::Chars}; use util::serde::default_true; -use workspace::{notifications::NotifyResultExt, searchable::Direction}; +use workspace::{ + notifications::{NotificationSource, NotifyResultExt}, + searchable::Direction, +}; use crate::{ Vim, VimSettings, @@ -571,7 +574,7 @@ impl Vim { anyhow::Ok(()) }) { workspace.update(cx, |workspace, cx| { - result.notify_err(workspace, cx); + result.notify_err(workspace, NotificationSource::Editor, cx); }) } let Some(search_bar) = pane.update(cx, |pane, cx| { diff --git a/crates/workspace/src/notifications.rs b/crates/workspace/src/notifications.rs index 10437743df39b22722638357976d5a8d6224eaf8..425132ad2d42199a2cba1857c9f6e0a5a5ac775a 100644 --- a/crates/workspace/src/notifications.rs +++ b/crates/workspace/src/notifications.rs @@ -9,11 +9,13 @@ use markdown::{Markdown, MarkdownElement, MarkdownStyle}; use parking_lot::Mutex; use project::project_settings::ProjectSettings; use settings::Settings; +use telemetry; use theme::ThemeSettings; +use std::any::TypeId; use std::ops::Deref; use std::sync::{Arc, LazyLock}; -use std::{any::TypeId, time::Duration}; +use std::time::Duration; use ui::{CopyButton, Tooltip, prelude::*}; use util::ResultExt; @@ -68,6 +70,153 @@ pub trait Notification: pub struct SuppressEvent; +/// Source categories for notification telemetry. +/// These help identify which part of Zed generated a notification. +#[derive(Clone, Copy, Debug, Default)] +pub enum NotificationSource { + /// Language server notifications (errors, warnings, info from LSP) + Lsp, + /// Settings and keymap parse/migration errors + Settings, + /// App update notifications, release notes + Update, + /// Extension suggestions, dev extension errors + Extension, + /// Git blame errors, commit message generation failures + Git, + /// Local settings/tasks/debug errors, project-level issues + Project, + /// Collaboration notifications (contact requests, channel invites) + Collab, + /// WSL filesystem warnings, SSH/remote project errors + Remote, + /// Database load failures + Database, + /// File access errors, file drop errors + File, + /// Dev container suggestions + DevContainer, + /// Agent/assistant related notifications + Agent, + /// Copilot related notifications + Copilot, + /// Editor operations (permalinks, encoding, search) + Editor, + /// Task execution notifications + Task, + /// CLI installation notifications + Cli, + /// REPL/Jupyter kernel notifications + Repl, + /// Generic system notifications (fallback) + #[default] + System, +} + +impl NotificationSource { + fn as_str(&self) -> &'static str { + match self { + NotificationSource::Lsp => "lsp", + NotificationSource::Settings => "settings", + NotificationSource::Update => "update", + NotificationSource::Extension => "extension", + NotificationSource::Git => "git", + NotificationSource::Project => "project", + NotificationSource::Collab => "collab", + NotificationSource::Remote => "remote", + NotificationSource::Database => "database", + NotificationSource::File => "file", + NotificationSource::DevContainer => "dev_container", + NotificationSource::Agent => "agent", + NotificationSource::Copilot => "copilot", + NotificationSource::Editor => "editor", + NotificationSource::Task => "task", + NotificationSource::Cli => "cli", + NotificationSource::Repl => "repl", + NotificationSource::System => "system", + } + } +} + +#[derive(Clone)] +struct NotificationTelemetry { + notification_type: &'static str, + source: &'static str, + lsp_name: Option, + level: Option<&'static str>, + has_actions: bool, + notification_id: String, + is_auto_dismissing: bool, +} + +impl NotificationTelemetry { + fn for_language_server_prompt(prompt: &LanguageServerPrompt, id: &NotificationId) -> Self { + let (level, has_actions, lsp_name) = prompt + .request + .as_ref() + .map(|req| { + let level = match req.level { + PromptLevel::Critical => "critical", + PromptLevel::Warning => "warning", + PromptLevel::Info => "info", + }; + ( + Some(level), + !req.actions.is_empty(), + Some(req.lsp_name.clone()), + ) + }) + .unwrap_or((None, false, None)); + + Self { + notification_type: "lsp", + source: "lsp", + lsp_name, + level, + has_actions, + notification_id: format!("{:?}", id), + is_auto_dismissing: !has_actions, + } + } + + fn for_error_message_prompt(source: NotificationSource, id: &NotificationId) -> Self { + Self { + notification_type: "error", + source: source.as_str(), + lsp_name: None, + level: Some("critical"), + has_actions: false, + notification_id: format!("{:?}", id), + is_auto_dismissing: false, + } + } + + fn for_message_notification(source: NotificationSource, id: &NotificationId) -> Self { + Self { + notification_type: "notification", + source: source.as_str(), + lsp_name: None, + level: None, + has_actions: false, + notification_id: format!("{:?}", id), + is_auto_dismissing: false, + } + } + + fn report(self) { + telemetry::event!( + "Notification Shown", + notification_type = self.notification_type, + source = self.source, + lsp_name = self.lsp_name, + level = self.level, + has_actions = self.has_actions, + notification_id = self.notification_id, + is_auto_dismissing = self.is_auto_dismissing, + ); + } +} + impl Workspace { #[cfg(any(test, feature = "test-support"))] pub fn notification_ids(&self) -> Vec { @@ -81,9 +230,12 @@ impl Workspace { pub fn show_notification( &mut self, id: NotificationId, + source: NotificationSource, cx: &mut Context, build_notification: impl FnOnce(&mut Context) -> Entity, ) { + let mut telemetry_data: Option = None; + self.show_notification_without_handling_dismiss_events(&id, cx, |cx| { let notification = build_notification(cx); cx.subscribe(¬ification, { @@ -104,6 +256,11 @@ impl Workspace { if let Ok(prompt) = AnyEntity::from(notification.clone()).downcast::() { + telemetry_data = Some(NotificationTelemetry::for_language_server_prompt( + prompt.read(cx), + &id, + )); + let is_prompt_without_actions = prompt .read(cx) .request @@ -133,9 +290,24 @@ impl Workspace { }); } } + } else if AnyEntity::from(notification.clone()) + .downcast::() + .is_ok() + { + telemetry_data = Some(NotificationTelemetry::for_error_message_prompt(source, &id)); + } else if AnyEntity::from(notification.clone()) + .downcast::() + .is_ok() + { + telemetry_data = Some(NotificationTelemetry::for_message_notification(source, &id)); } + notification.into() }); + + if let Some(telemetry) = telemetry_data { + telemetry.report(); + } } /// Shows a notification in this workspace's window. Caller must handle dismiss. @@ -158,11 +330,11 @@ impl Workspace { cx.notify(); } - pub fn show_error(&mut self, err: &E, cx: &mut Context) + pub fn show_error(&mut self, err: &E, source: NotificationSource, cx: &mut Context) where E: std::fmt::Debug + std::fmt::Display, { - self.show_notification(workspace_error_notification_id(), cx, |cx| { + self.show_notification(workspace_error_notification_id(), source, cx, |cx| { cx.new(|cx| ErrorMessagePrompt::new(format!("Error: {err}"), cx)) }); } @@ -170,14 +342,19 @@ impl Workspace { pub fn show_portal_error(&mut self, err: String, cx: &mut Context) { struct PortalError; - self.show_notification(NotificationId::unique::(), cx, |cx| { - cx.new(|cx| { - ErrorMessagePrompt::new(err.to_string(), cx).with_link_button( - "See docs", - "https://zed.dev/docs/linux#i-cant-open-any-files", - ) - }) - }); + self.show_notification( + NotificationId::unique::(), + NotificationSource::System, + cx, + |cx| { + cx.new(|cx| { + ErrorMessagePrompt::new(err.to_string(), cx).with_link_button( + "See docs", + "https://zed.dev/docs/linux#i-cant-open-any-files", + ) + }) + }, + ); } pub fn dismiss_notification(&mut self, id: &NotificationId, cx: &mut Context) { @@ -191,9 +368,9 @@ impl Workspace { }); } - pub fn show_toast(&mut self, toast: Toast, cx: &mut Context) { + pub fn show_toast(&mut self, toast: Toast, source: NotificationSource, cx: &mut Context) { self.dismiss_notification(&toast.id, cx); - self.show_notification(toast.id.clone(), cx, |cx| { + self.show_notification(toast.id.clone(), source, cx, |cx| { cx.new(|cx| match toast.on_click.as_ref() { Some((click_msg, on_click)) => { let on_click = on_click.clone(); @@ -1002,9 +1179,23 @@ impl AppNotifications { /// exist. If the notification is dismissed within any workspace, it will be removed from all. pub fn show_app_notification( id: NotificationId, + source: NotificationSource, cx: &mut App, build_notification: impl Fn(&mut Context) -> Entity + 'static + Send + Sync, ) { + let telemetry_data = if TypeId::of::() == TypeId::of::() { + Some(NotificationTelemetry::for_error_message_prompt(source, &id)) + } else if TypeId::of::() == TypeId::of::() + { + Some(NotificationTelemetry::for_message_notification(source, &id)) + } else { + None + }; + + if let Some(telemetry) = telemetry_data { + telemetry.report(); + } + // Defer notification creation so that windows on the stack can be returned to GPUI cx.defer(move |cx| { // Handle dismiss events by removing the notification from all workspaces. @@ -1073,13 +1264,21 @@ pub fn dismiss_app_notification(id: &NotificationId, cx: &mut App) { pub trait NotifyResultExt { type Ok; - fn notify_err(self, workspace: &mut Workspace, cx: &mut Context) - -> Option; + fn notify_err( + self, + workspace: &mut Workspace, + source: NotificationSource, + cx: &mut Context, + ) -> Option; - fn notify_async_err(self, cx: &mut AsyncWindowContext) -> Option; + fn notify_async_err( + self, + source: NotificationSource, + cx: &mut AsyncWindowContext, + ) -> Option; /// Notifies the active workspace if there is one, otherwise notifies all workspaces. - fn notify_app_err(self, cx: &mut App) -> Option; + fn notify_app_err(self, source: NotificationSource, cx: &mut App) -> Option; } impl NotifyResultExt for std::result::Result @@ -1088,25 +1287,34 @@ where { type Ok = T; - fn notify_err(self, workspace: &mut Workspace, cx: &mut Context) -> Option { + fn notify_err( + self, + workspace: &mut Workspace, + source: NotificationSource, + cx: &mut Context, + ) -> Option { match self { Ok(value) => Some(value), Err(err) => { log::error!("Showing error notification in workspace: {err:?}"); - workspace.show_error(&err, cx); + workspace.show_error(&err, source, cx); None } } } - fn notify_async_err(self, cx: &mut AsyncWindowContext) -> Option { + fn notify_async_err( + self, + source: NotificationSource, + cx: &mut AsyncWindowContext, + ) -> Option { match self { Ok(value) => Some(value), Err(err) => { log::error!("{err:?}"); cx.update_root(|view, _, cx| { if let Ok(workspace) = view.downcast::() { - workspace.update(cx, |workspace, cx| workspace.show_error(&err, cx)) + workspace.update(cx, |workspace, cx| workspace.show_error(&err, source, cx)) } }) .ok(); @@ -1115,13 +1323,13 @@ where } } - fn notify_app_err(self, cx: &mut App) -> Option { + fn notify_app_err(self, source: NotificationSource, cx: &mut App) -> Option { match self { Ok(value) => Some(value), Err(err) => { let message: SharedString = format!("Error: {err}").into(); log::error!("Showing error notification in app: {message}"); - show_app_notification(workspace_error_notification_id(), cx, { + show_app_notification(workspace_error_notification_id(), source, cx, { move |cx| { cx.new({ let message = message.clone(); @@ -1137,7 +1345,7 @@ where } pub trait NotifyTaskExt { - fn detach_and_notify_err(self, window: &mut Window, cx: &mut App); + fn detach_and_notify_err(self, source: NotificationSource, window: &mut Window, cx: &mut App); } impl NotifyTaskExt for Task> @@ -1145,9 +1353,9 @@ where E: std::fmt::Debug + std::fmt::Display + Sized + 'static, R: 'static, { - fn detach_and_notify_err(self, window: &mut Window, cx: &mut App) { + fn detach_and_notify_err(self, source: NotificationSource, window: &mut Window, cx: &mut App) { window - .spawn(cx, async move |cx| self.await.notify_async_err(cx)) + .spawn(cx, async move |cx| self.await.notify_async_err(source, cx)) .detach(); } } @@ -1252,7 +1460,7 @@ mod tests { lsp_name.to_string(), ); let notification_id = NotificationId::composite::(request.id); - workspace.show_notification(notification_id, cx, |cx| { + workspace.show_notification(notification_id, NotificationSource::Lsp, cx, |cx| { cx.new(|cx| LanguageServerPrompt::new(request, cx)) }); }) @@ -1311,7 +1519,7 @@ mod tests { ); let notification_id = NotificationId::composite::(request.id); - workspace.show_notification(notification_id, cx, |cx| { + workspace.show_notification(notification_id, NotificationSource::Lsp, cx, |cx| { cx.new(|cx| LanguageServerPrompt::new(request, cx)) }); }) @@ -1362,7 +1570,7 @@ mod tests { "test_server".to_string(), ); let notification_id = NotificationId::composite::(request.id); - workspace.show_notification(notification_id, cx, |cx| { + workspace.show_notification(notification_id, NotificationSource::Lsp, cx, |cx| { cx.new(|cx| LanguageServerPrompt::new(request, cx)) }); }); @@ -1405,7 +1613,7 @@ mod tests { "test_server".to_string(), ); let notification_id = NotificationId::composite::(request.id); - workspace.show_notification(notification_id, cx, |cx| { + workspace.show_notification(notification_id, NotificationSource::Lsp, cx, |cx| { cx.new(|cx| LanguageServerPrompt::new(request, cx)) }); }); diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index be5c1efbdf7b158e3b8f80828fb75eb4a2307d61..fce6b4ebd13b703aabf3a912261ccc2df6829ef4 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -9,7 +9,7 @@ use crate::{ TabContentParams, TabTooltipContent, WeakItemHandle, }, move_item, - notifications::NotifyResultExt, + notifications::{NotificationSource, NotifyResultExt}, toolbar::Toolbar, utility_pane::UtilityPaneSlot, workspace_settings::{AutosaveSetting, TabBarSettings, WorkspaceSettings}, @@ -3890,8 +3890,9 @@ impl Pane { { let load_path_task = workspace.load_path(project_path.clone(), window, cx); cx.spawn_in(window, async move |workspace, cx| { - if let Some((project_entry_id, build_item)) = - load_path_task.await.notify_async_err(cx) + if let Some((project_entry_id, build_item)) = load_path_task + .await + .notify_async_err(NotificationSource::File, cx) { let (to_pane, new_item_handle) = workspace .update_in(cx, |workspace, window, cx| { @@ -3961,6 +3962,7 @@ impl Pane { if workspace.project().read(cx).is_via_collab() { workspace.show_error( &anyhow::anyhow!("Cannot drop files on a remote project"), + NotificationSource::File, cx, ); true @@ -4018,7 +4020,7 @@ impl Pane { _ = workspace.update_in(cx, |workspace, window, cx| { for item in opened_items.into_iter().flatten() { if let Err(e) = item { - workspace.show_error(&e, cx); + workspace.show_error(&e, NotificationSource::File, cx); } } if to_pane.read(cx).items_len() == 0 { diff --git a/crates/workspace/src/tasks.rs b/crates/workspace/src/tasks.rs index f85e1488f97491a73314297d91c597bd7d3bb841..403b27d59527f44523b3880a00ccf3f052402cdb 100644 --- a/crates/workspace/src/tasks.rs +++ b/crates/workspace/src/tasks.rs @@ -10,7 +10,10 @@ use task::{ }; use ui::Window; -use crate::{Toast, Workspace, notifications::NotificationId}; +use crate::{ + Toast, Workspace, + notifications::{NotificationId, NotificationSource}, +}; impl Workspace { pub fn schedule_task( @@ -90,7 +93,11 @@ impl Workspace { log::error!("Task spawn failed: {e:#}"); _ = w.update(cx, |w, cx| { let id = NotificationId::unique::(); - w.show_toast(Toast::new(id, format!("Task spawn failed: {e}")), cx); + w.show_toast( + Toast::new(id, format!("Task spawn failed: {e}")), + NotificationSource::Task, + cx, + ); }) } None => log::debug!("Task spawn got cancelled"), diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 9f242d670d1ca7004c703496346e7bc196f969e5..e24c367b4a3a2c0de97cba2c95afbf3230af7e47 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -59,6 +59,7 @@ use itertools::Itertools; use language::{Buffer, LanguageRegistry, Rope, language_settings::all_language_settings}; pub use modal_layer::*; use node_runtime::NodeRuntime; +pub use notifications::NotificationSource; use notifications::{ DetachAndPromptErr, Notifications, dismiss_app_notification, simple_message_notification::MessageNotification, @@ -1358,6 +1359,7 @@ impl Workspace { link, } => this.show_notification( NotificationId::named(notification_id.clone()), + NotificationSource::Project, cx, |cx| { let mut notification = MessageNotification::new(message.clone(), cx); @@ -1380,6 +1382,7 @@ impl Workspace { this.show_notification( NotificationId::composite::(request.id), + NotificationSource::Lsp, cx, |cx| { cx.new(|cx| { @@ -3132,6 +3135,7 @@ impl Workspace { if project.is_via_collab() { self.show_error( &anyhow!("You cannot add folders to someone else's project"), + NotificationSource::Collab, cx, ); return; @@ -7061,6 +7065,7 @@ fn notify_if_database_failed(workspace: WindowHandle, cx: &mut AsyncA workspace.show_notification( NotificationId::unique::(), + NotificationSource::Database, cx, |cx| { cx.new(|cx| { @@ -8460,7 +8465,7 @@ pub fn open_paths( _ = existing.update(cx, |workspace, _, cx| { for item in open_task.iter().flatten() { if let Err(e) = item { - workspace.show_error(&e, cx); + workspace.show_error(&e, NotificationSource::File, cx); } } }); @@ -8487,7 +8492,7 @@ pub fn open_paths( workspace .update(cx, move |workspace, _window, cx| { struct OpenInWsl; - workspace.show_notification(NotificationId::unique::(), cx, move |cx| { + workspace.show_notification(NotificationId::unique::(), NotificationSource::Remote, cx, move |cx| { let display_path = util::markdown::MarkdownInlineCode(&path.to_string_lossy()); let msg = format!("{display_path} is inside a WSL filesystem, some features may not work unless you open it with WSL remote"); cx.new(move |cx| { @@ -8749,10 +8754,14 @@ async fn open_remote_project_inner( for error in project_path_errors { if error.error_code() == proto::ErrorCode::DevServerProjectPathDoesNotExist { if let Some(path) = error.error_tag("path") { - workspace.show_error(&anyhow!("'{path}' does not exist"), cx) + workspace.show_error( + &anyhow!("'{path}' does not exist"), + NotificationSource::Remote, + cx, + ) } } else { - workspace.show_error(&error, cx) + workspace.show_error(&error, NotificationSource::Remote, cx) } } })?; diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 322b0d2a245894362b501fff1746ea69c26a137c..249c67d6663383fa99fee04be52066c6f66e3300 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -55,7 +55,8 @@ use util::{ResultExt, TryFutureExt, maybe}; use uuid::Uuid; use workspace::{ AppState, PathList, SerializedWorkspaceLocation, Toast, Workspace, WorkspaceId, - WorkspaceSettings, WorkspaceStore, notifications::NotificationId, + WorkspaceSettings, WorkspaceStore, + notifications::{NotificationId, NotificationSource}, }; use zed::{ OpenListener, OpenRequest, RawOpenRequest, app_menus, build_window_options, @@ -938,6 +939,7 @@ fn handle_open_request(request: OpenRequest, app_state: Arc, cx: &mut format!("Imported shared thread from {}", response.sharer_username), ) .autohide(), + NotificationSource::Agent, cx, ); })?; @@ -1360,8 +1362,11 @@ async fn restore_or_create_workspace(app_state: Arc, cx: &mut AsyncApp { workspace .update(cx, |workspace, _, cx| { - workspace - .show_toast(Toast::new(NotificationId::unique::<()>(), message), cx) + workspace.show_toast( + Toast::new(NotificationId::unique::<()>(), message), + NotificationSource::System, + cx, + ) }) .ok(); return true; diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index f214e930c6016058b4b2da2e4517978841f74006..28fbf3a0ddad1bade2233c1e7bf3bcf6938737c1 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -84,7 +84,8 @@ use util::{ResultExt, asset_str, maybe}; use uuid::Uuid; use vim_mode_setting::VimModeSetting; use workspace::notifications::{ - NotificationId, SuppressEvent, dismiss_app_notification, show_app_notification, + NotificationId, NotificationSource, SuppressEvent, dismiss_app_notification, + show_app_notification, }; use workspace::utility_pane::utility_slot_for_dock_position; use workspace::{ @@ -803,6 +804,7 @@ fn register_actions( "Opening this URL in a browser failed because the URL is invalid: {}\n\nError was: {e}", action.url ), + NotificationSource::System, cx, ); } @@ -999,6 +1001,7 @@ fn register_actions( ReleaseChannel::global(cx).display_name() ), ), + NotificationSource::Cli, cx, ) })?; @@ -1410,6 +1413,7 @@ fn open_log_file(workspace: &mut Workspace, window: &mut Window, cx: &mut Contex .update(cx, |workspace, cx| { workspace.show_notification( NotificationId::unique::(), + NotificationSource::System, cx, |cx| { cx.new(|cx| { @@ -1494,7 +1498,7 @@ fn notify_settings_errors(result: settings::SettingsParseResult, is_user: bool, false // Local settings errors are displayed by the projects } else { - show_app_notification(id, cx, move |cx| { + show_app_notification(id, NotificationSource::Settings, cx, move |cx| { cx.new(|cx| { MessageNotification::new(format!("Invalid user settings file\n{error}"), cx) .primary_message("Open Settings File") @@ -1524,7 +1528,7 @@ fn notify_settings_errors(result: settings::SettingsParseResult, is_user: bool, } settings::MigrationStatus::Failed { error: err } => { if !showed_parse_error { - show_app_notification(id, cx, move |cx| { + show_app_notification(id, NotificationSource::Settings, cx, move |cx| { cx.new(|cx| { MessageNotification::new( format!( @@ -1730,17 +1734,22 @@ fn show_keymap_file_json_error( ) { let message: SharedString = format!("JSON parse error in keymap file. Bindings not reloaded.\n\n{error}").into(); - show_app_notification(notification_id, cx, move |cx| { - cx.new(|cx| { - MessageNotification::new(message.clone(), cx) - .primary_message("Open Keymap File") - .primary_icon(IconName::Settings) - .primary_on_click(|window, cx| { - window.dispatch_action(zed_actions::OpenKeymapFile.boxed_clone(), cx); - cx.emit(DismissEvent); - }) - }) - }); + show_app_notification( + notification_id, + NotificationSource::Settings, + cx, + move |cx| { + cx.new(|cx| { + MessageNotification::new(message.clone(), cx) + .primary_message("Open Keymap File") + .primary_icon(IconName::Settings) + .primary_on_click(|window, cx| { + window.dispatch_action(zed_actions::OpenKeymapFile.boxed_clone(), cx); + cx.emit(DismissEvent); + }) + }) + }, + ); } fn show_keymap_file_load_error( @@ -1785,29 +1794,34 @@ fn show_markdown_app_notification( let primary_button_message = primary_button_message.clone(); let primary_button_on_click = Arc::new(primary_button_on_click); cx.update(|cx| { - show_app_notification(notification_id, cx, move |cx| { - let workspace_handle = cx.entity().downgrade(); - let parsed_markdown = parsed_markdown.clone(); - let primary_button_message = primary_button_message.clone(); - let primary_button_on_click = primary_button_on_click.clone(); - cx.new(move |cx| { - MessageNotification::new_from_builder(cx, move |window, cx| { - image_cache(retain_all("notification-cache")) - .child(div().text_ui(cx).child( - markdown_preview::markdown_renderer::render_parsed_markdown( - &parsed_markdown.clone(), - Some(workspace_handle.clone()), - window, - cx, - ), - )) - .into_any() + show_app_notification( + notification_id, + NotificationSource::Settings, + cx, + move |cx| { + let workspace_handle = cx.entity().downgrade(); + let parsed_markdown = parsed_markdown.clone(); + let primary_button_message = primary_button_message.clone(); + let primary_button_on_click = primary_button_on_click.clone(); + cx.new(move |cx| { + MessageNotification::new_from_builder(cx, move |window, cx| { + image_cache(retain_all("notification-cache")) + .child(div().text_ui(cx).child( + markdown_preview::markdown_renderer::render_parsed_markdown( + &parsed_markdown.clone(), + Some(workspace_handle.clone()), + window, + cx, + ), + )) + .into_any() + }) + .primary_message(primary_button_message) + .primary_icon(IconName::Settings) + .primary_on_click_arc(primary_button_on_click) }) - .primary_message(primary_button_message) - .primary_icon(IconName::Settings) - .primary_on_click_arc(primary_button_on_click) - }) - }) + }, + ) }); }) .detach(); @@ -2007,9 +2021,12 @@ fn open_local_file( } else { struct NoOpenFolders; - workspace.show_notification(NotificationId::unique::(), cx, |cx| { - cx.new(|cx| MessageNotification::new("This project has no folders open.", cx)) - }) + workspace.show_notification( + NotificationId::unique::(), + NotificationSource::Project, + cx, + |cx| cx.new(|cx| MessageNotification::new("This project has no folders open.", cx)), + ) } } @@ -2184,6 +2201,7 @@ fn capture_recent_audio(workspace: &mut Workspace, _: &mut Window, cx: &mut Cont workspace.show_notification( NotificationId::unique::(), + NotificationSource::System, cx, |cx| cx.new(CaptureRecentAudioNotification::new), ); diff --git a/crates/zed/src/zed/migrate.rs b/crates/zed/src/zed/migrate.rs index 2452f17d04007364861e9a262b492155daec0c55..481529d52c2c372d43e7755cbf85d6f82876cb85 100644 --- a/crates/zed/src/zed/migrate.rs +++ b/crates/zed/src/zed/migrate.rs @@ -4,7 +4,7 @@ use fs::Fs; use migrator::{migrate_keymap, migrate_settings}; use settings::{KeymapFile, Settings, SettingsStore}; use util::ResultExt; -use workspace::notifications::NotifyTaskExt; +use workspace::notifications::{NotificationSource, NotifyTaskExt}; use std::sync::Arc; @@ -241,11 +241,19 @@ impl Render for MigrationBanner { match migration_type { Some(MigrationType::Keymap) => { cx.background_spawn(write_keymap_migration(fs.clone())) - .detach_and_notify_err(window, cx); + .detach_and_notify_err( + NotificationSource::Settings, + window, + cx, + ); } Some(MigrationType::Settings) => { cx.background_spawn(write_settings_migration(fs.clone())) - .detach_and_notify_err(window, cx); + .detach_and_notify_err( + NotificationSource::Settings, + window, + cx, + ); } None => unreachable!(), } diff --git a/crates/zed/src/zed/telemetry_log.rs b/crates/zed/src/zed/telemetry_log.rs index 06e13ef5d86fb665151b13ce01de5a60def9ba15..7e0b95e3d831f9b77650ef77fc5d24c9ab6688af 100644 --- a/crates/zed/src/zed/telemetry_log.rs +++ b/crates/zed/src/zed/telemetry_log.rs @@ -22,7 +22,7 @@ use ui::{ }; use workspace::{ Item, ItemHandle, Toast, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace, - notifications::NotificationId, + notifications::{NotificationId, NotificationSource}, }; const MAX_EVENTS: usize = 10_000; @@ -37,7 +37,7 @@ pub fn init(cx: &mut App) { cx.subscribe(&telemetry_log, |workspace, _, event, cx| { let TelemetryLogEvent::ShowToast(toast) = event; - workspace.show_toast(toast.clone(), cx); + workspace.show_toast(toast.clone(), NotificationSource::System, cx); }) .detach();