refactor(desktop): render app on main window webContents

Drop the nested WebContentsView and host the renderer on the main
BrowserWindow's own webContents. Removes the manual bounds sync plumbing
and the extra webContents lifecycle that was only ever a child of the
main window.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-24 09:39:17 +08:00
parent b48729e4a2
commit af600a6910
+18 -28
View File
@@ -2,11 +2,10 @@ import { hostname } from "node:os";
import { readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { BrowserWindow, WebContentsView, app, ipcMain, nativeTheme } from "electron/main";
import { BrowserWindow, app, ipcMain, nativeTheme } from "electron/main";
import type {
BrowserWindow as ElectronBrowserWindow,
WebContents as ElectronWebContents,
WebContentsView as ElectronWebContentsView,
} from "electron/main";
import type { DesktopAccountInfo, DesktopRuntimeSessionSyncRequest } from "@geo/shared-types";
@@ -170,42 +169,27 @@ function preloadPath(): string {
return join(__dirname, "../preload/bridge.cjs");
}
function syncViewBounds(window: ElectronBrowserWindow, view: ElectronWebContentsView): void {
const bounds = window.getContentBounds();
view.setBounds({ x: 0, y: 0, width: bounds.width, height: bounds.height });
}
async function mountRendererWebContents(window: ElectronBrowserWindow): Promise<void> {
const contents = window.webContents;
async function mountRendererView(window: ElectronBrowserWindow): Promise<void> {
const view = new WebContentsView({
webPreferences: {
preload: preloadPath(),
sandbox: true,
contextIsolation: true,
nodeIntegration: false,
},
});
view.webContents.setWindowOpenHandler(() => ({ action: "deny" }));
registerRendererDevtoolsProxyTarget(view.webContents);
registerObservedRequestRendererTarget(view.webContents);
mainRendererContents = view.webContents;
window.contentView.addChildView(view);
syncViewBounds(window, view);
window.on("resize", () => syncViewBounds(window, view));
contents.setWindowOpenHandler(() => ({ action: "deny" }));
registerRendererDevtoolsProxyTarget(contents);
registerObservedRequestRendererTarget(contents);
mainRendererContents = contents;
window.on("closed", () => {
if (mainRendererContents === view.webContents) {
if (mainRendererContents === contents) {
mainRendererContents = null;
}
});
const devServerURL = rendererURL();
if (devServerURL) {
await view.webContents.loadURL(devServerURL);
view.webContents.openDevTools({ mode: "detach" });
await contents.loadURL(devServerURL);
contents.openDevTools({ mode: "detach" });
return;
}
await view.webContents.loadFile(join(__dirname, "../renderer/index.html"));
await contents.loadFile(join(__dirname, "../renderer/index.html"));
}
async function createMainWindow(): Promise<ElectronBrowserWindow> {
@@ -218,6 +202,12 @@ async function createMainWindow(): Promise<ElectronBrowserWindow> {
titleBarStyle: "hiddenInset",
trafficLightPosition: { x: 12, y: 14 },
backgroundColor: nativeTheme.shouldUseDarkColors ? "#15191a" : "#eaf2ff",
webPreferences: {
preload: preloadPath(),
sandbox: true,
contextIsolation: true,
nodeIntegration: false,
},
});
const fallbackReveal = setTimeout(() => {
@@ -253,7 +243,7 @@ async function createMainWindow(): Promise<ElectronBrowserWindow> {
});
});
await mountRendererView(window);
await mountRendererWebContents(window);
return window;
}