feat(FN-1076): integrate desktop IPC bridge and lifecycle modules

- Extract main-process IPC registration into src/ipc.ts for window controls, system info, tray status, updater checks, and native dialogs
- Refactor main.ts startup into initializeApp/run and wire menu, tray, deep-link, updater, and window-state restore/quit cleanup
- Rework preload bridge to expose typed window.fusionAPI subscriptions and replace preload.d.ts with shared src/types.d.ts global declarations
- Add comprehensive tests for IPC handlers, preload contracts, and main-process integration behavior
- Document IPC channels, preload API, and lifecycle sequencing in packages/desktop/README.md
This commit is contained in:
gsxdsm
2026-04-08 00:45:23 -07:00
parent 750911a809
commit 8904fbf95d
11 changed files with 996 additions and 357 deletions

View File

@@ -1,66 +1,42 @@
import { contextBridge, ipcRenderer } from "electron";
import type { ElectronAPI, ElectronApiResponsePayload, WindowControlAction } from "./renderer/types";
import type { DeepLinkResult, FusionAPI, SystemInfo, UpdateCheckResult } from "./types";
export interface FusionDesktopAPI {
getAppVersion(): Promise<string>;
quit(): void;
onDashboardReady(callback: () => void): () => void;
}
export type FusionDesktopAPI = FusionAPI;
const fusionDesktop: FusionDesktopAPI = {
getAppVersion(): Promise<string> {
return ipcRenderer.invoke("app:get-version");
},
quit(): void {
ipcRenderer.send("app:quit");
},
onDashboardReady(callback: () => void): () => void {
const listener = () => callback();
ipcRenderer.on("dashboard:ready", listener);
return () => {
ipcRenderer.removeListener("dashboard:ready", listener);
};
},
};
contextBridge.exposeInMainWorld("fusionAPI", {
// Window control
minimize: (): Promise<void> => ipcRenderer.invoke("window:minimize"),
maximize: (): Promise<boolean> => ipcRenderer.invoke("window:maximize"),
close: (): Promise<void> => ipcRenderer.invoke("window:close"),
isMaximized: (): Promise<boolean> => ipcRenderer.invoke("window:isMaximized"),
const electronAPI: ElectronAPI = {
invoke(channel: string, payload?: unknown): Promise<unknown> {
return ipcRenderer.invoke(channel, payload);
},
apiRequest(method: string, path: string, body?: unknown): Promise<ElectronApiResponsePayload> {
return ipcRenderer.invoke("api-request", { method, path, body });
},
getServerPort(): Promise<number> {
return ipcRenderer.invoke("server:get-port");
},
windowControl(action: WindowControlAction): Promise<boolean | void> {
return ipcRenderer.invoke("window:control", action);
},
onUpdateAvailable(callback: (info: Record<string, unknown>) => void): () => void {
const listener = (_event: unknown, info: Record<string, unknown>) => {
callback(info);
};
ipcRenderer.on("update:available", listener);
return () => {
ipcRenderer.removeListener("update:available", listener);
};
},
installUpdate(): Promise<void> {
return ipcRenderer.invoke("update:install");
},
onDeepLink(callback: (url: string) => void): () => void {
const listener = (_event: unknown, url: string) => {
callback(url);
};
ipcRenderer.on("deep-link", listener);
return () => {
ipcRenderer.removeListener("deep-link", listener);
};
},
getPlatform() {
return ipcRenderer.invoke("system:get-platform");
},
};
// App info
getSystemInfo: (): Promise<SystemInfo> => ipcRenderer.invoke("app:getSystemInfo"),
checkForUpdates: (): Promise<UpdateCheckResult> => ipcRenderer.invoke("app:checkForUpdates"),
contextBridge.exposeInMainWorld("fusionDesktop", fusionDesktop);
contextBridge.exposeInMainWorld("electronAPI", electronAPI);
// Tray status
updateTrayStatus: (status: string): Promise<void> => ipcRenderer.invoke("tray:updateStatus", status),
// Native dialogs
showExportDialog: (): Promise<string | null> => ipcRenderer.invoke("native:showExportDialog"),
showImportDialog: (): Promise<string | null> => ipcRenderer.invoke("native:showImportDialog"),
// Deep link events (main → renderer)
onDeepLink: (callback: (result: DeepLinkResult) => void): (() => void) => {
const handler = (_event: Electron.IpcRendererEvent, result: DeepLinkResult) => callback(result);
ipcRenderer.on("deep-link", handler);
return () => ipcRenderer.removeListener("deep-link", handler);
},
// Auto-updater events (main → renderer)
onUpdateAvailable: (callback: (info: { version: string }) => void): (() => void) => {
const handler = (_event: Electron.IpcRendererEvent, info: { version: string }) => callback(info);
ipcRenderer.on("update-available", handler);
return () => ipcRenderer.removeListener("update-available", handler);
},
onUpdateDownloaded: (callback: () => void): (() => void) => {
const handler = () => callback();
ipcRenderer.on("update-downloaded", handler);
return () => ipcRenderer.removeListener("update-downloaded", handler);
},
});