From c1a74c195bd8f7bbfa20d957fd0931c4953db8d7 Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Sat, 1 Aug 2026 21:31:51 +0300 Subject: [PATCH] fix(electron): preserve HMR connection limits --- packages/electron/README.md | 2 +- packages/electron/main.mjs | 19 +++++++++++-------- packages/electron/startup-url-selection.mjs | 4 ++++ .../electron/startup-url-selection.test.mjs | 8 +++++++- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/packages/electron/README.md b/packages/electron/README.md index 1cb2f2dd..2e052833 100644 --- a/packages/electron/README.md +++ b/packages/electron/README.md @@ -19,7 +19,7 @@ The preload bridge exposes desktop-only APIs to the web UI through `window.__OPE | File | Purpose | |------|---------| | `main.mjs` | Electron main process, app lifecycle, windows, menus, deep links, native IPC handlers, updates, local server startup | -| `startup-url-selection.mjs` | Pure bundled/HMR startup probe policy used by main-process URL resolution | +| `startup-url-selection.mjs` | Pure bundled/HMR startup probe and loopback connection-limit policy | | `preload.mjs` | Safe bridge from the rendered UI to Electron IPC | | `ssh-manager.mjs` | SSH host import, connection lifecycle, tunnel/port forwarding helpers | | `scripts/electron-dev.mjs` | Desktop dev launcher with Vite HMR support | diff --git a/packages/electron/main.mjs b/packages/electron/main.mjs index 0b94d190..d82fbb3b 100644 --- a/packages/electron/main.mjs +++ b/packages/electron/main.mjs @@ -13,7 +13,7 @@ import updaterPkg from 'electron-updater'; import { ElectronSshManager } from './ssh-manager.mjs'; import { createTrayController } from './tray.mjs'; import { resolveManagedOpenCodeCwd } from './opencode-cwd.mjs'; -import { resolveStartupUrlProbePlan } from './startup-url-selection.mjs'; +import { resolveStartupUrlProbePlan, shouldIgnoreLoopbackConnectionLimit } from './startup-url-selection.mjs'; import { sanitizeRuntimeRequestHeaders } from './runtime-request-headers.mjs'; import { assertUpdaterCapability } from './updater-capability.mjs'; import { checkForDesktopUpdate } from './updater-check.mjs'; @@ -89,13 +89,16 @@ if (isDev) { } app.setAppUserModelId(APP_USER_MODEL_ID); app.commandLine.appendSwitch('proxy-bypass-list', '<-loopback>'); -// Lift Chromium's ~6-connections-per-host cap for the loopback backend. The -// packaged renderer is cross-origin (openchamber-ui:// → http://127.0.0.1), so -// every API call also needs a CORS preflight; during startup a few slow -// OpenCode-proxied requests otherwise hold the whole pool and every other -// request — including opening the first session — queues for seconds behind -// them. Loopback has no per-host connection cost that the cap protects. -app.commandLine.appendSwitch('ignore-connections-limit', '127.0.0.1,localhost'); +// Lift Chromium's per-host cap only for bundled UI. Applying this to Vite HMR +// lets the renderer request most of the module graph at once, overwhelming the +// dev server's transform pipeline and leaving the HTML splash visible for up +// to a minute before React mounts. +if (shouldIgnoreLoopbackConnectionLimit({ + development: isDev, + packagedUi: process.env.OPENCHAMBER_ELECTRON_USE_BUNDLED_UI === '1', +})) { + app.commandLine.appendSwitch('ignore-connections-limit', '127.0.0.1,localhost'); +} protocol.registerSchemesAsPrivileged([ { diff --git a/packages/electron/startup-url-selection.mjs b/packages/electron/startup-url-selection.mjs index c7827606..c34d0f04 100644 --- a/packages/electron/startup-url-selection.mjs +++ b/packages/electron/startup-url-selection.mjs @@ -2,3 +2,7 @@ export const resolveStartupUrlProbePlan = ({ development, packagedUi, skipLocalS probeHmrApi: development === true && packagedUi !== true && skipLocalServer !== true, probeHmrUi: development === true && packagedUi !== true, }); + +export const shouldIgnoreLoopbackConnectionLimit = ({ development, packagedUi }) => ( + development !== true || packagedUi === true +); diff --git a/packages/electron/startup-url-selection.test.mjs b/packages/electron/startup-url-selection.test.mjs index 48d3dba8..6497265d 100644 --- a/packages/electron/startup-url-selection.test.mjs +++ b/packages/electron/startup-url-selection.test.mjs @@ -1,7 +1,7 @@ import assert from 'node:assert/strict'; import test from 'node:test'; -import { resolveStartupUrlProbePlan } from './startup-url-selection.mjs'; +import { resolveStartupUrlProbePlan, shouldIgnoreLoopbackConnectionLimit } from './startup-url-selection.mjs'; test('bundled development never probes HMR endpoints', () => { assert.deepEqual(resolveStartupUrlProbePlan({ @@ -46,3 +46,9 @@ test('production does not probe HMR endpoints', () => { probeHmrUi: false, }); }); + +test('keeps Chromium connection limits for the Vite HMR module graph', () => { + assert.equal(shouldIgnoreLoopbackConnectionLimit({ development: true, packagedUi: false }), false); + assert.equal(shouldIgnoreLoopbackConnectionLimit({ development: true, packagedUi: true }), true); + assert.equal(shouldIgnoreLoopbackConnectionLimit({ development: false, packagedUi: false }), true); +});