Skip to content

Commit edd82eb

Browse files
committed
feat(desktop): refine settings proxy panel and silent Windows updates.
Move proxy address and health actions under the core row, run NSIS upgrades with /S to the current install dir, and bump desktop to v0.2.3.
1 parent 0be4866 commit edd82eb

7 files changed

Lines changed: 99 additions & 49 deletions

File tree

electron/desktop-update.js

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,12 +221,33 @@ async function verifyInstallerChecksum(installerPath, versionTag) {
221221
return true;
222222
}
223223

224+
function resolveDesktopInstallDir() {
225+
const execPath = process.execPath;
226+
if (!execPath) return "";
227+
if (process.platform === "darwin") {
228+
// .../ClovAPI Switcher.app/Contents/MacOS/ClovAPI Switcher
229+
const macOSDir = path.dirname(path.dirname(path.dirname(execPath)));
230+
return macOSDir.endsWith(".app") ? macOSDir : "";
231+
}
232+
return path.dirname(execPath);
233+
}
234+
235+
function installerLaunchArgs(installDir = resolveDesktopInstallDir()) {
236+
if (process.platform === "win32") {
237+
const dir = String(installDir || "").trim();
238+
// NSIS silent upgrade: /S hides the wizard; /D= must be last and has no quotes.
239+
if (dir) return ["/S", `/D=${dir}`];
240+
return ["/S"];
241+
}
242+
return [];
243+
}
244+
224245
function launchInstaller(installerPath) {
225246
if (process.platform === "win32") {
226-
spawn(installerPath, [], {
247+
spawn(installerPath, installerLaunchArgs(), {
227248
detached: true,
228249
stdio: "ignore",
229-
windowsHide: false,
250+
windowsHide: true,
230251
}).unref();
231252
return;
232253
}
@@ -270,4 +291,6 @@ module.exports = {
270291
fetchLatestDesktopVersion,
271292
checkDesktopUpdate,
272293
downloadAndLaunchDesktopUpdate,
294+
resolveDesktopInstallDir,
295+
installerLaunchArgs,
273296
};

electron/desktop-update.test.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,16 @@ test("installerDownloadUrl uses versioned desktop path", () => {
3636
"https://downloads.clovapi.com/desktop/v0.1.5/clovapi-desktop-darwin-universal.dmg",
3737
);
3838
});
39+
40+
test("installerLaunchArgs uses NSIS silent flags on Windows", () => {
41+
const { installerLaunchArgs } = require("./desktop-update");
42+
if (process.platform !== "win32") {
43+
assert.deepEqual(installerLaunchArgs("C:\\Apps\\ClovAPI Switcher"), []);
44+
return;
45+
}
46+
assert.deepEqual(installerLaunchArgs("C:\\Apps\\ClovAPI Switcher"), [
47+
"/S",
48+
"/D=C:\\Apps\\ClovAPI Switcher",
49+
]);
50+
assert.deepEqual(installerLaunchArgs(""), ["/S"]);
51+
});

electron/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "clovapi-switcher",
33
"private": true,
4-
"version": "0.2.2",
4+
"version": "0.2.3",
55
"description": "ClovAPI Switcher desktop app",
66
"main": "main.js",
77
"type": "commonjs",
@@ -93,6 +93,9 @@
9393
},
9494
"nsis": {
9595
"oneClick": false,
96+
"allowToChangeInstallationDirectory": false,
97+
"allowElevation": true,
98+
"runAfterFinish": true,
9699
"shortcutName": "ClovAPI Switcher",
97100
"uninstallDisplayName": "ClovAPI Switcher"
98101
}

electron/ui/src/components/ListRow.svelte

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
centerContent?: boolean;
2929
onOpen?: () => void;
3030
leading?: Snippet;
31-
actions: Snippet;
31+
actions?: Snippet;
3232
} = $props();
3333
3434
const dotClass = $derived(
@@ -116,7 +116,9 @@
116116
{/each}
117117
</div>
118118
</div>
119-
<div class="flex shrink-0 flex-wrap items-center gap-2 sm:justify-end">
120-
{@render actions()}
121-
</div>
119+
{#if actions}
120+
<div class="flex shrink-0 flex-wrap items-center gap-2 sm:justify-end">
121+
{@render actions()}
122+
</div>
123+
{/if}
122124
</div>

electron/ui/src/components/ProxyPanel.svelte

Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,23 @@
1515
import ListRow from "./ListRow.svelte";
1616
import SectionCard from "./SectionCard.svelte";
1717
18+
const electronDev = isElectronDev();
19+
1820
const copy = $derived.by(() => {
1921
void i18n.locale;
2022
return {
2123
title: t("proxy.title"),
2224
description: t("proxy.description", { path: "/{providerId}/{modelId}/{apiStyle}/v1" }),
2325
service: t("proxy.service"),
24-
statusLine: store.proxyRunning
25-
? t("proxy.running", { url: store.proxyBaseUrl })
26-
: t("proxy.stopped"),
26+
serviceLine: store.proxyRunning ? t("proxy.runningShort") : t("proxy.stopped"),
2727
appVersion: t("proxy.appVersion"),
2828
appVersionLine: store.appVersion
2929
? t("proxy.appVersionLine", { version: store.appVersion })
3030
: t("proxy.appVersionUnknown"),
3131
coreVersion: t("proxy.coreVersion"),
32-
coreVersionLine: store.coreVersion
33-
? t("proxy.coreVersionLine", { version: store.coreVersion })
34-
: t("proxy.coreVersionUnknown"),
32+
coreTitle: store.coreVersion
33+
? t("proxy.coreTitle", { version: store.coreVersion })
34+
: t("proxy.coreVersion"),
3535
test: t("common.test"),
3636
testing: t("common.testing"),
3737
restart: t("proxy.restart"),
@@ -43,7 +43,19 @@
4343
};
4444
});
4545
46-
const electronDev = isElectronDev();
46+
const coreLines = $derived.by(() => {
47+
void i18n.locale;
48+
const lines: string[] = [];
49+
if (store.proxyRunning) {
50+
lines.push(t("proxy.coreProxyAddress", { url: store.proxyBaseUrl }));
51+
} else if (!store.coreVersion) {
52+
lines.push(t("proxy.coreVersionUnknown"));
53+
}
54+
if (electronDev) {
55+
lines.push(t("proxy.updateDisabledInDev"));
56+
}
57+
return lines;
58+
});
4759
4860
const proxyHealthTest = $derived(store.proxyHealthTest);
4961
const proxyHealthTesting = $derived(proxyHealthTest?.status === "testing");
@@ -53,6 +65,7 @@
5365
const coreUpdateCheck = $derived(store.coreUpdateCheck);
5466
const coreUpdateTesting = $derived(coreUpdateCheck?.status === "testing" || store.coreUpdating);
5567
const coreUpdateBusy = $derived(store.running || coreUpdateTesting);
68+
const coreActionBusy = $derived(store.running || proxyHealthTesting);
5669
5770
function rowTestStatus(value: string | undefined): "" | ModelTestStatus {
5871
if (value === "testing" || value === "pass" || value === "fail") return value;
@@ -61,27 +74,7 @@
6174
</script>
6275

6376
<SectionCard title={copy.title} description={copy.description}>
64-
<ListRow
65-
title={copy.service}
66-
lines={[copy.statusLine]}
67-
showStatusDot={Boolean(proxyHealthTest?.status)}
68-
testStatus={rowTestStatus(proxyHealthTest?.status)}
69-
testSummary={proxyHealthTest?.summary || ""}
70-
>
71-
{#snippet actions()}
72-
<Button
73-
size="sm"
74-
variant="outline"
75-
disabled={store.running || proxyHealthTesting}
76-
onclick={() => void runProxyHealthTest()}
77-
>
78-
{proxyHealthTesting ? copy.testing : copy.test}
79-
</Button>
80-
<Button size="sm" disabled={store.running || proxyHealthTesting} onclick={() => void restartLocalProxy()}>
81-
{copy.restart}
82-
</Button>
83-
{/snippet}
84-
</ListRow>
77+
<ListRow title={copy.service} lines={[copy.serviceLine]} />
8578

8679
<ListRow
8780
title={copy.appVersion}
@@ -109,12 +102,24 @@
109102
</ListRow>
110103

111104
<ListRow
112-
title={copy.coreVersion}
113-
lines={electronDev ? [copy.coreVersionLine, copy.updateDisabledInDev] : [copy.coreVersionLine]}
114-
testStatus={electronDev ? "" : rowTestStatus(coreUpdateCheck?.status)}
115-
testSummary={electronDev ? "" : coreUpdateCheck?.summary || ""}
105+
title={copy.coreTitle}
106+
lines={coreLines}
107+
showStatusDot={Boolean(proxyHealthTest?.status)}
108+
testStatus={rowTestStatus(proxyHealthTest?.status)}
109+
testSummary={proxyHealthTest?.summary || ""}
116110
>
117111
{#snippet actions()}
112+
<Button
113+
size="sm"
114+
variant="outline"
115+
disabled={coreActionBusy}
116+
onclick={() => void runProxyHealthTest()}
117+
>
118+
{proxyHealthTesting ? copy.testing : copy.test}
119+
</Button>
120+
<Button size="sm" disabled={coreActionBusy} onclick={() => void restartLocalProxy()}>
121+
{copy.restart}
122+
</Button>
118123
{#if !electronDev}
119124
<Button
120125
size="sm"

electron/ui/src/lib/i18n/locales/en.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,21 +113,23 @@ const en = {
113113
service: "Proxy service",
114114
appVersion: "Desktop app",
115115
coreVersion: "Core",
116+
coreTitle: "Core v{version}",
116117
appVersionLine: "v{version}",
117118
coreVersionLine: "v{version}",
118119
appVersionUnknown: "Unknown",
119120
coreVersionUnknown: "Unknown — start the proxy or check for updates",
120-
running: "Running · {url}",
121-
stopped: "Stopped · starts when you apply a CLI, or restart manually",
121+
runningShort: "Running",
122+
coreProxyAddress: "Proxy address · {url}",
123+
stopped: "Stopped · starts when you apply a CLI, or restart the core manually",
122124
description:
123125
"CLIs point base URL at {path}; the proxy routes by path. No upstream binding in active. Proxy is always enabled.",
124-
restart: "Restart",
126+
restart: "Restart core",
125127
checkUpdate: "Check for updates",
126128
installUpdate: "Install update",
127129
appInstallUpdate: "Install and restart",
128130
updating: "Installing update…",
129131
appDownloading: "Downloading update…",
130-
appInstallLaunching: "Installer opened — quitting app…",
132+
appInstallLaunching: "Installing silently — quitting app…",
131133
updateUpToDate: "Up to date",
132134
updateAvailable: "Update available · v{latest}",
133135
updateInstalled: "Updated to v{version}",
@@ -268,7 +270,7 @@ const en = {
268270
appUpdateUnsupported: "Desktop update is not available in this environment.",
269271
appUpdateCheckFailed: "Failed to check desktop update",
270272
appUpdateInstallFailed: "Failed to download or launch desktop update",
271-
appUpdateLaunching: "Installer opened — the app will quit so you can finish installing",
273+
appUpdateLaunching: "Installing silently — the app will quit",
272274
clovapiUnavailable: "clovapi CLI is not available",
273275
clovapiStartFailed: "Failed to start clovapi",
274276
clovapiMissing: "clovapi CLI not found — build core/clovapi or add to PATH",

electron/ui/src/lib/i18n/locales/zh.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,21 +111,23 @@ const zh = {
111111
service: "代理服务",
112112
appVersion: "桌面应用",
113113
coreVersion: "内核",
114+
coreTitle: "内核 v{version}",
114115
appVersionLine: "v{version}",
115116
coreVersionLine: "v{version}",
116117
appVersionUnknown: "未知",
117118
coreVersionUnknown: "未知 — 启动代理或检查更新",
118-
running: "运行中 · {url}",
119-
stopped: "未运行 · 应用 CLI 时将自动启动,也可手动重启服务",
119+
runningShort: "运行中",
120+
coreProxyAddress: "代理地址 · {url}",
121+
stopped: "未运行 · 应用 CLI 时将自动启动,也可手动重启内核",
120122
description:
121123
"CLI 将 base URL 指向 {path};代理按路径转发,无需在 active 中绑定上游。代理始终启用。",
122-
restart: "重启服务",
124+
restart: "重启内核",
123125
checkUpdate: "检查更新",
124126
installUpdate: "安装更新",
125127
appInstallUpdate: "安装并重启",
126128
updating: "正在安装更新…",
127129
appDownloading: "正在下载更新…",
128-
appInstallLaunching: "安装程序已启动,应用即将退出…",
130+
appInstallLaunching: "正在静默安装,应用即将退出…",
129131
updateUpToDate: "已是最新版本",
130132
updateAvailable: "有可用更新 · v{latest}",
131133
updateInstalled: "已更新至 v{version}",
@@ -264,7 +266,7 @@ const zh = {
264266
appUpdateUnsupported: "当前环境不支持桌面应用更新",
265267
appUpdateCheckFailed: "检查桌面应用更新失败",
266268
appUpdateInstallFailed: "下载或启动桌面安装包失败",
267-
appUpdateLaunching: "安装程序已打开,应用即将退出以完成安装",
269+
appUpdateLaunching: "正在静默安装,应用即将退出",
268270
clovapiUnavailable: "当前环境无法调用 clovapi",
269271
clovapiStartFailed: "clovapi 启动失败",
270272
clovapiMissing: "未找到 clovapi CLI,请先构建 core/clovapi 或安装到 PATH",

0 commit comments

Comments
 (0)