From af600a6910d91d8dcf14f47618d70cf9dea7a0aa Mon Sep 17 00:00:00 2001 From: liangxu Date: Fri, 24 Apr 2026 09:39:17 +0800 Subject: [PATCH] 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) --- apps/desktop-client/src/main/bootstrap.ts | 46 +++++++++-------------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/apps/desktop-client/src/main/bootstrap.ts b/apps/desktop-client/src/main/bootstrap.ts index ce33009..c8ec0f4 100644 --- a/apps/desktop-client/src/main/bootstrap.ts +++ b/apps/desktop-client/src/main/bootstrap.ts @@ -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 { + const contents = window.webContents; -async function mountRendererView(window: ElectronBrowserWindow): Promise { - 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 { @@ -218,6 +202,12 @@ async function createMainWindow(): Promise { 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 { }); }); - await mountRendererView(window); + await mountRendererWebContents(window); return window; }