dsh-update-status 0.1.12 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/README.md +38 -51
- package/README.zh.md +37 -50
- package/assets/update-panel.png +0 -0
- package/lib/client.js +202 -443
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +47 -59
- package/lib/index.js +75 -89
- package/package.json +3 -2
package/lib/client.js
CHANGED
|
@@ -4,37 +4,28 @@ window.__ModuleLoader__.load({
|
|
|
4
4
|
var module = { exports: {} };
|
|
5
5
|
module.exports;
|
|
6
6
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
7
|
-
//#region src/shared/
|
|
8
|
-
const CHANNEL_ORDER = {
|
|
9
|
-
latest: 0,
|
|
10
|
-
next: 1,
|
|
11
|
-
alpha: 2
|
|
12
|
-
};
|
|
7
|
+
//#region src/shared/visual-state.ts
|
|
13
8
|
/**
|
|
14
|
-
*
|
|
15
|
-
* The selected channel and stable return path are always present; every other
|
|
16
|
-
* row must carry a version no earlier row already shows.
|
|
9
|
+
* Only a read the plugin could not complete is a chip-level problem.
|
|
17
10
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
11
|
+
* An advisory notice — a preview channel this bundle has not been verified against,
|
|
12
|
+
* say — leaves the chip surface alone and belongs in the panel. Repainting the brand
|
|
13
|
+
* row for a plugin-side bookkeeping fact is the surprise the breathing dot replaced:
|
|
14
|
+
* an operator who upgrades DSH ahead of this bundle must not watch the chip turn red.
|
|
15
|
+
*
|
|
16
|
+
* A Host older than `warningKind` sends no field at all; `undefined` then keeps the
|
|
17
|
+
* previous behaviour (any warning is a problem) rather than silently dropping a real
|
|
18
|
+
* failure on the floor.
|
|
25
19
|
*/
|
|
26
|
-
function
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
seenVersions.add(release.version);
|
|
36
|
-
}
|
|
37
|
-
return visible.sort((left, right) => CHANNEL_ORDER[left.channel] - CHANNEL_ORDER[right.channel]);
|
|
20
|
+
function isChipProblem(status) {
|
|
21
|
+
if (status === null) return false;
|
|
22
|
+
if (status.warningKind === void 0) return status.warning !== null;
|
|
23
|
+
return status.warningKind === "failure";
|
|
24
|
+
}
|
|
25
|
+
function visualState(status, loading, error) {
|
|
26
|
+
if (loading) return "loading";
|
|
27
|
+
if (error !== null || isChipProblem(status)) return status?.hasUpdate === true ? "update" : "problem";
|
|
28
|
+
return status?.hasUpdate === true ? "update" : "current";
|
|
38
29
|
}
|
|
39
30
|
//#endregion
|
|
40
31
|
//#region src/shared/types.ts
|
|
@@ -45,9 +36,13 @@ window.__ModuleLoader__.load({
|
|
|
45
36
|
* `harness.handle` / `host.call` are dynamic-Cordis-only closure APIs. The
|
|
46
37
|
* endpoint vocabulary remains deliberately small and private to this plugin
|
|
47
38
|
* channel.
|
|
39
|
+
*
|
|
40
|
+
* There is ONE release line, not a channel catalogue: the Host reports the
|
|
41
|
+
* newest version the npm registry publishes under any dist-tag, and the client
|
|
42
|
+
* compares it with the running version. `latest`, `next` and `alpha` are npm's
|
|
43
|
+
* bookkeeping, not a user choice this plugin asks anyone to make.
|
|
48
44
|
*/
|
|
49
45
|
const PLUGIN_ID$1 = "dsh-update-status";
|
|
50
|
-
const PACKAGE_NAME = "@deepseek-ai/dsh";
|
|
51
46
|
/** Shared Connection RPC channel. Custom prefixes 405 on the SPA fallback. */
|
|
52
47
|
const UPDATE_STATUS_CHANNEL = "/api";
|
|
53
48
|
const MAX_CACHE_TTL_MINUTES = 1440;
|
|
@@ -58,54 +53,6 @@ window.__ModuleLoader__.load({
|
|
|
58
53
|
getStatus: "dsh-update-status.get-status",
|
|
59
54
|
checkUpdate: "dsh-update-status.check-update"
|
|
60
55
|
};
|
|
61
|
-
function isReleaseChannel(value) {
|
|
62
|
-
return value === "latest" || value === "next" || value === "alpha";
|
|
63
|
-
}
|
|
64
|
-
//#endregion
|
|
65
|
-
//#region src/shared/semver.ts
|
|
66
|
-
const SEMVER = /^(?:v)?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
67
|
-
function parseSemver(value) {
|
|
68
|
-
const match = SEMVER.exec(value.trim());
|
|
69
|
-
if (match === null) return void 0;
|
|
70
|
-
const major = Number(match[1]);
|
|
71
|
-
const minor = Number(match[2]);
|
|
72
|
-
const patch = Number(match[3]);
|
|
73
|
-
if (!Number.isSafeInteger(major) || !Number.isSafeInteger(minor) || !Number.isSafeInteger(patch)) return void 0;
|
|
74
|
-
return {
|
|
75
|
-
major,
|
|
76
|
-
minor,
|
|
77
|
-
patch,
|
|
78
|
-
prerelease: match[4] === void 0 ? [] : match[4].split(".")
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
//#endregion
|
|
82
|
-
//#region src/shared/preview-guidance.ts
|
|
83
|
-
/** Pinned guidance only: never execute a command or mutate the followed channel. */
|
|
84
|
-
function previewCommand(status, version) {
|
|
85
|
-
if (version === null || version.trim() !== version || parseSemver(version) === void 0) return null;
|
|
86
|
-
if (status.installKind === "npm-global") return `npm install -g ${PACKAGE_NAME}@${version}`;
|
|
87
|
-
if (status.installKind === "pnpm-global") return `pnpm add -g ${PACKAGE_NAME}@${version}`;
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
//#endregion
|
|
91
|
-
//#region src/shared/visual-state.ts
|
|
92
|
-
/**
|
|
93
|
-
* Only a read the plugin could not complete is a chip-level problem.
|
|
94
|
-
*
|
|
95
|
-
* An advisory notice — a preview channel this bundle has not been verified against,
|
|
96
|
-
* say — leaves the chip surface alone and belongs in the panel. Repainting the brand
|
|
97
|
-
* row for a plugin-side bookkeeping fact is the surprise the breathing dot replaced:
|
|
98
|
-
* an operator who upgrades DSH ahead of this bundle must not watch the chip turn red.
|
|
99
|
-
*
|
|
100
|
-
* A Host older than `warningKind` sends no field at all; `undefined` then keeps the
|
|
101
|
-
* previous behaviour (any warning is a problem) rather than silently dropping a real
|
|
102
|
-
* failure on the floor.
|
|
103
|
-
*/
|
|
104
|
-
function isChipProblem(status) {
|
|
105
|
-
if (status === null) return false;
|
|
106
|
-
if (status.warningKind === void 0) return status.warning !== null;
|
|
107
|
-
return status.warningKind === "failure";
|
|
108
|
-
}
|
|
109
56
|
//#endregion
|
|
110
57
|
//#region src/client/i18n.ts
|
|
111
58
|
const DICTIONARY = {
|
|
@@ -157,13 +104,9 @@ window.__ModuleLoader__.load({
|
|
|
157
104
|
zh: "当前运行版本",
|
|
158
105
|
en: "Current running version"
|
|
159
106
|
},
|
|
160
|
-
"panel.
|
|
161
|
-
zh: "
|
|
162
|
-
en: "
|
|
163
|
-
},
|
|
164
|
-
"panel.channels": {
|
|
165
|
-
zh: "可用发布通道",
|
|
166
|
-
en: "Available release channels"
|
|
107
|
+
"panel.newer": {
|
|
108
|
+
zh: "可更新至",
|
|
109
|
+
en: "Newer version available"
|
|
167
110
|
},
|
|
168
111
|
"panel.compatVerified": {
|
|
169
112
|
zh: "已验证兼容",
|
|
@@ -177,14 +120,6 @@ window.__ModuleLoader__.load({
|
|
|
177
120
|
zh: "已知不兼容",
|
|
178
121
|
en: "Known incompatible"
|
|
179
122
|
},
|
|
180
|
-
"panel.selectChannel": {
|
|
181
|
-
zh: "选择此通道",
|
|
182
|
-
en: "Select channel"
|
|
183
|
-
},
|
|
184
|
-
"panel.selectedChannel": {
|
|
185
|
-
zh: "已选择",
|
|
186
|
-
en: "Selected"
|
|
187
|
-
},
|
|
188
123
|
"panel.notChecked": {
|
|
189
124
|
zh: "尚未取得",
|
|
190
125
|
en: "Not available yet"
|
|
@@ -194,8 +129,8 @@ window.__ModuleLoader__.load({
|
|
|
194
129
|
en: "Update available"
|
|
195
130
|
},
|
|
196
131
|
"panel.currentState": {
|
|
197
|
-
zh: "
|
|
198
|
-
en: "
|
|
132
|
+
zh: "已是最新",
|
|
133
|
+
en: "Up to date"
|
|
199
134
|
},
|
|
200
135
|
"panel.cached": {
|
|
201
136
|
zh: "来自 Host 缓存",
|
|
@@ -221,10 +156,6 @@ window.__ModuleLoader__.load({
|
|
|
221
156
|
zh: "检查中…",
|
|
222
157
|
en: "Checking…"
|
|
223
158
|
},
|
|
224
|
-
"panel.switchingChannel": {
|
|
225
|
-
zh: "正在切换通道…",
|
|
226
|
-
en: "Switching channel…"
|
|
227
|
-
},
|
|
228
159
|
"panel.releaseNotes": {
|
|
229
160
|
zh: "发布说明",
|
|
230
161
|
en: "Release notes"
|
|
@@ -261,17 +192,13 @@ window.__ModuleLoader__.load({
|
|
|
261
192
|
zh: "无法检查 npm registry:{detail}",
|
|
262
193
|
en: "Unable to check the npm registry: {detail}"
|
|
263
194
|
},
|
|
264
|
-
"warning.channelUnavailable": {
|
|
265
|
-
zh: "npm registry 未发布 {channel} 通道。",
|
|
266
|
-
en: "The npm registry does not publish a {channel} channel."
|
|
267
|
-
},
|
|
268
195
|
"warning.versionIncomparable": {
|
|
269
|
-
zh: "无法按 SemVer 比较当前版本 {currentVersion}
|
|
270
|
-
en: "Unable to compare the current version {currentVersion} with
|
|
196
|
+
zh: "无法按 SemVer 比较当前版本 {currentVersion} 与线上版本 {latestVersion}。",
|
|
197
|
+
en: "Unable to compare the current version {currentVersion} with the published version {latestVersion} using SemVer."
|
|
271
198
|
},
|
|
272
|
-
"warning.
|
|
273
|
-
zh: "
|
|
274
|
-
en: "
|
|
199
|
+
"warning.versionUnverified": {
|
|
200
|
+
zh: "线上版本 {version} 尚未验证与本插件兼容。",
|
|
201
|
+
en: "Version {version} has not been verified as compatible with this plugin."
|
|
275
202
|
},
|
|
276
203
|
"warning.staleSchemastery": {
|
|
277
204
|
zh: "本插件解析到的 @deepseek-ai/schemastery(版本 {version})来自 {path},不是 DSH 自带的那份,偏好字段无法标记为 volatile。请删除该残留目录并重启 DSH:rm -rf {nodeModulesDir}",
|
|
@@ -289,42 +216,6 @@ window.__ModuleLoader__.load({
|
|
|
289
216
|
zh: "升级前请先确认 DSH 的安装方式;本插件无法代为执行升级。",
|
|
290
217
|
en: "Confirm how DSH was installed before upgrading; this plugin cannot perform the upgrade for you."
|
|
291
218
|
},
|
|
292
|
-
"panel.static": {
|
|
293
|
-
zh: "此连接只显示静态版本;请在运行 DSH 的本机打开侧栏查看完整更新信息。",
|
|
294
|
-
en: "This connection shows only the static version. Open the sidebar on the computer running DSH for full update details."
|
|
295
|
-
},
|
|
296
|
-
"preview.open": {
|
|
297
|
-
zh: "查看预览版升级方式",
|
|
298
|
-
en: "View preview upgrade instructions"
|
|
299
|
-
},
|
|
300
|
-
"preview.close": {
|
|
301
|
-
zh: "收起升级说明",
|
|
302
|
-
en: "Hide upgrade instructions"
|
|
303
|
-
},
|
|
304
|
-
"preview.follow": {
|
|
305
|
-
zh: "关注通道只影响检查结果,不代表已安装或已切换版本。",
|
|
306
|
-
en: "Following a channel only changes update checks, not the installed version."
|
|
307
|
-
},
|
|
308
|
-
"preview.target": {
|
|
309
|
-
zh: "目标版本",
|
|
310
|
-
en: "Target version"
|
|
311
|
-
},
|
|
312
|
-
"preview.risk": {
|
|
313
|
-
zh: "预览版本可能不稳定,插件兼容性尚需确认。执行前请保存工作、备份配置并结束运行中的任务;安装后需手动重启 DSH。此页面不会安装或重启。",
|
|
314
|
-
en: "Preview builds may be unstable and plugin compatibility needs checking. Save work, back up configuration and finish active tasks before executing. Restart DSH manually afterwards. This page never installs or restarts."
|
|
315
|
-
},
|
|
316
|
-
"preview.unavailable": {
|
|
317
|
-
zh: "暂无可确认的预览目标,请先检查更新。",
|
|
318
|
-
en: "No confirmed preview target. Check for updates first."
|
|
319
|
-
},
|
|
320
|
-
"preview.manual": {
|
|
321
|
-
zh: "当前安装方式无法安全生成命令,请先确认安装来源;源码安装需按其构建说明操作。",
|
|
322
|
-
en: "Cannot safely generate a command for this installation. Confirm its source; source checkouts require their build instructions."
|
|
323
|
-
},
|
|
324
|
-
"preview.same": {
|
|
325
|
-
zh: "此目标与当前运行版本相同,无需重复安装。",
|
|
326
|
-
en: "This target is already running; no reinstall is needed."
|
|
327
|
-
},
|
|
328
219
|
"settings.title": {
|
|
329
220
|
zh: "版本与更新",
|
|
330
221
|
en: "Version & updates"
|
|
@@ -337,26 +228,6 @@ window.__ModuleLoader__.load({
|
|
|
337
228
|
zh: "关闭后不再渲染品牌行里的版本芯片。不会执行或安排升级。",
|
|
338
229
|
en: "When off, the version chip in the brand row is not rendered. No update is run or scheduled."
|
|
339
230
|
},
|
|
340
|
-
"settings.channel": {
|
|
341
|
-
zh: "关注发布通道",
|
|
342
|
-
en: "Release channel to follow"
|
|
343
|
-
},
|
|
344
|
-
"settings.channelLatest": {
|
|
345
|
-
zh: "稳定版(latest,推荐)",
|
|
346
|
-
en: "Stable (latest, recommended)"
|
|
347
|
-
},
|
|
348
|
-
"settings.channelNext": {
|
|
349
|
-
zh: "候选版(next)",
|
|
350
|
-
en: "Release candidate (next)"
|
|
351
|
-
},
|
|
352
|
-
"settings.channelAlpha": {
|
|
353
|
-
zh: "预览版(alpha)",
|
|
354
|
-
en: "Preview (alpha)"
|
|
355
|
-
},
|
|
356
|
-
"settings.channelHint": {
|
|
357
|
-
zh: "预览通道可能包含未稳定接口或插件兼容性变化。这里只检查并生成命令,不会安装。",
|
|
358
|
-
en: "Preview channels may contain unstable APIs or plugin compatibility changes. This only checks and generates a command; it never installs."
|
|
359
|
-
},
|
|
360
231
|
"settings.readonly": {
|
|
361
232
|
zh: "此连接的设置为只读;显示状态不受影响。",
|
|
362
233
|
en: "Settings are read-only on this connection; status display is unchanged."
|
|
@@ -377,16 +248,11 @@ window.__ModuleLoader__.load({
|
|
|
377
248
|
function warningText(warning, language = languageOf()) {
|
|
378
249
|
switch (warning.code) {
|
|
379
250
|
case "registry-unavailable": return t("warning.registryUnavailable", { detail: warning.detail }, language);
|
|
380
|
-
case "channel-unavailable": return t("warning.channelUnavailable", { channel: warning.channel }, language);
|
|
381
251
|
case "version-incomparable": return t("warning.versionIncomparable", {
|
|
382
252
|
currentVersion: warning.currentVersion,
|
|
383
|
-
|
|
384
|
-
selectedVersion: warning.selectedVersion
|
|
385
|
-
}, language);
|
|
386
|
-
case "preview-unverified": return t("warning.previewUnverified", {
|
|
387
|
-
channel: warning.channel,
|
|
388
|
-
version: warning.version
|
|
253
|
+
latestVersion: warning.latestVersion
|
|
389
254
|
}, language);
|
|
255
|
+
case "version-unverified": return t("warning.versionUnverified", { version: warning.version }, language);
|
|
390
256
|
case "stale-schemastery": {
|
|
391
257
|
const params = {
|
|
392
258
|
version: warning.version ?? "—",
|
|
@@ -433,11 +299,6 @@ window.__ModuleLoader__.load({
|
|
|
433
299
|
if (value.length <= 16) return value;
|
|
434
300
|
return value.slice(0, 15) + "…";
|
|
435
301
|
}
|
|
436
|
-
function channelLabel(channel) {
|
|
437
|
-
if (channel === "latest") return t("settings.channelLatest");
|
|
438
|
-
if (channel === "next") return t("settings.channelNext");
|
|
439
|
-
return t("settings.channelAlpha");
|
|
440
|
-
}
|
|
441
302
|
function compatibilityLabel(value) {
|
|
442
303
|
if (value === "verified") return t("panel.compatVerified");
|
|
443
304
|
if (value === "incompatible") return t("panel.compatIncompatible");
|
|
@@ -498,11 +359,6 @@ window.__ModuleLoader__.load({
|
|
|
498
359
|
]
|
|
499
360
|
});
|
|
500
361
|
}
|
|
501
|
-
function visualState(status, loading, error) {
|
|
502
|
-
if (loading) return "loading";
|
|
503
|
-
if (error !== null || isChipProblem(status)) return status?.hasUpdate === true ? "update" : "problem";
|
|
504
|
-
return status?.hasUpdate === true ? "update" : "current";
|
|
505
|
-
}
|
|
506
362
|
function badgeLabel(status, loading, error) {
|
|
507
363
|
const state = visualState(status, loading, error);
|
|
508
364
|
if (state === "loading") return t("brand.checking");
|
|
@@ -567,7 +423,7 @@ window.__ModuleLoader__.load({
|
|
|
567
423
|
};
|
|
568
424
|
if (status?.hasUpdate === true) return {
|
|
569
425
|
kind: "update",
|
|
570
|
-
text: t("panel.available")
|
|
426
|
+
text: `${t("panel.available")} · ${status.latestVersion ?? ""}`.trim()
|
|
571
427
|
};
|
|
572
428
|
if (status !== null) {
|
|
573
429
|
const warning = localizedWarning(status);
|
|
@@ -593,14 +449,63 @@ window.__ModuleLoader__.load({
|
|
|
593
449
|
element.focus();
|
|
594
450
|
} catch {}
|
|
595
451
|
}
|
|
452
|
+
/**
|
|
453
|
+
* The one upgrade command plus its copy affordance, shared by the panel and the
|
|
454
|
+
* settings section so both surfaces can never disagree about what to run.
|
|
455
|
+
*/
|
|
456
|
+
function CommandBlock({ command, disabled = false }) {
|
|
457
|
+
const commandRef = React.useRef(null);
|
|
458
|
+
const [copyMessage, setCopyMessage] = React.useState(null);
|
|
459
|
+
const copy = async () => {
|
|
460
|
+
setCopyMessage(null);
|
|
461
|
+
try {
|
|
462
|
+
if (!navigator.clipboard?.writeText) throw new Error("Clipboard API unavailable");
|
|
463
|
+
await navigator.clipboard.writeText(command);
|
|
464
|
+
setCopyMessage(t("panel.copied"));
|
|
465
|
+
} catch {
|
|
466
|
+
selectCommand(commandRef.current);
|
|
467
|
+
setCopyMessage(t("panel.copyFallback"));
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
471
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
472
|
+
className: "dus-command-label",
|
|
473
|
+
children: t("panel.command")
|
|
474
|
+
}),
|
|
475
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
476
|
+
ref: commandRef,
|
|
477
|
+
className: "dus-command",
|
|
478
|
+
tabIndex: 0,
|
|
479
|
+
children: command
|
|
480
|
+
}),
|
|
481
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
482
|
+
className: "dus-actions dus-command-actions",
|
|
483
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
484
|
+
className: "dus-action",
|
|
485
|
+
type: "button",
|
|
486
|
+
disabled,
|
|
487
|
+
onClick: () => {
|
|
488
|
+
copy();
|
|
489
|
+
},
|
|
490
|
+
children: t("panel.copy")
|
|
491
|
+
}), copyMessage !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
492
|
+
className: "dus-copy-message",
|
|
493
|
+
role: "status",
|
|
494
|
+
children: copyMessage
|
|
495
|
+
})]
|
|
496
|
+
}),
|
|
497
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
498
|
+
className: "dus-note",
|
|
499
|
+
children: t("panel.commandNote")
|
|
500
|
+
})
|
|
501
|
+
] });
|
|
502
|
+
}
|
|
596
503
|
/** Full detail from whichever page the operator is on (the transport is authenticated). */
|
|
597
504
|
function UpdatePanel({ ui }) {
|
|
598
505
|
const panel = useObservable(ui.panel);
|
|
599
506
|
const preferences = useObservable(ui.preferences);
|
|
600
507
|
const snapshot = useObservable(ui.status);
|
|
601
|
-
const commandRef = React.useRef(null);
|
|
602
508
|
const dialogRef = React.useRef(null);
|
|
603
|
-
const [copyMessage, setCopyMessage] = React.useState(null);
|
|
604
509
|
const visible = panel.open && preferences.sidebarEnabled;
|
|
605
510
|
React.useLayoutEffect(() => {
|
|
606
511
|
if (!visible) return void 0;
|
|
@@ -629,19 +534,8 @@ window.__ModuleLoader__.load({
|
|
|
629
534
|
const status = snapshot.status;
|
|
630
535
|
const warning = status === null ? null : localizedWarning(status);
|
|
631
536
|
const summary = statusSummary(status, snapshot.loading, snapshot.error);
|
|
632
|
-
const
|
|
633
|
-
const command =
|
|
634
|
-
const copy = async () => {
|
|
635
|
-
setCopyMessage(null);
|
|
636
|
-
try {
|
|
637
|
-
if (!navigator.clipboard?.writeText) throw new Error("Clipboard API unavailable");
|
|
638
|
-
await navigator.clipboard.writeText(command);
|
|
639
|
-
setCopyMessage(t("panel.copied"));
|
|
640
|
-
} catch {
|
|
641
|
-
selectCommand(commandRef.current);
|
|
642
|
-
setCopyMessage(t("panel.copyFallback"));
|
|
643
|
-
}
|
|
644
|
-
};
|
|
537
|
+
const hasUpdate = status?.hasUpdate === true && status.latestVersion !== null;
|
|
538
|
+
const command = status === null || !hasUpdate ? "" : localizedUpgradeGuidance(status);
|
|
645
539
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dialog", {
|
|
646
540
|
ref: dialogRef,
|
|
647
541
|
className: "dus-overlay-root",
|
|
@@ -698,28 +592,29 @@ window.__ModuleLoader__.load({
|
|
|
698
592
|
children: status?.currentVersion ?? "…"
|
|
699
593
|
})]
|
|
700
594
|
}),
|
|
701
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
595
|
+
hasUpdate && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
702
596
|
className: "dus-metadata-row",
|
|
703
597
|
children: [
|
|
704
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
598
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
705
599
|
className: "dus-meta-label",
|
|
706
|
-
children:
|
|
707
|
-
t("panel.latest"),
|
|
708
|
-
" · ",
|
|
709
|
-
status === null ? "latest" : channelLabel(status.channel)
|
|
710
|
-
]
|
|
600
|
+
children: t("panel.newer")
|
|
711
601
|
}),
|
|
712
602
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
713
603
|
className: "dus-meta-value",
|
|
714
|
-
children: status
|
|
604
|
+
children: status.latestVersion
|
|
715
605
|
}),
|
|
716
|
-
status
|
|
606
|
+
status.publishedAt !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
717
607
|
className: "dus-meta-sub",
|
|
718
608
|
children: [
|
|
719
609
|
t("panel.publishedAt"),
|
|
720
610
|
": ",
|
|
721
611
|
timeText(status.publishedAt)
|
|
722
612
|
]
|
|
613
|
+
}),
|
|
614
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
615
|
+
className: "dus-meta-sub dus-compat",
|
|
616
|
+
"data-compatibility": status.compatibility,
|
|
617
|
+
children: compatibilityLabel(status.compatibility)
|
|
723
618
|
})
|
|
724
619
|
]
|
|
725
620
|
}),
|
|
@@ -742,39 +637,6 @@ window.__ModuleLoader__.load({
|
|
|
742
637
|
})
|
|
743
638
|
]
|
|
744
639
|
}),
|
|
745
|
-
status !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
746
|
-
className: "dus-channel-list",
|
|
747
|
-
"aria-label": t("panel.channels"),
|
|
748
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
749
|
-
className: "dus-command-label",
|
|
750
|
-
children: t("panel.channels")
|
|
751
|
-
}), visibleChannelReleases(status).map((release) => {
|
|
752
|
-
const selected = release.channel === preferences.channel;
|
|
753
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
754
|
-
className: "dus-channel-row",
|
|
755
|
-
"data-selected": selected || void 0,
|
|
756
|
-
children: [
|
|
757
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: channelLabel(release.channel) }),
|
|
758
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: release.version ?? "—" }),
|
|
759
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
760
|
-
className: "dus-channel-compat",
|
|
761
|
-
"data-compatibility": release.compatibility,
|
|
762
|
-
children: compatibilityLabel(release.compatibility)
|
|
763
|
-
}),
|
|
764
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
765
|
-
className: "dus-channel-action",
|
|
766
|
-
type: "button",
|
|
767
|
-
disabled: selected || !preferences.writable || release.version === null,
|
|
768
|
-
"aria-label": `${selected ? t("panel.selectedChannel") : t("panel.selectChannel")}: ${channelLabel(release.channel)}`,
|
|
769
|
-
onClick: () => {
|
|
770
|
-
ui.preferences.setChannel(release.channel);
|
|
771
|
-
},
|
|
772
|
-
children: selected ? t("panel.selectedChannel") : t("panel.selectChannel")
|
|
773
|
-
})
|
|
774
|
-
]
|
|
775
|
-
}, release.channel);
|
|
776
|
-
})]
|
|
777
|
-
}),
|
|
778
640
|
warning !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
779
641
|
className: "dus-warning",
|
|
780
642
|
children: [
|
|
@@ -793,52 +655,25 @@ window.__ModuleLoader__.load({
|
|
|
793
655
|
}),
|
|
794
656
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
795
657
|
className: "dus-actions",
|
|
796
|
-
children: [
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
status
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
children: t("panel.releaseNotes")
|
|
812
|
-
}),
|
|
813
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
814
|
-
className: "dus-action",
|
|
815
|
-
type: "button",
|
|
816
|
-
disabled: switchingChannel || status === null,
|
|
817
|
-
onClick: () => {
|
|
818
|
-
copy();
|
|
819
|
-
},
|
|
820
|
-
children: t("panel.copy")
|
|
821
|
-
})
|
|
822
|
-
]
|
|
823
|
-
}),
|
|
824
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
825
|
-
className: "dus-command-label",
|
|
826
|
-
children: t("panel.command")
|
|
827
|
-
}),
|
|
828
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
829
|
-
ref: commandRef,
|
|
830
|
-
className: "dus-command",
|
|
831
|
-
tabIndex: 0,
|
|
832
|
-
children: command
|
|
833
|
-
}),
|
|
834
|
-
copyMessage !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
835
|
-
className: "dus-copy-message",
|
|
836
|
-
role: "status",
|
|
837
|
-
children: copyMessage
|
|
658
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
659
|
+
className: "dus-action",
|
|
660
|
+
type: "button",
|
|
661
|
+
disabled: snapshot.loading,
|
|
662
|
+
onClick: () => {
|
|
663
|
+
ui.status.refresh(preferences.cacheTtlMinutes);
|
|
664
|
+
},
|
|
665
|
+
children: snapshot.loading ? t("panel.checking") : t("panel.check")
|
|
666
|
+
}), status !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
667
|
+
className: "dus-action",
|
|
668
|
+
href: status.changelogUrl,
|
|
669
|
+
target: "_blank",
|
|
670
|
+
rel: "noreferrer",
|
|
671
|
+
children: t("panel.releaseNotes")
|
|
672
|
+
})]
|
|
838
673
|
}),
|
|
839
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
840
|
-
|
|
841
|
-
|
|
674
|
+
hasUpdate && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommandBlock, {
|
|
675
|
+
command,
|
|
676
|
+
disabled: snapshot.loading
|
|
842
677
|
}),
|
|
843
678
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
844
679
|
className: "dus-note",
|
|
@@ -852,9 +687,10 @@ window.__ModuleLoader__.load({
|
|
|
852
687
|
function UpdateSettings({ ui }) {
|
|
853
688
|
const preferences = useObservable(ui.preferences);
|
|
854
689
|
const snapshot = useObservable(ui.status);
|
|
855
|
-
const
|
|
856
|
-
const
|
|
857
|
-
const
|
|
690
|
+
const status = snapshot.status;
|
|
691
|
+
const summary = statusSummary(status, snapshot.loading, snapshot.error);
|
|
692
|
+
const hasUpdate = status?.hasUpdate === true && status.latestVersion !== null;
|
|
693
|
+
const command = status === null || !hasUpdate ? "" : localizedUpgradeGuidance(status);
|
|
858
694
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
859
695
|
className: "dus-settings",
|
|
860
696
|
children: [
|
|
@@ -889,94 +725,58 @@ window.__ModuleLoader__.load({
|
|
|
889
725
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
890
726
|
className: "dus-settings-card",
|
|
891
727
|
children: [
|
|
892
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
893
|
-
className: "dus-settings-field",
|
|
894
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("settings.channel") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
|
|
895
|
-
className: "dus-channel-select",
|
|
896
|
-
value: preferences.channel,
|
|
897
|
-
disabled: !preferences.writable,
|
|
898
|
-
onChange: (event) => {
|
|
899
|
-
ui.preferences.setChannel(event.currentTarget.value);
|
|
900
|
-
},
|
|
901
|
-
children: channelOptions.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
902
|
-
value: option.channel,
|
|
903
|
-
children: channelLabel(option.channel)
|
|
904
|
-
}, option.channel))
|
|
905
|
-
})]
|
|
906
|
-
}),
|
|
907
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
908
|
-
className: "dus-settings-hint",
|
|
909
|
-
children: t("settings.channelHint")
|
|
910
|
-
}),
|
|
911
728
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
912
729
|
className: "dus-settings-hint",
|
|
913
730
|
children: [
|
|
914
731
|
t("panel.current"),
|
|
915
732
|
": ",
|
|
916
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children:
|
|
733
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: status?.currentVersion ?? "…" })
|
|
917
734
|
]
|
|
918
735
|
}),
|
|
919
736
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
920
|
-
className: "dus-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
924
|
-
className: "dus-action",
|
|
925
|
-
type: "button",
|
|
926
|
-
"aria-expanded": showGuidance,
|
|
927
|
-
onClick: () => {
|
|
928
|
-
setShowGuidance(!showGuidance);
|
|
929
|
-
},
|
|
930
|
-
children: t(showGuidance ? "preview.close" : "preview.open")
|
|
737
|
+
className: "dus-state",
|
|
738
|
+
"data-kind": summary.kind,
|
|
739
|
+
children: summary.text
|
|
931
740
|
}),
|
|
932
|
-
|
|
933
|
-
|
|
741
|
+
hasUpdate && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
742
|
+
className: "dus-settings-hint",
|
|
934
743
|
children: [
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
940
|
-
className: "dus-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
onClick: () => {
|
|
944
|
-
ui.status.refresh(void 0, preferences.cacheTtlMinutes);
|
|
945
|
-
},
|
|
946
|
-
children: t(snapshot.loading ? "panel.checking" : "panel.check")
|
|
947
|
-
}),
|
|
948
|
-
snapshot.error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
949
|
-
role: "alert",
|
|
950
|
-
children: snapshot.error
|
|
951
|
-
}),
|
|
952
|
-
previews.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("preview.unavailable") }),
|
|
953
|
-
previews.map((release) => {
|
|
954
|
-
const status = snapshot.status;
|
|
955
|
-
const command = previewCommand(status, release.version);
|
|
956
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
957
|
-
className: "dus-settings-card",
|
|
958
|
-
children: [
|
|
959
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: [
|
|
960
|
-
channelLabel(release.channel),
|
|
961
|
-
" · ",
|
|
962
|
-
t("preview.target"),
|
|
963
|
-
": ",
|
|
964
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: release.version ?? t("panel.notChecked") })
|
|
965
|
-
] }),
|
|
966
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: compatibilityLabel(release.compatibility) }),
|
|
967
|
-
release.version === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("preview.unavailable") }) : release.version === status.currentVersion ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("preview.same") }) : command !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("panel.command") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
968
|
-
className: "dus-command",
|
|
969
|
-
tabIndex: 0,
|
|
970
|
-
children: command
|
|
971
|
-
})] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("preview.manual") })
|
|
972
|
-
]
|
|
973
|
-
}, release.channel);
|
|
974
|
-
}),
|
|
975
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
976
|
-
className: "dus-note",
|
|
977
|
-
children: t("panel.commandNote")
|
|
744
|
+
t("panel.newer"),
|
|
745
|
+
": ",
|
|
746
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: status.latestVersion }),
|
|
747
|
+
" · ",
|
|
748
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
749
|
+
className: "dus-compat",
|
|
750
|
+
"data-compatibility": status.compatibility,
|
|
751
|
+
children: compatibilityLabel(status.compatibility)
|
|
978
752
|
})
|
|
979
753
|
]
|
|
754
|
+
}),
|
|
755
|
+
snapshot.error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
756
|
+
role: "alert",
|
|
757
|
+
children: snapshot.error
|
|
758
|
+
}),
|
|
759
|
+
hasUpdate && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommandBlock, {
|
|
760
|
+
command,
|
|
761
|
+
disabled: snapshot.loading
|
|
762
|
+
}),
|
|
763
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
764
|
+
className: "dus-actions",
|
|
765
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
766
|
+
className: "dus-action",
|
|
767
|
+
type: "button",
|
|
768
|
+
disabled: snapshot.loading,
|
|
769
|
+
onClick: () => {
|
|
770
|
+
ui.status.refresh(preferences.cacheTtlMinutes);
|
|
771
|
+
},
|
|
772
|
+
children: snapshot.loading ? t("panel.checking") : t("panel.check")
|
|
773
|
+
}), status !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
774
|
+
className: "dus-action",
|
|
775
|
+
href: status.changelogUrl,
|
|
776
|
+
target: "_blank",
|
|
777
|
+
rel: "noreferrer",
|
|
778
|
+
children: t("panel.releaseNotes")
|
|
779
|
+
})]
|
|
980
780
|
})
|
|
981
781
|
]
|
|
982
782
|
})
|
|
@@ -985,7 +785,6 @@ window.__ModuleLoader__.load({
|
|
|
985
785
|
}
|
|
986
786
|
//#endregion
|
|
987
787
|
//#region src/client/contract.ts
|
|
988
|
-
/** Minimal structural client faces — runtime services stay owned by DSH. */
|
|
989
788
|
function errorMessage(error) {
|
|
990
789
|
return (error instanceof Error ? error.message : String(error)).replace(/\s+/g, " ").trim().slice(0, 220) || "unknown error";
|
|
991
790
|
}
|
|
@@ -999,19 +798,13 @@ window.__ModuleLoader__.load({
|
|
|
999
798
|
code: record.code,
|
|
1000
799
|
detail: record.detail
|
|
1001
800
|
};
|
|
1002
|
-
if (record.code === "
|
|
1003
|
-
code: record.code,
|
|
1004
|
-
channel: record.channel
|
|
1005
|
-
};
|
|
1006
|
-
if (record.code === "version-incomparable" && typeof record.currentVersion === "string" && isReleaseChannel(record.channel) && typeof record.selectedVersion === "string") return {
|
|
801
|
+
if (record.code === "version-incomparable" && typeof record.currentVersion === "string" && typeof record.latestVersion === "string") return {
|
|
1007
802
|
code: record.code,
|
|
1008
803
|
currentVersion: record.currentVersion,
|
|
1009
|
-
|
|
1010
|
-
selectedVersion: record.selectedVersion
|
|
804
|
+
latestVersion: record.latestVersion
|
|
1011
805
|
};
|
|
1012
|
-
if (record.code === "
|
|
806
|
+
if (record.code === "version-unverified" && typeof record.version === "string") return {
|
|
1013
807
|
code: record.code,
|
|
1014
|
-
channel: record.channel,
|
|
1015
808
|
version: record.version
|
|
1016
809
|
};
|
|
1017
810
|
if (record.code === "stale-schemastery") {
|
|
@@ -1030,8 +823,9 @@ window.__ModuleLoader__.load({
|
|
|
1030
823
|
function updateStatusOf(value) {
|
|
1031
824
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1032
825
|
const record = value;
|
|
1033
|
-
if (typeof record.currentVersion !== "string" || typeof record.hasUpdate !== "boolean" || typeof record.cached !== "boolean" || typeof record.installKind !== "string" || typeof record.upgradeCommand !== "string" || typeof record.releaseUrl !== "string" || typeof record.changelogUrl !== "string" || typeof record.packageName !== "string" ||
|
|
826
|
+
if (typeof record.currentVersion !== "string" || typeof record.hasUpdate !== "boolean" || typeof record.cached !== "boolean" || typeof record.installKind !== "string" || typeof record.upgradeCommand !== "string" || typeof record.releaseUrl !== "string" || typeof record.changelogUrl !== "string" || typeof record.packageName !== "string" || record.canApplyInPlace !== false) return void 0;
|
|
1034
827
|
if (!(record.installKind === "npm-global" || record.installKind === "pnpm-global" || record.installKind === "source-checkout" || record.installKind === "unknown")) return void 0;
|
|
828
|
+
if (record.compatibility !== "verified" && record.compatibility !== "unverified" && record.compatibility !== "incompatible") return void 0;
|
|
1035
829
|
const latestVersion = record.latestVersion === null ? null : stringOrNull(record.latestVersion);
|
|
1036
830
|
const checkedAt = record.checkedAt === null ? null : stringOrNull(record.checkedAt);
|
|
1037
831
|
const warning = record.warning === null ? null : stringOrNull(record.warning);
|
|
@@ -1047,24 +841,11 @@ window.__ModuleLoader__.load({
|
|
|
1047
841
|
warnings.push(parsed);
|
|
1048
842
|
}
|
|
1049
843
|
}
|
|
1050
|
-
const channels = [];
|
|
1051
|
-
for (const raw of record.channels) {
|
|
1052
|
-
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
1053
|
-
const item = raw;
|
|
1054
|
-
const version = item.version === null ? null : stringOrNull(item.version);
|
|
1055
|
-
const channelPublishedAt = item.publishedAt === null ? null : stringOrNull(item.publishedAt);
|
|
1056
|
-
if (!isReleaseChannel(item.channel) || item.version !== null && version === null || item.publishedAt !== null && channelPublishedAt === null || item.compatibility !== "verified" && item.compatibility !== "unverified" && item.compatibility !== "incompatible") return void 0;
|
|
1057
|
-
channels.push({
|
|
1058
|
-
channel: item.channel,
|
|
1059
|
-
version,
|
|
1060
|
-
publishedAt: channelPublishedAt,
|
|
1061
|
-
compatibility: item.compatibility
|
|
1062
|
-
});
|
|
1063
|
-
}
|
|
1064
844
|
return {
|
|
1065
845
|
currentVersion: record.currentVersion,
|
|
1066
846
|
latestVersion,
|
|
1067
847
|
hasUpdate: record.hasUpdate,
|
|
848
|
+
compatibility: record.compatibility,
|
|
1068
849
|
cached: record.cached,
|
|
1069
850
|
checkedAt,
|
|
1070
851
|
warning,
|
|
@@ -1076,8 +857,6 @@ window.__ModuleLoader__.load({
|
|
|
1076
857
|
changelogUrl: record.changelogUrl,
|
|
1077
858
|
publishedAt,
|
|
1078
859
|
packageName: record.packageName,
|
|
1079
|
-
channel: record.channel,
|
|
1080
|
-
channels,
|
|
1081
860
|
canApplyInPlace: false
|
|
1082
861
|
};
|
|
1083
862
|
}
|
|
@@ -1107,20 +886,24 @@ window.__ModuleLoader__.load({
|
|
|
1107
886
|
};
|
|
1108
887
|
/** First mount reads the Host's cached status; it never starts a browser timer. */
|
|
1109
888
|
async load(cacheTtlMinutes = 360) {
|
|
1110
|
-
await this.request(false,
|
|
889
|
+
await this.request(false, cacheTtlMinutes);
|
|
1111
890
|
}
|
|
1112
891
|
/** User gesture only: force the Host to bypass TTL (while retaining single-flight). */
|
|
1113
|
-
async refresh(
|
|
1114
|
-
await this.request(true,
|
|
892
|
+
async refresh(cacheTtlMinutes = 360) {
|
|
893
|
+
await this.request(true, cacheTtlMinutes);
|
|
1115
894
|
}
|
|
1116
|
-
/**
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
895
|
+
/**
|
|
896
|
+
* Ordinary read that waits out an in-flight request first.
|
|
897
|
+
*
|
|
898
|
+
* Used once per mount, when the stored cache policy arrives after the first
|
|
899
|
+
* read already started: joining that request would keep the default policy, so
|
|
900
|
+
* the follow-up read has to come after it settles.
|
|
901
|
+
*/
|
|
902
|
+
async reload(cacheTtlMinutes) {
|
|
903
|
+
const pending = this.inFlight;
|
|
904
|
+
if (pending !== void 0) await pending;
|
|
905
|
+
if (this.stopped) return;
|
|
906
|
+
await this.request(false, cacheTtlMinutes);
|
|
1124
907
|
}
|
|
1125
908
|
stop() {
|
|
1126
909
|
this.stopped = true;
|
|
@@ -1131,7 +914,7 @@ window.__ModuleLoader__.load({
|
|
|
1131
914
|
this.snapshot = next;
|
|
1132
915
|
for (const listener of this.listeners) listener();
|
|
1133
916
|
}
|
|
1134
|
-
request(force,
|
|
917
|
+
request(force, cacheTtlMinutes) {
|
|
1135
918
|
if (this.inFlight !== void 0) return this.inFlight;
|
|
1136
919
|
const rpc = this.connection.rpc;
|
|
1137
920
|
if (rpc === void 0 || typeof rpc.call !== "function") {
|
|
@@ -1153,12 +936,8 @@ window.__ModuleLoader__.load({
|
|
|
1153
936
|
const ttl = isCacheTtlMinutes(cacheTtlMinutes) ? cacheTtlMinutes : 360;
|
|
1154
937
|
const raw = await rpc.call(UPDATE_STATUS_CHANNEL, endpoint, force ? {
|
|
1155
938
|
force: true,
|
|
1156
|
-
channel,
|
|
1157
939
|
cacheTtlMinutes: ttl
|
|
1158
|
-
} : {
|
|
1159
|
-
channel,
|
|
1160
|
-
cacheTtlMinutes: ttl
|
|
1161
|
-
});
|
|
940
|
+
} : { cacheTtlMinutes: ttl });
|
|
1162
941
|
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) throw new Error("invalid update-status RPC response");
|
|
1163
942
|
const envelope = raw;
|
|
1164
943
|
if (envelope.ok !== true) throw new Error(typeof envelope.error?.message === "string" ? envelope.error.message : "update-status RPC failed");
|
|
@@ -1188,7 +967,6 @@ window.__ModuleLoader__.load({
|
|
|
1188
967
|
};
|
|
1189
968
|
const INITIAL_PREFERENCES = {
|
|
1190
969
|
sidebarEnabled: true,
|
|
1191
|
-
channel: "latest",
|
|
1192
970
|
cacheTtlMinutes: 360,
|
|
1193
971
|
writable: false,
|
|
1194
972
|
status: "loading"
|
|
@@ -1212,7 +990,6 @@ window.__ModuleLoader__.load({
|
|
|
1212
990
|
const status = raw.status === "ready" || raw.status === "unavailable" ? raw.status : "loading";
|
|
1213
991
|
this.publish({
|
|
1214
992
|
sidebarEnabled: value.sidebarEnabled !== false,
|
|
1215
|
-
channel: isReleaseChannel(value.channel) ? value.channel : "latest",
|
|
1216
993
|
cacheTtlMinutes: isCacheTtlMinutes(value.cacheTtlMinutes) ? value.cacheTtlMinutes : 360,
|
|
1217
994
|
writable: raw.writable === true,
|
|
1218
995
|
status
|
|
@@ -1235,7 +1012,6 @@ window.__ModuleLoader__.load({
|
|
|
1235
1012
|
const value = raw.value !== null && typeof raw.value === "object" && !Array.isArray(raw.value) ? raw.value : {};
|
|
1236
1013
|
this.publish({
|
|
1237
1014
|
sidebarEnabled: value.sidebarEnabled !== false,
|
|
1238
|
-
channel: isReleaseChannel(value.channel) ? value.channel : "latest",
|
|
1239
1015
|
cacheTtlMinutes: isCacheTtlMinutes(value.cacheTtlMinutes) ? value.cacheTtlMinutes : 360,
|
|
1240
1016
|
writable: raw.writable === true,
|
|
1241
1017
|
status: raw.status === "ready" || raw.status === "unavailable" ? raw.status : "loading"
|
|
@@ -1245,18 +1021,6 @@ window.__ModuleLoader__.load({
|
|
|
1245
1021
|
}
|
|
1246
1022
|
});
|
|
1247
1023
|
}
|
|
1248
|
-
setChannel(channel) {
|
|
1249
|
-
const previous = this.snapshot;
|
|
1250
|
-
this.publish({
|
|
1251
|
-
...previous,
|
|
1252
|
-
channel
|
|
1253
|
-
});
|
|
1254
|
-
const scope = this.scope;
|
|
1255
|
-
if (scope === void 0 || !previous.writable) return;
|
|
1256
|
-
scope.set("channel", channel).catch(() => {
|
|
1257
|
-
this.publish(previous);
|
|
1258
|
-
});
|
|
1259
|
-
}
|
|
1260
1024
|
setCacheTtlMinutes(cacheTtlMinutes) {
|
|
1261
1025
|
if (!isCacheTtlMinutes(cacheTtlMinutes)) return;
|
|
1262
1026
|
const previous = this.snapshot;
|
|
@@ -1272,7 +1036,7 @@ window.__ModuleLoader__.load({
|
|
|
1272
1036
|
}
|
|
1273
1037
|
publish(next) {
|
|
1274
1038
|
const previous = this.snapshot;
|
|
1275
|
-
if (previous.sidebarEnabled === next.sidebarEnabled && previous.
|
|
1039
|
+
if (previous.sidebarEnabled === next.sidebarEnabled && previous.cacheTtlMinutes === next.cacheTtlMinutes && previous.writable === next.writable && previous.status === next.status) return;
|
|
1276
1040
|
this.snapshot = next;
|
|
1277
1041
|
for (const listener of this.listeners) listener();
|
|
1278
1042
|
}
|
|
@@ -1789,19 +1553,11 @@ window.__ModuleLoader__.load({
|
|
|
1789
1553
|
.dus-settings-card{border:1px solid var(--dsw-alias-border-l2);border-radius:10px;padding:12px}
|
|
1790
1554
|
.dus-settings-toggle{align-items:flex-start;cursor:pointer;display:flex;gap:10px;font-size:13px;line-height:1.4}
|
|
1791
1555
|
.dus-settings-toggle input{accent-color:var(--dsw-alias-state-business-primary);height:18px;margin:0;min-height:18px;min-width:18px;width:18px}
|
|
1792
|
-
.dus-settings-field{display:grid;font-size:13px;font-weight:600;gap:8px}
|
|
1793
|
-
.dus-channel-select{background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;color:inherit;font:inherit;min-height:40px;padding:0 10px;width:100%}
|
|
1794
|
-
.dus-channel-list{border-top:1px solid var(--dsw-alias-border-l2);margin:12px 0 0;padding-top:2px}
|
|
1795
|
-
.dus-channel-row{align-items:center;display:grid;font-size:11px;gap:8px;grid-template-columns:minmax(0,1fr) auto;line-height:1.4;padding:6px 0}
|
|
1796
|
-
.dus-channel-row[data-selected=true]{color:var(--dsw-alias-state-business-primary)}
|
|
1797
|
-
.dus-channel-row code{font-size:11px}
|
|
1798
|
-
.dus-channel-compat{color:var(--dsw-alias-label-secondary)}
|
|
1799
|
-
.dus-channel-action{background:transparent;border:1px solid var(--dsw-alias-border-l2);border-radius:7px;color:inherit;cursor:pointer;font-size:11px;justify-self:end;min-height:32px;padding:0 9px}
|
|
1800
|
-
.dus-channel-action:disabled{cursor:default;opacity:.55}
|
|
1801
|
-
.dus-channel-action:not(:disabled):hover{background:var(--dsw-alias-button-floating-hover)}
|
|
1802
|
-
.dus-channel-compat[data-compatibility=verified]{color:var(--dsw-alias-state-success-primary)}
|
|
1803
|
-
.dus-channel-compat[data-compatibility=incompatible]{color:var(--dsw-alias-state-error-primary)}
|
|
1804
1556
|
.dus-settings-hint{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.45;margin:8px 0 0}
|
|
1557
|
+
.dus-compat{color:var(--dsw-alias-label-secondary)}
|
|
1558
|
+
.dus-compat[data-compatibility=verified]{color:var(--dsw-alias-state-success-primary)}
|
|
1559
|
+
.dus-compat[data-compatibility=incompatible]{color:var(--dsw-alias-state-error-primary)}
|
|
1560
|
+
.dus-command-actions{align-items:center;margin:8px 0 0}
|
|
1805
1561
|
@media (max-width:640px),(hover:none) and (pointer:coarse){.dus-panel{border-bottom:0;border-bottom-left-radius:0;border-bottom-right-radius:0;bottom:0;left:0;right:0;max-height:min(78dvh,calc(100dvh - env(safe-area-inset-top)));padding:16px max(16px,env(safe-area-inset-right)) max(16px,env(safe-area-inset-bottom)) max(16px,env(safe-area-inset-left));position:fixed;top:auto;width:100vw}.dus-action{min-height:40px;line-height:38px}.dus-close{height:40px;min-height:40px;min-width:40px;width:40px}}
|
|
1806
1562
|
`;
|
|
1807
1563
|
//#endregion
|
|
@@ -1866,16 +1622,19 @@ window.__ModuleLoader__.load({
|
|
|
1866
1622
|
ctx.effect(() => {
|
|
1867
1623
|
const disposeStyles = installStyles();
|
|
1868
1624
|
const detach = preferences.attach(channel);
|
|
1869
|
-
let
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
if (
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1625
|
+
let usedCacheTtl = preferences.getSnapshot().cacheTtlMinutes;
|
|
1626
|
+
let storedPolicyApplied = false;
|
|
1627
|
+
const applyStoredPolicyOnce = () => {
|
|
1628
|
+
if (storedPolicyApplied) return;
|
|
1629
|
+
const snapshot = preferences.getSnapshot();
|
|
1630
|
+
if (snapshot.status === "loading") return;
|
|
1631
|
+
storedPolicyApplied = true;
|
|
1632
|
+
if (snapshot.cacheTtlMinutes === usedCacheTtl) return;
|
|
1633
|
+
usedCacheTtl = snapshot.cacheTtlMinutes;
|
|
1634
|
+
status.reload(snapshot.cacheTtlMinutes);
|
|
1876
1635
|
};
|
|
1877
|
-
|
|
1878
|
-
const unsubscribe = preferences.subscribe(
|
|
1636
|
+
applyStoredPolicyOnce();
|
|
1637
|
+
const unsubscribe = preferences.subscribe(applyStoredPolicyOnce);
|
|
1879
1638
|
status.load(preferences.getSnapshot().cacheTtlMinutes);
|
|
1880
1639
|
return () => {
|
|
1881
1640
|
stopped = true;
|