From 654ef0c506b521c5641656bd2e4145e2b1585dce Mon Sep 17 00:00:00 2001 From: sunrioa <178722768+sunrioa@users.noreply.github.com> Date: Fri, 21 Aug 2026 01:39:51 +0800 Subject: [PATCH] fix(desktop): use a supported macOS bundle icon size Refs #3352 Refs #1919 Refs #1920 Generated-by: OpenAI Codex --- .../__tests__/permission-overlay-controller.test.ts | 11 ++++++++++- .../src/main/permission-overlay/app-bundle.ts | 13 +++++++++++-- .../permission-overlay/permission-overlay-main.ts | 8 ++++---- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/apps/desktop/src/main/__tests__/permission-overlay-controller.test.ts b/apps/desktop/src/main/__tests__/permission-overlay-controller.test.ts index 5450db4f3a..dffac89eb1 100644 --- a/apps/desktop/src/main/__tests__/permission-overlay-controller.test.ts +++ b/apps/desktop/src/main/__tests__/permission-overlay-controller.test.ts @@ -314,7 +314,16 @@ describe('app bundle resolution for the drag', () => { }); assert.equal(icon, null); assert.equal(calls, 0, 'unpackaged development must not call app.getFileIcon()'); - assert.equal(await loadNativeBundleIcon(true, async () => 'icon'), 'icon'); + }); + + it('uses the macOS-supported normal icon size for a packaged app', async () => { + let received: unknown; + const icon = await loadNativeBundleIcon(true, async (options) => { + received = options; + return 'icon'; + }); + assert.equal(icon, 'icon'); + assert.deepEqual(received, { size: 'normal' }); }); it('walks three levels up from the executable to the .app', () => { diff --git a/apps/desktop/src/main/permission-overlay/app-bundle.ts b/apps/desktop/src/main/permission-overlay/app-bundle.ts index 4c148f1511..67f2f86bba 100644 --- a/apps/desktop/src/main/permission-overlay/app-bundle.ts +++ b/apps/desktop/src/main/permission-overlay/app-bundle.ts @@ -34,6 +34,15 @@ export interface ResolveAppBundleDeps { exists(path: string): boolean; } +interface NativeBundleIconOptions { + size: 'normal'; +} + +// Electron's `large` file-icon size is unsupported on macOS and can terminate +// the packaged process before the promise settles. Both packaged icon reads +// must use `normal`; callers may resize the decorative image afterwards. +const NATIVE_BUNDLE_ICON_OPTIONS: NativeBundleIconOptions = { size: 'normal' }; + /** * Reading a bundle icon is presentation-only. The original unpackaged npm * Electron runtime could terminate natively while macOS resolved its bundle @@ -43,11 +52,11 @@ export interface ResolveAppBundleDeps { */ export async function loadNativeBundleIcon( isPackaged: boolean, - load: () => Promise, + load: (options: NativeBundleIconOptions) => Promise, ): Promise { if (!isPackaged) return null; try { - return await load(); + return await load(NATIVE_BUNDLE_ICON_OPTIONS); } catch { return null; } diff --git a/apps/desktop/src/main/permission-overlay/permission-overlay-main.ts b/apps/desktop/src/main/permission-overlay/permission-overlay-main.ts index c9a992d776..68f3d0cfdf 100644 --- a/apps/desktop/src/main/permission-overlay/permission-overlay-main.ts +++ b/apps/desktop/src/main/permission-overlay/permission-overlay-main.ts @@ -76,8 +76,8 @@ export function createPermissionOverlayMain( async function resolveAppIconDataUrl(bundlePath: string | null): Promise { if (!bundlePath) return null; - const icon = await loadNativeBundleIcon(app.isPackaged, () => - app.getFileIcon(bundlePath, { size: 'large' }), + const icon = await loadNativeBundleIcon(app.isPackaged, (options) => + app.getFileIcon(bundlePath, options), ); if (!icon || icon.isEmpty()) return null; // nativeImage.createFromPath does not decode .icns reliably. Asking @@ -273,8 +273,8 @@ function attachCardGestures(win: import('electron').BrowserWindow): void { if (!fromRenderer.isEmpty()) icon = fromRenderer; } if (icon.isEmpty()) { - const fallback = await loadNativeBundleIcon(app.isPackaged, () => - app.getFileIcon(resolved.bundlePath, { size: 'large' }), + const fallback = await loadNativeBundleIcon(app.isPackaged, (options) => + app.getFileIcon(resolved.bundlePath, options), ); if (fallback && !fallback.isEmpty()) icon = fallback.resize({ width: 64, height: 64 }); // The file drag still works without a decorative drag image.