fix(desktop): app icon + harden auto-updater loading

Generate a 1024x1024 macOS app icon from the dashboard logo so packaged
builds carry the Fusion brand instead of the default Electron icon, and
load electron-updater dynamically inside a try/catch so packaged builds
tolerate CJS/ESM interop quirks and missing transitive deps. Widen the
electron-builder files whitelist to include electron-updater's
transitive dep tree so they are actually bundled into the asar.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-05-16 12:51:12 -07:00
parent c6e978286a
commit 6dde53ff0d
9 changed files with 89 additions and 27 deletions

View File

@@ -96,6 +96,7 @@ vi.mock("electron", () => ({
}));
vi.mock("electron-updater", () => ({
default: { autoUpdater: mocks.autoUpdater },
autoUpdater: mocks.autoUpdater,
}));

View File

@@ -14,4 +14,7 @@ declare module "electron-updater" {
}
export const autoUpdater: AutoUpdater;
const electronUpdater: { autoUpdater: AutoUpdater };
export default electronUpdater;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 B

After

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 231 B

After

Width:  |  Height:  |  Size: 816 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 344 B

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -8,7 +8,6 @@ import {
type OpenDialogOptions,
type SaveDialogOptions,
} from "electron";
import { autoUpdater } from "electron-updater";
export interface WindowState {
x?: number;
@@ -167,34 +166,54 @@ export function showDesktopNotification(
}
export function setupAutoUpdater(mainWindow?: BrowserWindow): void {
try {
autoUpdater.autoDownload = true;
autoUpdater.autoInstallOnAppQuit = true;
void (async () => {
try {
const mod = (await import("electron-updater")) as {
default?: { autoUpdater?: unknown };
autoUpdater?: unknown;
};
const autoUpdater = (mod.default?.autoUpdater ?? mod.autoUpdater) as
| {
autoDownload: boolean;
autoInstallOnAppQuit: boolean;
on: (event: string, handler: (...args: unknown[]) => void) => unknown;
checkForUpdates: () => Promise<unknown>;
}
| undefined;
autoUpdater.on("update-available", (info) => {
showDesktopNotification("Fusion Update Available", "Update available — downloading in background", {
silent: true,
if (!autoUpdater) {
console.warn("[desktop/native] Auto-updater module loaded without autoUpdater export");
return;
}
autoUpdater.autoDownload = true;
autoUpdater.autoInstallOnAppQuit = true;
autoUpdater.on("update-available", (info) => {
showDesktopNotification("Fusion Update Available", "Update available — downloading in background", {
silent: true,
});
mainWindow?.webContents.send("update-available", info);
});
mainWindow?.webContents.send("update-available", info);
});
autoUpdater.on("update-downloaded", (info) => {
showDesktopNotification("Fusion Update Ready", "Update ready — will install on quit", {
silent: true,
autoUpdater.on("update-downloaded", (info) => {
showDesktopNotification("Fusion Update Ready", "Update ready — will install on quit", {
silent: true,
});
mainWindow?.webContents.send("update-downloaded", info);
});
mainWindow?.webContents.send("update-downloaded", info);
});
autoUpdater.on("error", (error) => {
console.error("[desktop/native] Auto-updater error", error);
});
autoUpdater.on("error", (error) => {
console.error("[desktop/native] Auto-updater error", error);
});
void autoUpdater.checkForUpdates().catch((error) => {
console.error("[desktop/native] Auto-updater check failed", error);
});
} catch (error) {
console.error("[desktop/native] Auto-updater unavailable", error);
}
await autoUpdater.checkForUpdates().catch((error: unknown) => {
console.error("[desktop/native] Auto-updater check failed", error);
});
} catch (error) {
console.warn("[desktop/native] Auto-updater unavailable", error);
}
})();
}
function normalizeServerBaseUrl(serverUrl: string): string | null {