From 6a18d8376065f7c7989512e1c1efb36607bcb897 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=86=E9=80=8A?= <72533078+UncertaintyDeterminesYou4ndMe@users.noreply.github.com> Date: Sat, 22 Aug 2026 09:33:35 +0800 Subject: [PATCH] fix(desktop): never request the 'large' file icon that kills packaged macOS builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit app.getFileIcon(path, { size: 'large' }) is unsupported on macOS and hits a fatal NOTREACHED inside Chromium's IconLoader — the process dies with SIGTRAP before the promise settles, so loadNativeBundleIcon's try/catch never runs. Packaged builds crashed the moment the drag-to-grant permission guide loaded the app icon; dev builds were spared only by the isPackaged gate from #1920. Hoist the size choice into one shared BUNDLE_ICON_OPTIONS constant requesting 'normal' (32x32, supported everywhere), which the existing resize step upscales to the same 64x64 the code already produced. Fixes #3352 Generated-by: Claude Code --- .../main/__tests__/permission-overlay-controller.test.ts | 7 +++++++ apps/desktop/src/main/permission-overlay/app-bundle.ts | 9 +++++++++ .../main/permission-overlay/permission-overlay-main.ts | 6 +++--- 3 files changed, 19 insertions(+), 3 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..aa43c203d1 100644 --- a/apps/desktop/src/main/__tests__/permission-overlay-controller.test.ts +++ b/apps/desktop/src/main/__tests__/permission-overlay-controller.test.ts @@ -21,6 +21,7 @@ import { type PermissionOverlayWindowLike, } from '../permission-overlay/permission-overlay-controller.js'; import { + BUNDLE_ICON_OPTIONS, loadNativeBundleIcon, resolveAppBundle, } from '../permission-overlay/app-bundle.js'; @@ -317,6 +318,12 @@ describe('app bundle resolution for the drag', () => { assert.equal(await loadNativeBundleIcon(true, async () => 'icon'), 'icon'); }); + it('never requests the large icon size that kills packaged macOS builds', () => { + // 'large' hits a fatal NOTREACHED inside Chromium's IconLoader on + // macOS (SIGTRAP, not a catchable error) — see issue #3352. + assert.notEqual(BUNDLE_ICON_OPTIONS.size as string, 'large'); + }); + it('walks three levels up from the executable to the .app', () => { assert.deepEqual( resolveAppBundle({ diff --git a/apps/desktop/src/main/permission-overlay/app-bundle.ts b/apps/desktop/src/main/permission-overlay/app-bundle.ts index 4c148f1511..01767a101d 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; } +/** + * The only size every platform can actually deliver. `'large'` is + * unsupported on macOS: Chromium's IconLoader hits a fatal NOTREACHED and + * the process dies with SIGTRAP before the promise settles — no JavaScript + * error is ever thrown, so the try/catch in `loadNativeBundleIcon` cannot + * save the app. Callers upscale the 32x32 result as needed. + */ +export const BUNDLE_ICON_OPTIONS = { size: 'normal' } as const; + /** * Reading a bundle icon is presentation-only. The original unpackaged npm * Electron runtime could terminate natively while macOS resolved its bundle 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..9aa7701974 100644 --- a/apps/desktop/src/main/permission-overlay/permission-overlay-main.ts +++ b/apps/desktop/src/main/permission-overlay/permission-overlay-main.ts @@ -24,7 +24,7 @@ import { join } from 'node:path'; import type { UiLocale } from '@maka/core/ui-locale'; import { resolveOverlayAssetDir } from '../overlay-assets.js'; import { openSystemPermissionPane, requestPermissionAccess } from '../permissions-actions.js'; -import { loadNativeBundleIcon, resolveAppBundle } from './app-bundle.js'; +import { BUNDLE_ICON_OPTIONS, loadNativeBundleIcon, resolveAppBundle } from './app-bundle.js'; import { getPermissionOverlayCopy } from './permission-overlay-copy.js'; import { createPermissionOverlayController, @@ -77,7 +77,7 @@ 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' }), + app.getFileIcon(bundlePath, BUNDLE_ICON_OPTIONS), ); if (!icon || icon.isEmpty()) return null; // nativeImage.createFromPath does not decode .icns reliably. Asking @@ -274,7 +274,7 @@ function attachCardGestures(win: import('electron').BrowserWindow): void { } if (icon.isEmpty()) { const fallback = await loadNativeBundleIcon(app.isPackaged, () => - app.getFileIcon(resolved.bundlePath, { size: 'large' }), + app.getFileIcon(resolved.bundlePath, BUNDLE_ICON_OPTIONS), ); if (fallback && !fallback.isEmpty()) icon = fallback.resize({ width: 64, height: 64 }); // The file drag still works without a decorative drag image.