diff --git a/src-tauri/src/app/notification.rs b/src-tauri/src/app/notification.rs index b781810eff..54b80bbef1 100644 --- a/src-tauri/src/app/notification.rs +++ b/src-tauri/src/app/notification.rs @@ -92,6 +92,9 @@ pub struct NotificationParams { title: String, body: String, icon: String, + #[serde(default)] + #[cfg_attr(not(target_os = "windows"), allow(dead_code))] + silent: bool, } #[derive(serde::Serialize)] @@ -166,11 +169,24 @@ pub fn send( } use tauri_plugin_notification::NotificationExt; - app.notification() + let notification = app + .notification() .builder() .title(¶ms.title) .body(¶ms.body) - .icon(¶ms.icon) + .icon(¶ms.icon); + + // On Windows, omitting sound makes notify-rust emit a silent toast. + // "Default" selects the OS notification sound while honoring Web + // Notification's silent option. Other platforms keep existing behavior. + #[cfg(target_os = "windows")] + let notification = if params.silent { + notification + } else { + notification.sound("Default") + }; + + notification .show() .map_err(|e| format!("Failed to show notification: {e}"))?; diff --git a/src-tauri/src/inject/event.js b/src-tauri/src/inject/event.js index 9edffdd384..e8e9e08a6e 100644 --- a/src-tauri/src/inject/event.js +++ b/src-tauri/src/inject/event.js @@ -1469,7 +1469,13 @@ document.addEventListener("DOMContentLoaded", () => { pendingFocusClick = null; invoke("send_notification", { - params: { id, title: this.title, body: this.body, icon: this.icon }, + params: { + id, + title: this.title, + body: this.body, + icon: this.icon, + silent: this.silent, + }, }) .then((outcome) => { if (liveNotifications.get(id) !== this) { diff --git a/tests/unit/event-link-guard.test.js b/tests/unit/event-link-guard.test.js index 80eff151ce..9ebf028040 100644 --- a/tests/unit/event-link-guard.test.js +++ b/tests/unit/event-link-guard.test.js @@ -846,6 +846,7 @@ describe("event link guard", () => { title: "Hello", body: "World", icon: "https://example.com/icon.png", + silent: false, }, }, ], diff --git a/tests/unit/notification-bridge.test.js b/tests/unit/notification-bridge.test.js index 80226a20f1..8008e3d2c8 100644 --- a/tests/unit/notification-bridge.test.js +++ b/tests/unit/notification-bridge.test.js @@ -140,6 +140,22 @@ function loadNotificationBridge({ } describe("notification bridge", () => { + it("forwards silent true, false, and default false to the native command", async () => { + const bridge = loadNotificationBridge(); + new bridge.Notification("Default"); + new bridge.Notification("Audible", { silent: false }); + new bridge.Notification("Quiet", { silent: true }); + await bridge.settle(); + const notifications = bridge.invokeCalls.filter( + (call) => call.command === "send_notification", + ); + expect(notifications.map((call) => call.payload.params.silent)).toEqual([ + false, + false, + true, + ]); + }); + it("preserves native event identity when EventTarget is not constructible", async () => { const bridge = loadNotificationBridge({ nativeClick: true,