diff --git a/crates-tauri/yaak-app/src/lib.rs b/crates-tauri/yaak-app/src/lib.rs index 23da70a3..966c692b 100644 --- a/crates-tauri/yaak-app/src/lib.rs +++ b/crates-tauri/yaak-app/src/lib.rs @@ -1383,13 +1383,12 @@ async fn cmd_reload_plugins( app_handle: AppHandle, window: WebviewWindow, plugin_manager: State<'_, PluginManager>, -) -> YaakResult<()> { +) -> YaakResult> { let plugins = app_handle.db().list_plugins()?; let plugin_context = PluginContext::new(Some(window.label().to_string()), window.workspace_id()); - let _errors = plugin_manager.initialize_all_plugins(plugins, &plugin_context).await; - // Note: errors are returned but we don't show toasts here since this is a manual reload - Ok(()) + let errors = plugin_manager.initialize_all_plugins(plugins, &plugin_context).await; + Ok(errors) } #[tauri::command] @@ -1731,6 +1730,7 @@ pub fn run() { git_ext::cmd_git_rm_remote, // // Plugin commands + plugins_ext::cmd_plugin_init_errors, plugins_ext::cmd_plugins_install_from_directory, plugins_ext::cmd_plugins_search, plugins_ext::cmd_plugins_install, diff --git a/crates-tauri/yaak-app/src/plugins_ext.rs b/crates-tauri/yaak-app/src/plugins_ext.rs index 143ae769..714d7948 100644 --- a/crates-tauri/yaak-app/src/plugins_ext.rs +++ b/crates-tauri/yaak-app/src/plugins_ext.rs @@ -198,6 +198,13 @@ pub async fn cmd_plugins_uninstall( Ok(delete_and_uninstall(plugin_manager, &query_manager, &plugin_context, plugin_id).await?) } +#[command] +pub async fn cmd_plugin_init_errors( + plugin_manager: State<'_, PluginManager>, +) -> Result> { + Ok(plugin_manager.take_init_errors().await) +} + #[command] pub async fn cmd_plugins_updates( app_handle: AppHandle, @@ -306,7 +313,7 @@ pub fn init() -> TauriPlugin { dev_mode, ) .await - .expect("Failed to initialize plugins"); + .expect("Failed to start plugin runtime"); app_handle_clone.manage(manager); }); diff --git a/crates/yaak-plugins/src/manager.rs b/crates/yaak-plugins/src/manager.rs index 26400e63..91d02a59 100644 --- a/crates/yaak-plugins/src/manager.rs +++ b/crates/yaak-plugins/src/manager.rs @@ -50,6 +50,8 @@ pub struct PluginManager { vendored_plugin_dir: PathBuf, pub(crate) installed_plugin_dir: PathBuf, dev_mode: bool, + /// Errors from plugin initialization, retrievable once via `take_init_errors`. + init_errors: Arc>>, } /// Callback for plugin initialization events (e.g., toast notifications) @@ -93,6 +95,7 @@ impl PluginManager { vendored_plugin_dir, installed_plugin_dir, dev_mode, + init_errors: Default::default(), }; // Forward events to subscribers @@ -183,17 +186,21 @@ impl PluginManager { let init_errors = plugin_manager.initialize_all_plugins(plugins, plugin_context).await; if !init_errors.is_empty() { - let joined = init_errors - .into_iter() - .map(|(dir, err)| format!("{dir}: {err}")) - .collect::>() - .join("; "); - return Err(PluginErr(format!("Failed to initialize plugin(s): {joined}"))); + for (dir, err) in &init_errors { + warn!("Plugin failed to initialize: {dir}: {err}"); + } + *plugin_manager.init_errors.lock().await = init_errors; } Ok(plugin_manager) } + /// Take any initialization errors, clearing them from the manager. + /// Returns a list of `(plugin_directory, error_message)` pairs. + pub async fn take_init_errors(&self) -> Vec<(String, String)> { + std::mem::take(&mut *self.init_errors.lock().await) + } + /// Get the vendored plugin directory path (resolves dev mode path if applicable) pub fn get_plugins_dir(&self) -> PathBuf { if self.dev_mode { diff --git a/src-web/lib/initGlobalListeners.tsx b/src-web/lib/initGlobalListeners.tsx index 796c4234..1df5d723 100644 --- a/src-web/lib/initGlobalListeners.tsx +++ b/src-web/lib/initGlobalListeners.tsx @@ -123,6 +123,39 @@ export function initGlobalListeners() { console.log('Got plugin updates event', payload); showPluginUpdatesToast(payload); }); + + // Check for plugin initialization errors + invokeCmd<[string, string][]>('cmd_plugin_init_errors').then((errors) => { + for (const [dir, message] of errors) { + const dirBasename = dir.split('/').pop() ?? dir; + showToast({ + id: `plugin-init-error-${dirBasename}`, + color: 'warning', + timeout: null, + message: ( + +

Plugin failed to load

+

+ {dirBasename}: {message} +

+
+ ), + action: ({ hide }) => ( + + ), + }); + } + }); } function showUpdateInstalledToast(version: string) { diff --git a/src-web/lib/tauri.ts b/src-web/lib/tauri.ts index b8946752..df37d0d1 100644 --- a/src-web/lib/tauri.ts +++ b/src-web/lib/tauri.ts @@ -42,6 +42,7 @@ type TauriCmd = | 'cmd_new_child_window' | 'cmd_new_main_window' | 'cmd_plugin_info' + | 'cmd_plugin_init_errors' | 'cmd_reload_plugins' | 'cmd_render_template' | 'cmd_save_response'