| 59 | /// Notify when a task completes |
| 60 | #[cfg(not(test))] |
| 61 | pub fn notify_task_complete(&self, task_name: &str, success: bool, message: Option<&str>) { |
| 62 | let summary = if success { |
| 63 | "Task Completed" |
| 64 | } else { |
| 65 | "Task Failed" |
| 66 | }; |
| 67 | let body = format!( |
| 68 | "Task '{}' has {}{}", |
| 69 | task_name, |
| 70 | if success { |
| 71 | "completed successfully" |
| 72 | } else { |
| 73 | "failed" |
| 74 | }, |
| 75 | message.map(|m| format!(": {}", m)).unwrap_or_default() |
| 76 | ); |
| 77 | |
| 78 | // On macOS, use osascript which is reliable and non-blocking |
| 79 | #[cfg(target_os = "macos")] |
| 80 | { |
| 81 | self.show_macos_notification( |
| 82 | summary, |
| 83 | &body, |
| 84 | self.sound_enabled.load(Ordering::Relaxed), |
| 85 | ); |
| 86 | } |
| 87 | |
| 88 | // On Linux, use notify-rust with sound hints |
| 89 | #[cfg(target_os = "linux")] |
| 90 | { |
| 91 | if is_headless() { |
| 92 | return; |
| 93 | } |
| 94 | |
| 95 | let timeout_seconds = self.timeout_seconds; |
| 96 | let sound_enabled = self.sound_enabled.load(Ordering::Relaxed); |
| 97 | let sound_name = if success { |
| 98 | "message-new-instant" |
| 99 | } else { |
| 100 | "dialog-warning" |
| 101 | }; |
| 102 | |
| 103 | let mut notification = Notification::new(); |
| 104 | notification.summary(summary); |
| 105 | notification.body(&body); |
| 106 | notification.timeout(Timeout::Milliseconds(timeout_seconds * 1000)); |
| 107 | |
| 108 | if sound_enabled { |
| 109 | notification.hint(Hint::SoundName(sound_name.into())); |
| 110 | } |
| 111 | |
| 112 | let _ = notification.show(); |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | /// Notify when a task completes (no-op in test builds) |
| 117 | #[cfg(test)] |