fix(desktop): tray badge — IPC handler died when icon.ico missing from packaged resources

This commit is contained in:
byGalax
2026-05-16 16:47:19 +02:00
parent c52c65faa9
commit 472665b980
+27 -2
View File
@@ -30,6 +30,17 @@ function resolveIconPathDev(): string {
return path.join(app.getAppPath(), 'resources', 'icon.ico'); return path.join(app.getAppPath(), 'resources', 'icon.ico');
} }
// 16×16 grey square — last-ditch fallback when neither the packaged
// nor dev icon file resolves. Tray constructor on Windows throws when
// handed an empty NativeImage, which would tear down the whole
// registrar before `ipcMain.handle(TRAY_UNREAD)` runs — leaving the
// taskbar overlay badge wired but the renderer's invoke rejecting
// with "No handler registered". A non-empty placeholder keeps the
// constructor happy so the IPC handler always gets registered.
const FALLBACK_TRAY_PNG_BASE64 =
'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAH0lEQVR42mNk' +
'YGD4z0ABYBxVOKpwVOGowlGFwwoBAEnYAR9XlIldAAAAAElFTkSuQmCC';
function loadTrayIcon(): NativeImage { function loadTrayIcon(): NativeImage {
for (const p of [resolveIconPath(), resolveIconPathDev()]) { for (const p of [resolveIconPath(), resolveIconPathDev()]) {
try { try {
@@ -39,7 +50,7 @@ function loadTrayIcon(): NativeImage {
/* try next */ /* try next */
} }
} }
return nativeImage.createEmpty(); return nativeImage.createFromBuffer(Buffer.from(FALLBACK_TRAY_PNG_BASE64, 'base64'));
} }
function buildOverlay(): NativeImage { function buildOverlay(): NativeImage {
@@ -53,8 +64,16 @@ function buildOverlay(): NativeImage {
} }
export function register(mainWindow: BrowserWindow): void { export function register(mainWindow: BrowserWindow): void {
// Tray icon is best-effort: if neither the packaged resource nor the
// dev path resolves (e.g. icon.ico isn't shipped under resourcesPath
// in packaged builds — only the app icon goes into the .exe metadata),
// we still want the taskbar overlay badge to work. setOverlayIcon is
// a BrowserWindow method, so it functions even when the systray icon
// creation fails.
try {
const icon = loadTrayIcon(); const icon = loadTrayIcon();
trayRef = new Tray(icon.isEmpty() ? nativeImage.createEmpty() : icon); if (!icon.isEmpty()) {
trayRef = new Tray(icon);
trayRef.setToolTip('Netralax'); trayRef.setToolTip('Netralax');
const menu = Menu.buildFromTemplate([ const menu = Menu.buildFromTemplate([
@@ -82,6 +101,12 @@ export function register(mainWindow: BrowserWindow): void {
if (mainWindow.isVisible()) mainWindow.focus(); if (mainWindow.isVisible()) mainWindow.focus();
else mainWindow.show(); else mainWindow.show();
}); });
}
} catch (err: unknown) {
// Swallow: the overlay badge below is the user-visible bit. A missing
// systray icon is cosmetic and shouldn't take the unread handler with it.
console.warn('[tray] systray init failed, overlay badge still active', err);
}
ipcMain.handle( ipcMain.handle(
CHANNELS.TRAY_UNREAD, CHANNELS.TRAY_UNREAD,