fix: make windows register deeplinks

This commit is contained in:
Leafd 2025-10-09 10:18:55 -04:00
parent 1e9eeb3c2e
commit fa6acaf41f
No known key found for this signature in database
GPG key ID: D44AE7A3699406BE
5 changed files with 120 additions and 96 deletions

122
src-tauri/Cargo.lock generated
View file

@ -294,12 +294,6 @@ dependencies = [
"serde",
]
[[package]]
name = "block"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]]
name = "block-buffer"
version = "0.10.4"
@ -518,35 +512,6 @@ dependencies = [
"windows-link 0.2.0",
]
[[package]]
name = "cocoa"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad36507aeb7e16159dfe68db81ccc27571c3ccd4b76fb2fb72fc59e7a4b1b64c"
dependencies = [
"bitflags 2.9.4",
"block",
"cocoa-foundation",
"core-foundation 0.10.1",
"core-graphics",
"foreign-types 0.5.0",
"libc",
"objc",
]
[[package]]
name = "cocoa-foundation"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81411967c50ee9a1fc11365f8c585f863a22a9697c89239c452292c40ba79b0d"
dependencies = [
"bitflags 2.9.4",
"block",
"core-foundation 0.10.1",
"core-graphics-types",
"objc",
]
[[package]]
name = "combine"
version = "4.6.7"
@ -854,9 +819,10 @@ version = "0.1.0"
dependencies = [
"base64 0.21.7",
"chrono",
"cocoa",
"discord-rich-presence",
"objc",
"objc2 0.5.2",
"objc2-app-kit 0.2.2",
"objc2-foundation 0.2.2",
"once_cell",
"open",
"rand 0.9.2",
@ -2381,15 +2347,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
[[package]]
name = "malloc_buf"
version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
dependencies = [
"libc",
]
[[package]]
name = "markup5ever"
version = "0.14.1"
@ -2490,7 +2447,7 @@ dependencies = [
"gtk",
"keyboard-types",
"objc2 0.6.3",
"objc2-app-kit",
"objc2-app-kit 0.3.2",
"objc2-core-foundation",
"objc2-foundation 0.3.2",
"once_cell",
@ -2647,15 +2604,6 @@ dependencies = [
"syn 2.0.106",
]
[[package]]
name = "objc"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
dependencies = [
"malloc_buf",
]
[[package]]
name = "objc-sys"
version = "0.3.5"
@ -2682,6 +2630,22 @@ dependencies = [
"objc2-exception-helper",
]
[[package]]
name = "objc2-app-kit"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff"
dependencies = [
"bitflags 2.9.4",
"block2 0.5.1",
"libc",
"objc2 0.5.2",
"objc2-core-data 0.2.2",
"objc2-core-image 0.2.2",
"objc2-foundation 0.2.2",
"objc2-quartz-core 0.2.2",
]
[[package]]
name = "objc2-app-kit"
version = "0.3.2"
@ -2693,10 +2657,10 @@ dependencies = [
"libc",
"objc2 0.6.3",
"objc2-cloud-kit",
"objc2-core-data",
"objc2-core-data 0.3.2",
"objc2-core-foundation",
"objc2-core-graphics",
"objc2-core-image",
"objc2-core-image 0.3.2",
"objc2-core-text",
"objc2-core-video",
"objc2-foundation 0.3.2",
@ -2714,6 +2678,18 @@ dependencies = [
"objc2-foundation 0.3.2",
]
[[package]]
name = "objc2-core-data"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef"
dependencies = [
"bitflags 2.9.4",
"block2 0.5.1",
"objc2 0.5.2",
"objc2-foundation 0.2.2",
]
[[package]]
name = "objc2-core-data"
version = "0.3.2"
@ -2749,6 +2725,18 @@ dependencies = [
"objc2-io-surface",
]
[[package]]
name = "objc2-core-image"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80"
dependencies = [
"block2 0.5.1",
"objc2 0.5.2",
"objc2-foundation 0.2.2",
"objc2-metal",
]
[[package]]
name = "objc2-core-image"
version = "0.3.2"
@ -2865,7 +2853,7 @@ checksum = "f112d1746737b0da274ef79a23aac283376f335f4095a083a267a082f21db0c0"
dependencies = [
"bitflags 2.9.4",
"objc2 0.6.3",
"objc2-app-kit",
"objc2-app-kit 0.3.2",
"objc2-foundation 0.3.2",
]
@ -2925,7 +2913,7 @@ dependencies = [
"bitflags 2.9.4",
"block2 0.6.2",
"objc2 0.6.3",
"objc2-app-kit",
"objc2-app-kit 0.3.2",
"objc2-core-foundation",
"objc2-foundation 0.3.2",
"objc2-javascript-core",
@ -4699,7 +4687,7 @@ dependencies = [
"ndk-context",
"ndk-sys",
"objc2 0.6.3",
"objc2-app-kit",
"objc2-app-kit 0.3.2",
"objc2-foundation 0.3.2",
"once_cell",
"parking_lot",
@ -4765,7 +4753,7 @@ dependencies = [
"mime",
"muda",
"objc2 0.6.3",
"objc2-app-kit",
"objc2-app-kit 0.3.2",
"objc2-foundation 0.3.2",
"objc2-ui-kit",
"objc2-web-kit",
@ -4903,7 +4891,7 @@ checksum = "786156aa8e89e03d271fbd3fe642207da8e65f3c961baa9e2930f332bf80a1f5"
dependencies = [
"dunce",
"glob",
"objc2-app-kit",
"objc2-app-kit 0.3.2",
"objc2-foundation 0.3.2",
"open",
"schemars 0.8.22",
@ -4995,7 +4983,7 @@ dependencies = [
"jni",
"log",
"objc2 0.6.3",
"objc2-app-kit",
"objc2-app-kit 0.3.2",
"objc2-foundation 0.3.2",
"once_cell",
"percent-encoding",
@ -5447,7 +5435,7 @@ dependencies = [
"libappindicator",
"muda",
"objc2 0.6.3",
"objc2-app-kit",
"objc2-app-kit 0.3.2",
"objc2-core-foundation",
"objc2-core-graphics",
"objc2-foundation 0.3.2",
@ -5974,7 +5962,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c"
dependencies = [
"objc2 0.6.3",
"objc2-app-kit",
"objc2-app-kit 0.3.2",
"objc2-core-foundation",
"objc2-foundation 0.3.2",
"raw-window-handle",
@ -6510,7 +6498,7 @@ dependencies = [
"libc",
"ndk",
"objc2 0.6.3",
"objc2-app-kit",
"objc2-app-kit 0.3.2",
"objc2-core-foundation",
"objc2-foundation 0.3.2",
"objc2-ui-kit",

View file

@ -21,6 +21,7 @@ tauri-build = { version = "2", features = [] }
tauri = { version = "2", features = ["tray-icon"] }
tauri-plugin-opener = "2"
tauri-plugin-deep-link = "2"
tauri-plugin-single-instance = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
open = "5"
@ -41,6 +42,7 @@ once_cell = "1"
tauri-plugin-updater = "2"
[target."cfg(target_os = \"macos\")".dependencies]
cocoa = "0.26"
objc = "0.2"
objc2 = "0.5"
objc2-app-kit = { version = "0.2", features = ["NSColor"] }
objc2-foundation = "0.2"

View file

@ -61,6 +61,24 @@ fn get_recent_logs() -> Vec<LogEntry> {
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_single_instance::init(|app, args, cwd| {
push_log("info", "backend", format!("Single instance detected. Args: {:?}, CWD: {}", args, cwd));
// Show the existing window
if let Some(window) = app.get_webview_window("main") {
let _ = window.show();
let _ = window.set_focus();
push_log("info", "backend", "Brought existing window to front".to_string());
}
// Process any deep links from the new instance attempt
for arg in args {
if arg.starts_with("hackatime://") {
push_log("info", "backend", format!("Processing deep link from second instance: {}", arg));
handle_oauth_callback(&app, &arg);
}
}
}))
.plugin(tauri_plugin_process::init())
.plugin(tauri_plugin_updater::Builder::new().build())
.plugin(tauri_plugin_opener::init())
@ -149,33 +167,34 @@ pub fn run() {
#[cfg(target_os = "macos")]
{
window.set_title_bar_style(TitleBarStyle::Transparent).unwrap();
use objc2::runtime::AnyObject;
use objc2_app_kit::NSColor;
use objc2::msg_send;
if let Err(e) = window.set_title_bar_style(TitleBarStyle::Transparent) {
push_log("error", "backend", format!("Failed to set title bar style: {}", e));
}
#[allow(deprecated)]
#[allow(unexpected_cfgs)]
{
use cocoa::appkit::{NSColor, NSWindow};
use cocoa::base::{id, nil};
let ns_window = window.ns_window().unwrap() as id;
// Apply macOS-specific window styling with proper error handling
if let Ok(ns_win) = window.ns_window() {
let ns_window = ns_win as *mut AnyObject;
unsafe {
use objc::{msg_send, sel, sel_impl};
use objc::runtime::NO;
let clear_color = NSColor::clearColor();
let _: () = msg_send![ns_window, setBackgroundColor: &*clear_color];
let _: () = msg_send![ns_window, setOpaque: false];
let bg_color = NSColor::clearColor(nil);
ns_window.setBackgroundColor_(bg_color);
ns_window.setOpaque_(NO);
let content_view: id = msg_send![ns_window, contentView];
let content_view: *mut AnyObject = msg_send![ns_window, contentView];
let _: () = msg_send![content_view, setWantsLayer: true];
let layer: id = msg_send![content_view, layer];
let layer: *mut AnyObject = msg_send![content_view, layer];
let _: () = msg_send![layer, setCornerRadius: 12.0f64];
let _: () = msg_send![layer, setMasksToBounds: true];
let _: () = msg_send![layer, setNeedsDisplayOnBoundsChange: true];
}
push_log("info", "backend", "✅ macOS window styling applied".to_string());
} else {
push_log("error", "backend", "Failed to get NSWindow".to_string());
}
}
}
@ -224,7 +243,7 @@ pub fn run() {
});
#[cfg(any(target_os = "linux", all(debug_assertions, target_os = "windows")))]
#[cfg(any(target_os = "linux", target_os = "windows"))]
{
app.deep_link().register_all().unwrap_or_else(|e| {
push_log("error", "backend", format!("Failed to register deep links: {}", e));
@ -236,14 +255,29 @@ pub fn run() {
if let Some(window) = app.get_webview_window("main") {
let window_handle = window.clone();
let _ = window.on_window_event(move |event| {
let app_handle = app.handle().clone();
window.on_window_event(move |event| {
match event {
WindowEvent::CloseRequested { api, .. } => {
push_log("info", "backend", "🪟 Window close requested - hiding to tray".to_string());
api.prevent_close();
let _ = window_handle.hide();
push_log("info", "backend", "✅ Window hidden to tray".to_string());
// Use the app handle to get the window and hide it asynchronously
// This prevents potential re-entrancy issues
let app_clone = app_handle.clone();
std::thread::spawn(move || {
if let Some(win) = app_clone.get_webview_window("main") {
let _ = win.hide();
push_log("info", "backend", "✅ Window hidden to tray".to_string());
}
});
}
WindowEvent::Resized(_) => {
// Handle resize events gracefully - no action needed
// This prevents potential crashes on macOS with transparent windows
}
WindowEvent::Moved(_) => {
// Handle move events gracefully
}
_ => {}
}

View file

@ -4,13 +4,13 @@ use crate::push_log;
pub fn setup_app_menu(app: &AppHandle) -> Result<(), Box<dyn std::error::Error>> {
let about_quit = MenuItem::with_id(app, "quit", "Quit Hackatime", true, None::<&str>)?;
let quit_item = PredefinedMenuItem::quit(app, Some("Quit Hackatime"))?;
let about_menu = Submenu::with_items(
app,
"About",
"Hackatime",
true,
&[
&about_quit,
&quit_item,
],
)?;
@ -62,7 +62,6 @@ pub fn setup_app_menu(app: &AppHandle) -> Result<(), Box<dyn std::error::Error>>
app.on_menu_event(|app, event| {
match event.id.as_ref() {
"quit" => app.exit(0),
"show" => {
if let Some(window) = app.get_webview_window("main") {
let _ = window.show();

View file

@ -61,6 +61,7 @@
]
}
},
"single-instance": {},
"updater": {
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDdERjg4QTFCNTJFMDk0MUQKUldRZGxPQlNHNHI0ZlRkMDN0MGI1MnllY1dUVStZalV3dVdhcTFuREx5SGtBc0txQ2xnTWs3WU4K",
"endpoints": [