Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down
13 changes: 11 additions & 2 deletions apps/desktop/src/main/permission-overlay/app-bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -43,11 +52,11 @@ export interface ResolveAppBundleDeps {
*/
export async function loadNativeBundleIcon<T>(
isPackaged: boolean,
load: () => Promise<T>,
load: (options: NativeBundleIconOptions) => Promise<T>,
): Promise<T | null> {
if (!isPackaged) return null;
try {
return await load();
return await load(NATIVE_BUNDLE_ICON_OPTIONS);
} catch {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ export function createPermissionOverlayMain(

async function resolveAppIconDataUrl(bundlePath: string | null): Promise<string | null> {
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
Expand Down Expand Up @@ -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.
Expand Down