From fa22a17d7db7f80c63c6bad8f90a929367ce4bf5 Mon Sep 17 00:00:00 2001 From: DigHuang <114602213+DigHuang@users.noreply.github.com> Date: Mon, 9 Feb 2026 05:22:38 -0800 Subject: [PATCH] chore(icon): use platform-specific tray icons (#21) --- electron-builder.yml | 2 +- electron/main/index.ts | 30 ++++++++++++++++++++++++++++- electron/main/tray.ts | 42 ++++++++++++++++++++++++++++++----------- public/icons/icon.png | Bin 1344 -> 0 bytes 4 files changed, 61 insertions(+), 13 deletions(-) delete mode 100644 public/icons/icon.png diff --git a/electron-builder.yml b/electron-builder.yml index 3cc90d26d..1a6ea96f4 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -78,7 +78,7 @@ dmg: win: forceCodeSigning: false verifyUpdateCodeSignature: false - signAndEditExecutable: false + signAndEditExecutable: true extraResources: - from: resources/bin/win32-${arch} to: bin diff --git a/electron/main/index.ts b/electron/main/index.ts index 42f4aed88..206467ba9 100644 --- a/electron/main/index.ts +++ b/electron/main/index.ts @@ -2,7 +2,7 @@ * Electron Main Process Entry * Manages window creation, system tray, and IPC handlers */ -import { app, BrowserWindow, session, shell } from 'electron'; +import { app, BrowserWindow, nativeImage, session, shell } from 'electron'; import { join } from 'path'; import { GatewayManager } from '../gateway/manager'; import { registerIpcHandlers } from './ipc-handlers'; @@ -22,6 +22,33 @@ let mainWindow: BrowserWindow | null = null; const gatewayManager = new GatewayManager(); const clawHubService = new ClawHubService(); +/** + * Resolve the icons directory path (works in both dev and packaged mode) + */ +function getIconsDir(): string { + if (app.isPackaged) { + // Packaged: icons are in extraResources → process.resourcesPath/resources/icons + return join(process.resourcesPath, 'resources', 'icons'); + } + // Development: relative to dist-electron/main/ + return join(__dirname, '../../resources/icons'); +} + +/** + * Get the app icon for the current platform + */ +function getAppIcon(): Electron.NativeImage | undefined { + if (process.platform === 'darwin') return undefined; // macOS uses the app bundle icon + + const iconsDir = getIconsDir(); + const iconPath = + process.platform === 'win32' + ? join(iconsDir, 'icon.ico') + : join(iconsDir, 'icon.png'); + const icon = nativeImage.createFromPath(iconPath); + return icon.isEmpty() ? undefined : icon; +} + /** * Create the main application window */ @@ -33,6 +60,7 @@ function createWindow(): BrowserWindow { height: 800, minWidth: 960, minHeight: 600, + icon: getAppIcon(), webPreferences: { preload: join(__dirname, '../preload/index.js'), nodeIntegration: false, diff --git a/electron/main/tray.ts b/electron/main/tray.ts index c3c8a6117..a1b975780 100644 --- a/electron/main/tray.ts +++ b/electron/main/tray.ts @@ -7,22 +7,42 @@ import { join } from 'path'; let tray: Tray | null = null; +/** + * Resolve the icons directory path (works in both dev and packaged mode) + */ +function getIconsDir(): string { + if (app.isPackaged) { + return join(process.resourcesPath, 'resources', 'icons'); + } + return join(__dirname, '../../resources/icons'); +} + /** * Create system tray icon and menu */ export function createTray(mainWindow: BrowserWindow): Tray { - // Create tray icon - const iconPath = join(__dirname, '../../resources/icons/tray-icon.png'); - - // Create a template image for macOS (adds @2x support automatically) - let icon = nativeImage.createFromPath(iconPath); - - // If icon doesn't exist, create a simple placeholder - if (icon.isEmpty()) { - // Create a simple 16x16 icon as placeholder - icon = nativeImage.createEmpty(); + // Use platform-appropriate icon for system tray + const iconsDir = getIconsDir(); + let iconPath: string; + + if (process.platform === 'win32') { + // Windows: use .ico for best quality in system tray + iconPath = join(iconsDir, 'icon.ico'); + } else if (process.platform === 'darwin') { + // macOS: use 16x16 PNG as template image + iconPath = join(iconsDir, '16x16.png'); + } else { + // Linux: use 32x32 PNG + iconPath = join(iconsDir, '32x32.png'); } - + + let icon = nativeImage.createFromPath(iconPath); + + // Fallback to icon.png if platform-specific icon not found + if (icon.isEmpty()) { + icon = nativeImage.createFromPath(join(iconsDir, 'icon.png')); + } + // On macOS, set as template image for proper dark/light mode support if (process.platform === 'darwin') { icon.setTemplateImage(true); diff --git a/public/icons/icon.png b/public/icons/icon.png deleted file mode 100644 index 8c171292352ef9d9f67ae1c1faa6d1638a3f8700..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1344 zcmV-G1;6@=YJGESwbg3+t<_eW%dNKD{1D|I7$G9mjZ|g`n2Nx?_uO;OJ^%0d^R)Lp z_Z$y+`4f}+;%~b<@4fe&_j!KL@ArG&!{C1o<~9M`qObs%7GPNR)%veWV3;=GO@s&u zYXJ$E9sqJ3jhkp1)KJr}qJ1O*S-1bRZZ!cjDKQCE2kI|q*w85AeV>R;0TDY+$;fY! zu=-dW!7<%^X5Iv>Bp{L6$(Vd^8A>3h$p$TKmr&3uA-hqepR!&Bje{E2)Cn_!&xwFH z5(aSQs*atVGPbu%*xDvxU4wwUfP`cHD!Q)dID1XUCqGNr*eoI2FXA$1+KK#I0#@=G zmMpXnCs21@L)GspO3y0Tb6P=$UqErEjNzya-~^ch5?MYG=@mRg&6*#9glAvP00vnT z^#dBVwM*Fbi;R&enGtn#6E5JY${5zx#ofK8D+MzhfvdcU*5L$|_Ha$@+WjtBJ(WWI)6;#2zJB}qq5nN>L`(Hg4$CiLd*Nz&@Oa#d6 zllbCS89B`&R+MsdA{-Oa>}1e9n84zltUGdFcFEY#B;rHAh_>Mb{*IYQKNJI#Ie{Ms zv`Kq2Q#1B`-)hk<%aM;QG{lG zM{WpA_5|+yIE3{+@y6+x$_I=jf!gyLJhyg;_Iuv1Lo70(}v(GGw7a~6F`Ad(DG)SKx{Z%B-{1lwCB`XRBRj6;4vq5#e&j!LBV z9U}>>I?Pj>exaJDdrSqQZ%ZlMZ(0HedlfwLRTRtib9lQ!z%|}P$!W#C`MYx}UayT~ zIBHP;KiaS2l_R|C4Gzhnkki`BTg-*i|Yv8jE zd$eYI@t6|N%JW1{MZofuqif-chgh=E}?}YFxBrDGMI&jxJ>{vmf#;OH57ar!TLl10000