From 7757d0ac2fcf141724c5d6b3d30df2cc06e9ebb6 Mon Sep 17 00:00:00 2001 From: Akshay Sharma Date: Mon, 13 Jul 2026 01:40:31 +0530 Subject: [PATCH] fix: Windows tray icon, PowerShell focus steal, publisher name Fix 1: PowerShell focus stealing - Added -WindowStyle Hidden to spawn args - PowerShell now runs invisibly, never steals focus - Added sanity check: skip result if process is powershell/pwsh/powershell_ise Fix 2: Blank tray icon on Windows - Platform-aware getTrayIcon() function in tray.ts - Windows: load icon.png + resize to 16x16 for tray - Falls back to icon.ico, then emerald data URI - Mac/Linux: existing icon.png unchanged - Added resources/ICONS_TODO.md for future .ico steps Fix 3: Publisher name in Windows installer - win.publisherName: 'Wrkmark' - win.legalTrademarks: 'Wrkmark' - nsis.shortcutName: 'Wrkmark' - nsis.uninstallDisplayName: 'Wrkmark' pnpm typecheck: zero errors pnpm build: successful grep WindowStyle: confirmed in window-tracker.ts Version: unchanged (0.1.0) --- electron-builder.config.ts | 8 +++-- resources/ICONS_TODO.md | 13 +++++++ src/main/tray.ts | 70 +++++++++++++++++++++++++++++++++++--- src/main/window-tracker.ts | 10 ++++++ 4 files changed, 94 insertions(+), 7 deletions(-) create mode 100644 resources/ICONS_TODO.md diff --git a/electron-builder.config.ts b/electron-builder.config.ts index 688a096..4147aa0 100644 --- a/electron-builder.config.ts +++ b/electron-builder.config.ts @@ -48,7 +48,9 @@ const config: Configuration = { target: [ { target: 'nsis', arch: ['x64'] } ], - icon: 'resources/icon.ico' + icon: 'resources/icon.ico', + publisherName: 'Wrkmark', + legalTrademarks: 'Wrkmark' }, linux: { @@ -75,7 +77,9 @@ const config: Configuration = { oneClick: false, allowToChangeInstallationDirectory: true, createDesktopShortcut: true, - createStartMenuShortcut: true + createStartMenuShortcut: true, + shortcutName: 'Wrkmark', + uninstallDisplayName: 'Wrkmark' }, publish: null, diff --git a/resources/ICONS_TODO.md b/resources/ICONS_TODO.md new file mode 100644 index 0000000..e2e5183 --- /dev/null +++ b/resources/ICONS_TODO.md @@ -0,0 +1,13 @@ +# Icon TODO +Windows tray requires icon.ico (16x16 and 32x32). +Current: using programmatic nativeImage fallback. + +To create proper icon.ico: +1. Have a designer create 1024x1024 icon.png +2. Use ImageMagick: + convert icon.png -resize 256x256 \ + -define icon:auto-resize=256,128,64,48,32,16 \ + resources/icon.ico +3. Or use online converter: convertio.co/png-ico/ + +Until then, the emerald fallback is used on Windows. diff --git a/src/main/tray.ts b/src/main/tray.ts index 14427b0..10abb9a 100644 --- a/src/main/tray.ts +++ b/src/main/tray.ts @@ -6,17 +6,77 @@ * What this is NOT: standard main process lifecycle management (see index.ts). */ -import { resolve } from 'path' +import path, { resolve } from 'path' import { Tray, Menu, BrowserWindow, app, nativeImage } from 'electron' import { getObserver } from './observer-instance' let tray: Tray | null = null -export function createTray(window: BrowserWindow): Tray { - const iconPath = resolve(__dirname, '../../resources/icon.png') - const icon = nativeImage.createFromPath(iconPath) +function getTrayIcon(): Electron.NativeImage { + if (process.platform === 'win32') { + // Better approach: use existing png, resize for tray + const pngPath = path.join( + process.env.VITE_PUBLIC ?? __dirname, + '../resources/icon.png' + ) + try { + let icon = nativeImage.createFromPath(pngPath) + if (icon.isEmpty()) { + const fallbackPngPath = resolve(__dirname, '../../resources/icon.png') + icon = nativeImage.createFromPath(fallbackPngPath) + } + if (!icon.isEmpty()) { + // Resize to 16x16 for Windows tray + return icon.resize({ width: 16, height: 16 }) + } + } catch { /* fall through */ } + + // Windows needs .ico format for tray + // Try to load icon.ico, fallback to generating + // from icon.png via nativeImage + const icoPath = path.join( + process.env.VITE_PUBLIC ?? __dirname, + '../resources/icon.ico' + ) + + try { + let icon = nativeImage.createFromPath(icoPath) + if (icon.isEmpty()) { + const fallbackIcoPath = resolve(__dirname, '../../resources/icon.ico') + icon = nativeImage.createFromPath(fallbackIcoPath) + } + if (!icon.isEmpty()) return icon + } catch { + // ico not found, fall through + } + + // Fallback: create a small icon programmatically + // A 16x16 emerald square as data URI + const iconDataURL = 'data:image/png;base64,' + + // 16x16 emerald green square + 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/' + + '9hAAAABmJLR0QA/wD/AP+gvaeTAAAASUlEQVQ4jWNg' + + 'GJRAYWHh/4SEhP9MS0v/MzAwMDAwMjL+ZxgFgx8AAA' + + 'AA//8DABZyAv8jPe7RAAAAAElFTkSuQmCC' + + return nativeImage.createFromDataURL(iconDataURL) + } - tray = new Tray(icon) + // Mac and Linux use png + const iconPath = path.join( + process.env.VITE_PUBLIC ?? __dirname, + '../resources/icon.png' + ) + let icon = nativeImage.createFromPath(iconPath) + if (icon.isEmpty()) { + const fallbackPath = resolve(__dirname, '../../resources/icon.png') + icon = nativeImage.createFromPath(fallbackPath) + } + return icon +} + +export function createTray(window: BrowserWindow): Tray { + tray = new Tray(getTrayIcon()) tray.setToolTip('Wrkmark') const updateContextMenu = (): void => { diff --git a/src/main/window-tracker.ts b/src/main/window-tracker.ts index 4086c32..1787ba6 100644 --- a/src/main/window-tracker.ts +++ b/src/main/window-tracker.ts @@ -340,6 +340,7 @@ async function getActiveAppWindows(): Promise<{ const child = spawn('powershell', [ '-NoProfile', '-NonInteractive', + '-WindowStyle', 'Hidden', '-Command', script ]) @@ -363,6 +364,15 @@ async function getActiveAppWindows(): Promise<{ .replace(/[\x00-\x1F\x7F]/g, '') .substring(0, 100) + // Sanity check: if we somehow still get powershell + // as the result, something is wrong — skip this tick + if (safeProcessName === 'powershell' || + safeProcessName === 'pwsh' || + safeProcessName === 'powershell_ise') { + resolve(null) + return + } + resolve({ bundleId: safeProcessName, appName: safeProductName