diff --git a/Cargo.lock b/Cargo.lock index b964135..57f4a66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1924,6 +1924,16 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "gethostname" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc3655aa6818d65bc620d6911f05aa7b6aeb596291e1e9f79e52df85583d1e30" +dependencies = [ + "rustix", + "windows-targets 0.52.6", +] + [[package]] name = "getrandom" version = "0.1.16" @@ -3456,6 +3466,17 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "os_info" +version = "3.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e6520c8cc998c5741ee68ec1dc369fc47e5f0ea5320018ecf2a1ccd6328f48b" +dependencies = [ + "log", + "serde", + "windows-sys 0.52.0", +] + [[package]] name = "os_pipe" version = "1.2.1" @@ -4112,6 +4133,8 @@ dependencies = [ "anyhow", "base64 0.22.1", "log", + "objc2-app-kit", + "objc2-foundation", "radicle", "radicle-surf", "radicle-types", @@ -4122,6 +4145,7 @@ dependencies = [ "tauri-plugin-clipboard-manager", "tauri-plugin-dialog", "tauri-plugin-log", + "tauri-plugin-os", "tauri-plugin-shell", "tauri-plugin-window-state", "thiserror 1.0.69", @@ -5287,6 +5311,15 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "sys-locale" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" +dependencies = [ + "libc", +] + [[package]] name = "system-deps" version = "6.2.2" @@ -5583,6 +5616,24 @@ dependencies = [ "time", ] +[[package]] +name = "tauri-plugin-os" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dda2d571a9baf0664c1f2088db227e3072f9028602fafa885deade7547c3b738" +dependencies = [ + "gethostname 0.5.0", + "log", + "os_info", + "serde", + "serde_json", + "serialize-to-javascript", + "sys-locale", + "tauri", + "tauri-plugin", + "thiserror 2.0.7", +] + [[package]] name = "tauri-plugin-shell" version = "2.2.0" @@ -7288,7 +7339,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" dependencies = [ - "gethostname", + "gethostname 0.4.3", "rustix", "x11rb-protocol", ] diff --git a/crates/radicle-tauri/Cargo.toml b/crates/radicle-tauri/Cargo.toml index 8f67275..4efdeae 100644 --- a/crates/radicle-tauri/Cargo.toml +++ b/crates/radicle-tauri/Cargo.toml @@ -32,6 +32,16 @@ thiserror = { version = "1.0.64" } ts-rs = { version = "10.0.0", features = ["serde-json-impl", "no-serde-warnings"] } tokio = { version = "1.40.0", features = ["time"] } tauri-plugin-dialog = { version = "2.2.0" } +tauri-plugin-os = "2" + +[target.'cfg(target_os = "macos")'.dependencies] +objc2-foundation = { version = "0.2.2", features = [ + "NSThread", +] } +objc2-app-kit = { version = "0.2.2", features = [ + "NSApplication", + "NSDockTile", +] } [features] # by default Tauri runs in production mode diff --git a/crates/radicle-tauri/capabilities/default.json b/crates/radicle-tauri/capabilities/default.json index e7b13eb..e54fae8 100644 --- a/crates/radicle-tauri/capabilities/default.json +++ b/crates/radicle-tauri/capabilities/default.json @@ -2,7 +2,9 @@ "$schema": "../gen/schemas/desktop-schema.json", "identifier": "default", "description": "Capability for the main window", - "windows": ["main"], + "windows": [ + "main" + ], "permissions": [ "core:path:default", "core:event:default", @@ -17,6 +19,7 @@ "clipboard-manager:default", "clipboard-manager:allow-write-text", "log:default", - "dialog:default" + "dialog:default", + "os:default" ] -} +} \ No newline at end of file diff --git a/crates/radicle-tauri/src/commands/inbox.rs b/crates/radicle-tauri/src/commands/inbox.rs index 75fd6e9..cc0cde6 100644 --- a/crates/radicle-tauri/src/commands/inbox.rs +++ b/crates/radicle-tauri/src/commands/inbox.rs @@ -179,3 +179,28 @@ pub fn clear_notifications( Ok(()) } + +#[tauri::command] +#[cfg(target_os = "macos")] +pub fn set_badge(count: i32) { + use objc2_app_kit::NSApp; + use objc2_foundation::{MainThreadMarker, NSString}; + + let label = if count > 0 { + Some(NSString::from_str(&format!("{}", count))) + } else { + None + }; + + if let Some(thread) = MainThreadMarker::new() { + unsafe { + let app = NSApp(thread); + let dock_tile = app.dockTile(); + + dock_tile.setBadgeLabel(label.as_deref()); + dock_tile.display(); + } + } else { + eprintln!("Failed to obtain MainThreadMarker."); + } +} diff --git a/crates/radicle-tauri/src/lib.rs b/crates/radicle-tauri/src/lib.rs index 80bf9a4..dd227bb 100644 --- a/crates/radicle-tauri/src/lib.rs +++ b/crates/radicle-tauri/src/lib.rs @@ -15,6 +15,7 @@ use commands::{auth, cob, diff, inbox, profile, repo, thread}; pub fn run() { #[cfg(debug_assertions)] let builder = tauri::Builder::default() + .plugin(tauri_plugin_os::init()) .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_log::Builder::new().build()); #[cfg(not(debug_assertions))] @@ -84,9 +85,11 @@ pub fn run() { repo::diff_stats, repo::list_commits, diff::get_diff, - inbox::list_notifications, - inbox::count_notifications_by_repo, inbox::clear_notifications, + inbox::count_notifications_by_repo, + inbox::list_notifications, + #[cfg(target_os = "macos")] + inbox::set_badge, cob::get_embed, cob::save_embed_to_disk, cob::save_embed_by_path, diff --git a/package-lock.json b/package-lock.json index 334af3f..afb3685 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@tauri-apps/plugin-clipboard-manager": "^2.2.0", "@tauri-apps/plugin-dialog": "^2.2.0", "@tauri-apps/plugin-log": "^2.2.0", + "@tauri-apps/plugin-os": "^2.2.0", "@tauri-apps/plugin-shell": "^2.2.0", "@tauri-apps/plugin-window-state": "^2.2.0" }, @@ -1312,6 +1313,15 @@ "@tauri-apps/api": "^2.0.0" } }, + "node_modules/@tauri-apps/plugin-os": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-os/-/plugin-os-2.2.0.tgz", + "integrity": "sha512-HszbCdbisMlu5QhCNAN8YIWyz2v33abAWha6+uvV2CKX8P5VSct/y+kEe22JeyqrxCnWlQ3DRx7s49Byg7/0EA==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.0.0" + } + }, "node_modules/@tauri-apps/plugin-shell": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-shell/-/plugin-shell-2.2.0.tgz", diff --git a/package.json b/package.json index 65458c7..19e7f69 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@tauri-apps/plugin-clipboard-manager": "^2.2.0", "@tauri-apps/plugin-dialog": "^2.2.0", "@tauri-apps/plugin-log": "^2.2.0", + "@tauri-apps/plugin-os": "^2.2.0", "@tauri-apps/plugin-shell": "^2.2.0", "@tauri-apps/plugin-window-state": "^2.2.0" }, diff --git a/src/App.svelte b/src/App.svelte index ce8d1ca..6cb488e 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -1,7 +1,11 @@