diff --git a/.claude/settings.local.json b/.claude/settings.local.json index db797c0..8d8c128 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -100,7 +100,11 @@ "Bash(pkg-config --exists openssl)", "Bash(sudo apt-get install -y libssl-dev pkg-config)", "Bash(dpkg -L libssl3t64)", - "Bash(grep *)" + "Bash(grep *)", + "Read(//home/christoph/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tauri-2.11.5/src/**)", + "Read(//home/christoph/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tauri-runtime-wry-2.11.4/src/**)", + "Read(//home/christoph/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tauri-2.11.5/src/window/**)", + "WebSearch" ] } } diff --git a/src-tauri/src/badge.rs b/src-tauri/src/badge.rs index 6775718..8e34f5a 100644 --- a/src-tauri/src/badge.rs +++ b/src-tauri/src/badge.rs @@ -177,7 +177,21 @@ pub fn set_sync_badge( .map_err(|err| format!("Could not set taskbar badge: {err}"))?; } - #[cfg(not(target_os = "windows"))] + // Native badge count: works on Linux (via the desktop's launcher API, e.g. Unity's + // LauncherEntry, also honored by GNOME/KDE) and macOS (dock badge). Matched against the + // app's `.desktop` file on Linux, so it's a silent no-op on launchers that + // don't implement the protocol. + #[cfg(any(target_os = "linux", target_os = "macos"))] + { + let Some(window) = app.get_webview_window("main") else { + return Ok(()); + }; + window + .set_badge_count(if count > 0 { Some(count as i64) } else { None }) + .map_err(|err| format!("Could not set taskbar badge: {err}"))?; + } + + #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))] { let _ = (app, count); } diff --git a/src/App.svelte b/src/App.svelte index e871891..3b54e63 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -421,7 +421,21 @@ } function waitForAnimationFrame(): Promise { - return new Promise((resolve) => requestAnimationFrame(() => resolve())); + // requestAnimationFrame never fires for an unmapped/invisible window on + // WebKitGTK (Linux) — the compositor frame clock only runs for realized + // windows. The main window starts hidden until the splashscreen closes, + // so without a timeout fallback this promise (and the whole startup + // sequence, including closing the splashscreen) would hang forever on Linux. + return new Promise((resolve) => { + let settled = false; + const settle = () => { + if (settled) return; + settled = true; + resolve(); + }; + requestAnimationFrame(() => settle()); + window.setTimeout(settle, 50); + }); } async function waitForStartupPaint() {