diff --git a/lightningbeam-ui/Cargo.toml b/lightningbeam-ui/Cargo.toml index 1d7b708..f52917f 100644 --- a/lightningbeam-ui/Cargo.toml +++ b/lightningbeam-ui/Cargo.toml @@ -39,3 +39,6 @@ resvg = "0.42" # Utilities pollster = "0.3" + +# Desktop notifications +notify-rust = "4.11" diff --git a/lightningbeam-ui/lightningbeam-editor/Cargo.toml b/lightningbeam-ui/lightningbeam-editor/Cargo.toml index 671897c..ead4c8a 100644 --- a/lightningbeam-ui/lightningbeam-editor/Cargo.toml +++ b/lightningbeam-ui/lightningbeam-editor/Cargo.toml @@ -48,3 +48,6 @@ rfd = "0.15" # Cross-platform config paths directories = "5.0" + +# Desktop notifications +notify-rust = { workspace = true } diff --git a/lightningbeam-ui/lightningbeam-editor/src/main.rs b/lightningbeam-ui/lightningbeam-editor/src/main.rs index a2c65e0..bad5281 100644 --- a/lightningbeam-ui/lightningbeam-editor/src/main.rs +++ b/lightningbeam-ui/lightningbeam-editor/src/main.rs @@ -28,6 +28,8 @@ mod default_instrument; mod export; +mod notifications; + mod effect_thumbnails; use effect_thumbnails::EffectThumbnailGenerator; @@ -2975,6 +2977,12 @@ impl eframe::App for EditorApp { ); // Close the progress dialog after a brief delay self.export_progress_dialog.close(); + + // Send desktop notification + if let Err(e) = notifications::notify_export_complete(output_path) { + // Log but don't fail - notifications are non-critical + eprintln!("⚠️ Could not send desktop notification: {}", e); + } } lightningbeam_core::export::ExportProgress::Error { ref message } => { eprintln!("❌ Export error: {}", message); @@ -2983,6 +2991,12 @@ impl eframe::App for EditorApp { 0.0, ); // Keep the dialog open to show the error + + // Send desktop notification for error + if let Err(e) = notifications::notify_export_error(message) { + // Log but don't fail - notifications are non-critical + eprintln!("⚠️ Could not send desktop notification: {}", e); + } } } } diff --git a/lightningbeam-ui/lightningbeam-editor/src/notifications.rs b/lightningbeam-ui/lightningbeam-editor/src/notifications.rs new file mode 100644 index 0000000..573626e --- /dev/null +++ b/lightningbeam-ui/lightningbeam-editor/src/notifications.rs @@ -0,0 +1,54 @@ +//! Desktop notifications for export completion + +use notify_rust::Notification; +use std::path::Path; + +/// Send a desktop notification for successful export +/// +/// # Arguments +/// * `output_path` - Path to the exported file +/// +/// # Returns +/// Ok(()) if notification sent successfully, Err with log message if failed +pub fn notify_export_complete(output_path: &Path) -> Result<(), String> { + let filename = output_path + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or("unknown"); + + Notification::new() + .summary("Export Complete") + .body(&format!("Successfully exported: {}", filename)) + .icon("dialog-information") // Standard icon name (freedesktop.org) + .timeout(5000) // 5 seconds + .show() + .map_err(|e| format!("Failed to show notification: {}", e))?; + + Ok(()) +} + +/// Send a desktop notification for export error +/// +/// # Arguments +/// * `error_message` - The error message to display +/// +/// # Returns +/// Ok(()) if notification sent successfully, Err with log message if failed +pub fn notify_export_error(error_message: &str) -> Result<(), String> { + // Truncate very long error messages + let truncated = if error_message.len() > 100 { + format!("{}...", &error_message[..97]) + } else { + error_message.to_string() + }; + + Notification::new() + .summary("Export Failed") + .body(&truncated) + .icon("dialog-error") // Standard error icon + .timeout(10000) // 10 seconds for errors (longer to read) + .show() + .map_err(|e| format!("Failed to show notification: {}", e))?; + + Ok(()) +}