Skip to content

Commit c9b89e7

Browse files
joohwcursoragent
andcommitted
release(desktop): v0.1.3 with dev startup fixes and settings version split
Fix Windows dev Electron launch so preload bridges inject reliably, split Settings into desktop app vs core version rows, and improve dev lifecycle (port cleanup, dev-only userData, quit-on-close). Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 14efe4f commit c9b89e7

19 files changed

Lines changed: 541 additions & 156 deletions

File tree

electron/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ npm install
3939
npm run dev
4040
```
4141

42-
Development runs Vite on port 31873 and Electron with `ELECTRON_DEV=1`.
42+
Development runs Vite on `http://127.0.0.1:31873` and Electron with `ELECTRON_DEV=1`. **Use the Electron window that opens automatically** — do not open the Vite URL in a browser (the UI will not work there). Closing the Electron window in dev mode quits the app and stops Vite (via `scripts/dev.mjs`). If `Port 31873 is already in use` persists, run `npm run dev` again — the script tries to free the port automatically.
4343

4444
Production-style (built UI):
4545

electron/config-paths.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ function electronUserDataDir() {
2424
return path.join(configDir(), "desktop");
2525
}
2626

27+
function electronDevUserDataDir() {
28+
return path.join(configDir(), "desktop-dev");
29+
}
30+
2731
function callLogsDBPath() {
2832
return path.join(logsDir(), "call-logs.sqlite");
2933
}
@@ -33,5 +37,6 @@ module.exports = {
3337
cliBinPath,
3438
logsDir,
3539
electronUserDataDir,
40+
electronDevUserDataDir,
3641
callLogsDBPath,
3742
};

electron/main.js

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,26 @@ const {
1111
resolveClovapiExecutable: resolveBundledClovapiExecutable,
1212
} = require("./clovapi-exec");
1313
const { buildTrayMenuModel, isValidTrayTab, trayStatusSummary, trayTooltip } = require("./tray-menu");
14-
const { cliBinPath, electronUserDataDir } = require("./config-paths");
14+
const { cliBinPath, electronDevUserDataDir, electronUserDataDir } = require("./config-paths");
1515
const { sanitizeForIpc } = require("./ipc-utils");
1616

17+
const isElectronDev =
18+
process.env.ELECTRON_DEV === "1" || process.argv.includes("--clovapi-dev");
19+
1720
// Overlay scrollbars float above content instead of reserving layout width (Windows/Linux).
1821
app.commandLine.appendSwitch("enable-features", "OverlayScrollbar,FluentOverlayScrollbar");
1922
app.setName("ClovAPI Switcher");
2023

21-
fs.mkdirSync(electronUserDataDir(), { recursive: true });
22-
app.setPath("userData", electronUserDataDir());
24+
const electronDataDir = isElectronDev ? electronDevUserDataDir() : electronUserDataDir();
25+
fs.mkdirSync(electronDataDir, { recursive: true });
26+
app.setPath("userData", electronDataDir);
27+
28+
if (isElectronDev) {
29+
app.commandLine.appendSwitch("disk-cache-dir", path.join(electronDataDir, "cache"));
30+
app.commandLine.appendSwitch("disable-gpu-shader-disk-cache");
31+
}
2332

24-
const gotSingleInstanceLock = app.requestSingleInstanceLock();
33+
const gotSingleInstanceLock = isElectronDev ? true : app.requestSingleInstanceLock();
2534
if (!gotSingleInstanceLock) {
2635
app.quit();
2736
}
@@ -338,15 +347,26 @@ function createWindow() {
338347
icon: windowIconPath(),
339348
title: "ClovAPI Switcher",
340349
webPreferences: {
341-
preload: path.join(__dirname, "preload.js"),
350+
preload: path.resolve(__dirname, "preload.js"),
342351
contextIsolation: true,
343-
nodeIntegration: false
344-
}
352+
nodeIntegration: false,
353+
sandbox: false,
354+
},
345355
});
356+
if (isElectronDev) {
357+
mainWindow.webContents.on("preload-error", (_event, preloadPath, error) => {
358+
emitOutput("stderr", `[dev] preload failed (${preloadPath}): ${error?.message || error}\n`);
359+
});
360+
}
346361
forceLightModeForWindow(mainWindow);
347362

348363
mainWindow.on("close", (event) => {
349364
if (quitting) return;
365+
if (isElectronDev) {
366+
quitting = true;
367+
app.quit();
368+
return;
369+
}
350370
event.preventDefault();
351371
hideMainWindow();
352372
});
@@ -361,9 +381,27 @@ function createWindow() {
361381
void updateTrayMenu();
362382
});
363383

364-
const devUrl = process.env.ELECTRON_DEV === "1" ? process.env.VITE_DEV_SERVER_URL || "http://localhost:31873" : "";
384+
const devUrl = isElectronDev ? process.env.VITE_DEV_SERVER_URL || "http://127.0.0.1:31873" : "";
365385
if (devUrl) {
366386
void mainWindow.loadURL(devUrl);
387+
mainWindow.webContents.once("did-finish-load", () => {
388+
void (async () => {
389+
try {
390+
const hasProfiles = await mainWindow.webContents.executeJavaScript(
391+
"Boolean(window.clovapiProfiles?.load)",
392+
true,
393+
);
394+
emitOutput("system", `[dev] preload bridge profiles=${hasProfiles}\n`);
395+
if (!hasProfiles) {
396+
emitOutput("stderr", "[dev] preload bridge missing — restart npm run dev\n");
397+
}
398+
} catch (error) {
399+
const message = error instanceof Error ? error.message : String(error);
400+
emitOutput("stderr", `[dev] bridge check failed: ${message}\n`);
401+
}
402+
mainWindow.webContents.openDevTools({ mode: "detach" });
403+
})();
404+
});
367405
} else {
368406
void mainWindow.loadFile(path.join(__dirname, "ui-dist", "index.html"));
369407
}
@@ -554,6 +592,8 @@ function stopRunningProcess() {
554592
}
555593
}
556594

595+
ipcMain.handle("app:version", () => app.getVersion());
596+
557597
ipcMain.handle("cli:run", async (_event, payload) => {
558598
if (runningProcess) {
559599
return { ok: false, error: "A command is already running." };
@@ -1043,7 +1083,9 @@ if (gotSingleInstanceLock) {
10431083
app.whenReady().then(async () => {
10441084
nativeTheme.themeSource = "light";
10451085
createWindow();
1046-
createTray();
1086+
if (!isElectronDev) {
1087+
createTray();
1088+
}
10471089
watchCoreDevBinary();
10481090
try {
10491091
await proxyManager.autostartIfAllowed();

electron/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
{
22
"name": "clovapi-switcher",
33
"private": true,
4-
"version": "0.1.2",
4+
"version": "0.1.3",
55
"description": "ClovAPI Switcher desktop app",
66
"main": "main.js",
77
"type": "commonjs",
88
"scripts": {
99
"dev:ui": "vite --config ui/vite.config.mjs",
1010
"prepare:cli:dev": "node scripts/prepare-dev-cli.mjs",
11-
"dev": "concurrently -k \"npm run dev:ui\" \"wait-on tcp:31873 && cross-env ELECTRON_DEV=1 electron .\"",
11+
"dev": "node scripts/dev.mjs",
1212
"build:ui": "vite build --config ui/vite.config.mjs",
1313
"build:icons": "node scripts/build-icons.mjs",
1414
"build:mac": "npm run build:ui && npm run build:icons && electron-builder --mac dmg",

0 commit comments

Comments
 (0)