feat(desktop): Windows taskbar overlay icon for unread badge

Red-dot overlay drawn as raw RGBA (no extra resource bundled).
Shown on Windows when unread count > 0, cleared when 0.
macOS keeps numeric Dock badge; Linux has no cross-DE badge API.

Bumps version 0.10.0 -> 0.10.1.
This commit is contained in:
2026-04-21 17:31:06 +02:00
parent eb452bf57e
commit 713bca5177
5 changed files with 45 additions and 8 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@chat-app/desktop", "name": "@chat-app/desktop",
"version": "0.10.0", "version": "0.10.1",
"private": true, "private": true,
"description": "Tauri v2 desktop client (Windows / macOS / Linux)", "description": "Tauri v2 desktop client (Windows / macOS / Linux)",
"type": "module", "type": "module",
+1 -1
View File
@@ -726,7 +726,7 @@ dependencies = [
[[package]] [[package]]
name = "chat-app-desktop" name = "chat-app-desktop"
version = "0.10.0" version = "0.10.1"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"dryoc", "dryoc",
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "chat-app-desktop" name = "chat-app-desktop"
version = "0.10.0" version = "0.10.1"
description = "ChatApp desktop client" description = "ChatApp desktop client"
authors = ["Dennis"] authors = ["Dennis"]
edition = "2021" edition = "2021"
+41 -4
View File
@@ -22,6 +22,33 @@ struct TrayUnreadPayload {
count: u32, count: u32,
} }
// Red-dot overlay icon for the Windows taskbar. Drawn as raw RGBA instead of
// shipping a PNG so we don't add another resource to the bundle. Kept small
// (32x32) since Windows scales the overlay down anyway.
#[cfg(target_os = "windows")]
fn unread_overlay_rgba() -> Vec<u8> {
const SIZE: u32 = 32;
let r = SIZE as f32 / 2.0;
let mut buf = Vec::with_capacity((SIZE * SIZE * 4) as usize);
for y in 0..SIZE {
for x in 0..SIZE {
let dx = x as f32 - r + 0.5;
let dy = y as f32 - r + 0.5;
let d = (dx * dx + dy * dy).sqrt();
let edge = r - 1.0;
if d <= edge {
buf.extend_from_slice(&[0xDC, 0x26, 0x26, 0xFF]);
} else if d <= r {
let alpha = (255.0 * (r - d)).clamp(0.0, 255.0) as u8;
buf.extend_from_slice(&[0xDC, 0x26, 0x26, alpha]);
} else {
buf.extend_from_slice(&[0, 0, 0, 0]);
}
}
}
buf
}
#[cfg(not(any(target_os = "android", target_os = "ios")))] #[cfg(not(any(target_os = "android", target_os = "ios")))]
fn show_main_window(app: &AppHandle) { fn show_main_window(app: &AppHandle) {
if let Some(win) = app.get_webview_window("main") { if let Some(win) = app.get_webview_window("main") {
@@ -171,9 +198,9 @@ pub fn run() {
format!("ChatApp · {} neu", payload.count) format!("ChatApp · {} neu", payload.count)
}; };
let _ = tray_handle.set_tooltip(Some(tooltip)); let _ = tray_handle.set_tooltip(Some(tooltip));
// Dock/taskbar badge. `set_badge_label` is macOS-only. // Dock/taskbar badge. macOS uses a numeric label; Windows uses
// Windows taskbar uses an overlay icon (different API). // an overlay icon (red dot = unread). Linux has no cross-DE
// Linux has no cross-DE badge API — skip. // badge API — skip.
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
if let Some(win) = badge_window.as_ref() { if let Some(win) = badge_window.as_ref() {
let badge = if payload.count == 0 { let badge = if payload.count == 0 {
@@ -183,7 +210,17 @@ pub fn run() {
}; };
let _ = win.set_badge_label(badge); let _ = win.set_badge_label(badge);
} }
#[cfg(not(target_os = "macos"))] #[cfg(target_os = "windows")]
if let Some(win) = badge_window.as_ref() {
if payload.count == 0 {
let _ = win.set_overlay_icon(None);
} else {
let rgba = unread_overlay_rgba();
let img = tauri::image::Image::new_owned(rgba, 32, 32);
let _ = win.set_overlay_icon(Some(img));
}
}
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
let _ = &badge_window; let _ = &badge_window;
}); });
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"$schema": "https://schema.tauri.app/config/2", "$schema": "https://schema.tauri.app/config/2",
"productName": "ChatApp", "productName": "ChatApp",
"version": "0.10.0", "version": "0.10.1",
"identifier": "com.meinname.chatapp", "identifier": "com.meinname.chatapp",
"build": { "build": {
"beforeDevCommand": "pnpm vite:dev", "beforeDevCommand": "pnpm vite:dev",