dsh-plugin-upgrader 1.0.0 → 1.0.2

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.
Files changed (53) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.en.md +52 -21
  3. package/README.md +57 -20
  4. package/lib/client.js +959 -0
  5. package/lib/host/bundle.d.ts +52 -0
  6. package/lib/host/bundle.d.ts.map +1 -0
  7. package/lib/host/bundle.js +73 -0
  8. package/lib/host/bundle.js.map +1 -0
  9. package/lib/host/http.d.ts +46 -0
  10. package/lib/host/http.d.ts.map +1 -0
  11. package/lib/host/http.js +104 -0
  12. package/lib/host/http.js.map +1 -0
  13. package/lib/host/install.d.ts +71 -0
  14. package/lib/host/install.d.ts.map +1 -0
  15. package/lib/host/install.js +161 -0
  16. package/lib/host/install.js.map +1 -0
  17. package/lib/host/manager.d.ts +151 -0
  18. package/lib/host/manager.d.ts.map +1 -0
  19. package/lib/host/manager.js +28 -0
  20. package/lib/host/manager.js.map +1 -0
  21. package/lib/host/overview.d.ts +52 -0
  22. package/lib/host/overview.d.ts.map +1 -0
  23. package/lib/host/overview.js +109 -0
  24. package/lib/host/overview.js.map +1 -0
  25. package/lib/host/registry.d.ts +116 -0
  26. package/lib/host/registry.d.ts.map +1 -0
  27. package/lib/host/registry.js +177 -0
  28. package/lib/host/registry.js.map +1 -0
  29. package/lib/host/repo.d.ts +62 -0
  30. package/lib/host/repo.d.ts.map +1 -0
  31. package/lib/host/repo.js +102 -0
  32. package/lib/host/repo.js.map +1 -0
  33. package/lib/host/version.d.ts +67 -0
  34. package/lib/host/version.d.ts.map +1 -0
  35. package/lib/host/version.js +114 -0
  36. package/lib/host/version.js.map +1 -0
  37. package/lib/index.d.ts +12 -0
  38. package/lib/index.d.ts.map +1 -0
  39. package/lib/index.js +254 -0
  40. package/lib/index.js.map +1 -0
  41. package/package.json +32 -14
  42. package/screenshots/history.png +0 -0
  43. package/screenshots/selection.png +0 -0
  44. package/screenshots.json +0 -4
  45. package/src/client.js +0 -1136
  46. package/src/index.js +0 -275
  47. package/src/lib/bundle.js +0 -64
  48. package/src/lib/http.js +0 -93
  49. package/src/lib/install.js +0 -163
  50. package/src/lib/overview.js +0 -108
  51. package/src/lib/registry.js +0 -188
  52. package/src/lib/repo.js +0 -91
  53. package/src/lib/version.js +0 -107
package/lib/client.js ADDED
@@ -0,0 +1,959 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "dsh-plugin-upgrader",
3
+ factory(require) {
4
+ var module = { exports: {} }
5
+ var exports = module.exports
6
+ "use strict";
7
+ var __defProp = Object.defineProperty;
8
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
9
+ var __getOwnPropNames = Object.getOwnPropertyNames;
10
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
11
+ var __export = (target, all) => {
12
+ for (var name2 in all)
13
+ __defProp(target, name2, { get: all[name2], enumerable: true });
14
+ };
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") {
17
+ for (let key of __getOwnPropNames(from))
18
+ if (!__hasOwnProp.call(to, key) && key !== except)
19
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
+ }
21
+ return to;
22
+ };
23
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
24
+
25
+ // src/client/index.ts
26
+ var index_exports = {};
27
+ __export(index_exports, {
28
+ createPlugin: () => createPlugin
29
+ });
30
+ module.exports = __toCommonJS(index_exports);
31
+
32
+ // src/client/env.ts
33
+ var installed;
34
+ function installReact(react) {
35
+ installed = react;
36
+ }
37
+ function getReact() {
38
+ if (installed === void 0) throw new Error("plugin-upgrader: React was not installed");
39
+ return installed;
40
+ }
41
+ function getH() {
42
+ return getReact().createElement;
43
+ }
44
+
45
+ // src/client/copy.ts
46
+ var ZH = {
47
+ tab: "\u7248\u672C",
48
+ title: "\u63D2\u4EF6\u7248\u672C\u7BA1\u7406",
49
+ subtitle: "\u4E00\u952E\u5347\u7EA7\uFF0C\u6216\u5207\u6362\u5230\u4EFB\u610F\u5386\u53F2\u7248\u672C\u3002",
50
+ refresh: "\u68C0\u67E5\u66F4\u65B0",
51
+ refreshing: "\u68C0\u67E5\u4E2D\u2026",
52
+ upgradeAll: "\u4E00\u952E\u5347\u7EA7",
53
+ upgradeSelected: "\u5347\u7EA7\u9009\u4E2D",
54
+ selectAll: "\u5168\u9009\u53EF\u5347\u7EA7",
55
+ clearSelection: "\u6E05\u9664\u9009\u62E9",
56
+ selected: (n) => `\u5DF2\u9009 ${n} \u9879`,
57
+ selectRow: "\u9009\u4E2D\u8FD9\u4E2A\u63D2\u4EF6",
58
+ nothingSelected: "\u5148\u52FE\u9009\u8981\u5347\u7EA7\u7684\u63D2\u4EF6",
59
+ installed: "\u5DF2\u5B89\u88C5",
60
+ upgradable: "\u53EF\u5347\u7EA7",
61
+ upToDate: "\u5168\u90E8\u5DF2\u662F\u6700\u65B0",
62
+ upgrade: "\u5347\u7EA7",
63
+ upgrading: "\u5347\u7EA7\u4E2D\u2026",
64
+ history: "\u5386\u53F2\u7248\u672C",
65
+ hideHistory: "\u6536\u8D77\u5386\u53F2",
66
+ github: "GitHub \u4E3B\u9875",
67
+ githubVersion: "\u5728 GitHub \u4E0A\u6253\u5F00\u8FD9\u4E2A\u7248\u672C\u7684\u53D1\u5E03\u9875",
68
+ githubReleases: "\u5728 GitHub \u4E0A\u6253\u5F00\u53D1\u5E03\u5217\u8868",
69
+ noRepo: "\u8FD9\u4E2A\u5305\u6CA1\u6709\u767B\u8BB0 GitHub \u4ED3\u5E93",
70
+ switchTo: "\u5207\u6362",
71
+ current: "\u5F53\u524D",
72
+ latest: "\u6700\u65B0",
73
+ older: "\u964D\u7EA7",
74
+ back: "\u8FD4\u56DE\u6700\u65B0",
75
+ empty: "\u8FD8\u6CA1\u6709\u53EF\u7BA1\u7406\u7684\u63D2\u4EF6",
76
+ emptyHint: "\u8FD9\u4E2A\u6807\u7B7E\u9875\u5217\u51FA\u968F\u914D\u7F6E\u5B89\u88C5\uFF08profile \u4F9D\u8D56\uFF09\u7684\u63D2\u4EF6\uFF1B\u5185\u7F6E\u63D2\u4EF6\u4E0D\u5728\u6B64\u5217\u3002",
77
+ search: "\u641C\u7D22\u63D2\u4EF6",
78
+ noMatch: "\u6CA1\u6709\u5339\u914D\u7684\u63D2\u4EF6",
79
+ loading: "\u6B63\u5728\u8BFB\u53D6\u5DF2\u5B89\u88C5\u63D2\u4EF6\u2026",
80
+ loadFailed: "\u8BFB\u53D6\u5931\u8D25",
81
+ retry: "\u91CD\u8BD5",
82
+ openPage: "\u5728\u5DF2\u5B89\u88C5\u4E2D\u67E5\u770B",
83
+ noHistory: "\u6CA1\u6709\u53EF\u7528\u7684\u7248\u672C\u8BB0\u5F55",
84
+ noHistoryWhy: "\u8FD9\u4E2A\u5305\u4E0D\u662F\u4ECE npm \u6CE8\u518C\u8868\u5B89\u88C5\u7684\uFF0C\u6CA1\u6709\u53D1\u5E03\u5386\u53F2\u3002",
85
+ versions: (n) => `${n} \u4E2A\u7248\u672C`,
86
+ showingNewest: (n) => `\u5217\u51FA\u6700\u65B0 ${n} \u4E2A`,
87
+ progress: (done, total) => `\u6B63\u5728\u5904\u7406 ${done}/${total}`,
88
+ cancel: "\u53D6\u6D88",
89
+ cancelling: "\u6B63\u5728\u53D6\u6D88\u2026",
90
+ restart: "\u9700\u8981\u91CD\u542F DSH \u624D\u4F1A\u751F\u6548\u3002",
91
+ restartHint: "\u5DF2\u4FDD\u5B58\u7684\u6539\u52A8\u5728\u4E0B\u6B21\u542F\u52A8\u65F6\u751F\u6548\u3002",
92
+ done: "\u5B8C\u6210",
93
+ failed: "\u5931\u8D25",
94
+ failedSome: (n) => `${n} \u4E2A\u5931\u8D25`,
95
+ jobDone: "\u6279\u91CF\u5347\u7EA7\u5B8C\u6210",
96
+ jobCancelled: "\u5DF2\u53D6\u6D88",
97
+ busyAdopted: "\u5DF2\u6709\u4E00\u6279\u5347\u7EA7\u6B63\u5728\u8FDB\u884C\uFF0C\u5DF2\u63A5\u7740\u663E\u793A\u5B83\u7684\u8FDB\u5EA6\u3002",
98
+ log: "\u8FD0\u884C\u65E5\u5FD7",
99
+ showLog: "\u67E5\u770B\u65E5\u5FD7",
100
+ hideLog: "\u6536\u8D77\u65E5\u5FD7",
101
+ buildBlocked: "\u8FD9\u4E2A\u5305\u6709\u672A\u6279\u51C6\u7684\u6784\u5EFA\u811A\u672C",
102
+ approve: "\u6279\u51C6\u5E76\u91CD\u542F",
103
+ registry: "\u6CE8\u518C\u8868",
104
+ generated: "\u6570\u636E\u66F4\u65B0\u4E8E",
105
+ sourcePath: "\u672C\u5730\u8DEF\u5F84\u5B89\u88C5\uFF0C\u65E0\u6CD5\u68C0\u67E5\u7248\u672C",
106
+ sourceGit: "Git \u5B89\u88C5\uFF0C\u65E0\u6CD5\u68C0\u67E5\u7248\u672C",
107
+ unknownVersion: "\u7248\u672C\u672A\u77E5",
108
+ dialogHint: "\u4E5F\u53EF\u4EE5\u9010\u4E2A\u6253\u5F00\u5B89\u88C5\u5BF9\u8BDD\u6846\u5B8C\u6210\u540C\u6837\u7684\u5347\u7EA7\u3002",
109
+ danger: "\u964D\u7EA7\u4F1A\u56DE\u5230\u65E7\u7248\u672C\uFF0C\u65E7\u7248\u672C\u53EF\u80FD\u542B\u6709\u5DF2\u4FEE\u590D\u7684\u95EE\u9898\u3002"
110
+ };
111
+ var EN = {
112
+ tab: "Versions",
113
+ title: "Plugin versions",
114
+ subtitle: "Upgrade everything at once, or switch to any published version.",
115
+ refresh: "Check for updates",
116
+ refreshing: "Checking\u2026",
117
+ upgradeAll: "Upgrade all",
118
+ upgradeSelected: "Upgrade selected",
119
+ selectAll: "Select all upgradable",
120
+ clearSelection: "Clear selection",
121
+ selected: (n) => `${n} selected`,
122
+ selectRow: "Select this plugin",
123
+ nothingSelected: "Pick the plugins you want to upgrade first.",
124
+ installed: "installed",
125
+ upgradable: "upgradable",
126
+ upToDate: "Everything is up to date",
127
+ upgrade: "Upgrade",
128
+ upgrading: "Upgrading\u2026",
129
+ history: "Version history",
130
+ hideHistory: "Hide history",
131
+ github: "GitHub page",
132
+ githubVersion: "Open this version on GitHub",
133
+ githubReleases: "Open the releases list on GitHub",
134
+ noRepo: "This package lists no GitHub repository",
135
+ switchTo: "Switch",
136
+ current: "installed",
137
+ latest: "latest",
138
+ older: "downgrade",
139
+ back: "Back to latest",
140
+ empty: "No manageable plugins yet.",
141
+ emptyHint: "This tab lists plugins installed as profile dependencies; built-in ones are not listed.",
142
+ search: "Search plugins",
143
+ noMatch: "No plugin matches that search.",
144
+ loading: "Reading installed plugins\u2026",
145
+ loadFailed: "Could not read the plugin list",
146
+ retry: "Retry",
147
+ openPage: "Open in Installed",
148
+ noHistory: "No published versions found",
149
+ noHistoryWhy: "This package was not installed from an npm registry, so it has no published history.",
150
+ versions: (n) => `${n} versions`,
151
+ showingNewest: (n) => `showing newest ${n}`,
152
+ progress: (done, total) => `Working on ${done}/${total}`,
153
+ cancel: "Cancel",
154
+ cancelling: "Cancelling\u2026",
155
+ restart: "Restart DSH to finish.",
156
+ restartHint: "Saved changes take effect on the next start.",
157
+ done: "done",
158
+ failed: "failed",
159
+ failedSome: (n) => `${n} failed`,
160
+ jobDone: "Batch upgrade finished",
161
+ jobCancelled: "Cancelled",
162
+ busyAdopted: "A batch is already running; showing its progress instead.",
163
+ log: "Run log",
164
+ showLog: "Show log",
165
+ hideLog: "Hide log",
166
+ buildBlocked: "This package has unapproved build scripts",
167
+ approve: "Approve and retry",
168
+ registry: "Registry",
169
+ generated: "Updated",
170
+ sourcePath: "Installed from a local path, so no versions are published",
171
+ sourceGit: "Installed from git, so no versions are published",
172
+ unknownVersion: "version unknown",
173
+ dialogHint: "You can also upgrade one at a time through the install dialog.",
174
+ danger: "A downgrade returns to an older build that may contain fixed bugs."
175
+ };
176
+
177
+ // src/client/format.ts
178
+ function makeT(bound, active) {
179
+ return (key, ...args) => {
180
+ let text;
181
+ if (bound !== void 0) {
182
+ try {
183
+ const value = bound(key, ...args);
184
+ if (typeof value === "string" && value !== key) text = value;
185
+ } catch {
186
+ }
187
+ }
188
+ if (text === void 0) {
189
+ const dict = active().toLowerCase().startsWith("zh") ? ZH : EN;
190
+ const fallback = dict[key] ?? EN[key] ?? key;
191
+ text = typeof fallback === "function" ? fallback(...args) : String(fallback);
192
+ }
193
+ return text;
194
+ };
195
+ }
196
+ function formatDate(iso) {
197
+ if (typeof iso !== "string") return "";
198
+ const date = new Date(iso);
199
+ if (Number.isNaN(date.getTime())) return "";
200
+ return date.toLocaleDateString(void 0, { year: "numeric", month: "2-digit", day: "2-digit" });
201
+ }
202
+ function openExternal(url) {
203
+ if (typeof url !== "string" || !/^https?:\/\//i.test(url)) return;
204
+ if (typeof window !== "undefined" && typeof window.open === "function") {
205
+ window.open(url, "_blank", "noopener,noreferrer");
206
+ }
207
+ }
208
+ function resolveVersionLink(row, version) {
209
+ const raw = typeof version.href === "string" ? version.href : typeof version.url === "string" && typeof row.repoUrl === "string" && version.url.startsWith("/") ? `${row.repoUrl}${version.url}` : version.url;
210
+ return typeof raw === "string" && /^https?:\/\//i.test(raw) ? raw : void 0;
211
+ }
212
+ async function readJson(response) {
213
+ let body;
214
+ try {
215
+ body = await response.json();
216
+ } catch {
217
+ body = void 0;
218
+ }
219
+ if (!response.ok) {
220
+ const record = body !== null && typeof body === "object" ? body : void 0;
221
+ const message = typeof record?.error === "string" ? record.error : `HTTP ${response.status}`;
222
+ const failure = new Error(message);
223
+ failure.status = response.status;
224
+ failure.body = record;
225
+ throw failure;
226
+ }
227
+ return body;
228
+ }
229
+ function isRequestFailure(value) {
230
+ return value instanceof Error && typeof value.status === "number";
231
+ }
232
+
233
+ // src/client/api.ts
234
+ var API = "/api/plugin-upgrader";
235
+ var POLL_MS = 800;
236
+ function useOverview(localeId) {
237
+ const React = getReact();
238
+ const [state, setState] = React.useState(
239
+ { data: void 0, error: void 0, loading: true }
240
+ );
241
+ const [tick, setTick] = React.useState(0);
242
+ const [force, setForce] = React.useState(false);
243
+ React.useEffect(() => {
244
+ let cancelled = false;
245
+ setState((current) => ({ ...current, loading: true, error: void 0 }));
246
+ const query = new URLSearchParams({ refresh: force ? "1" : "0", locale: localeId });
247
+ fetch(`${API}/overview?${query.toString()}`, { credentials: "same-origin" }).then((response) => readJson(response)).then((data) => {
248
+ if (cancelled) return;
249
+ setState({ data, error: void 0, loading: false });
250
+ }).catch((error) => {
251
+ if (cancelled) return;
252
+ setState((current) => ({
253
+ data: current.data,
254
+ error: error instanceof Error ? error.message : String(error),
255
+ loading: false
256
+ }));
257
+ });
258
+ return () => {
259
+ cancelled = true;
260
+ };
261
+ }, [localeId, tick, force]);
262
+ const reload = React.useCallback((shouldForce = false) => {
263
+ setForce(shouldForce);
264
+ setTick((value) => value + 1);
265
+ }, []);
266
+ return { ...state, reload };
267
+ }
268
+ async function startJob(steps) {
269
+ const response = await fetch(`${API}/job`, {
270
+ method: "POST",
271
+ credentials: "same-origin",
272
+ headers: { "content-type": "application/json" },
273
+ body: JSON.stringify({ steps })
274
+ });
275
+ const started = await readJson(response);
276
+ return started.id;
277
+ }
278
+ async function watchJob(id, options) {
279
+ for (; ; ) {
280
+ if (!options.isAlive()) return void 0;
281
+ const response = await fetch(`${API}/job?id=${encodeURIComponent(id)}`, { credentials: "same-origin" });
282
+ const snapshot = await readJson(response).catch(() => void 0);
283
+ if (snapshot === void 0) return void 0;
284
+ if (options.isAlive()) options.onSnapshot(snapshot);
285
+ if (snapshot.status !== "running" && snapshot.status !== "queued") return snapshot;
286
+ await new Promise((resolve) => setTimeout(resolve, POLL_MS));
287
+ }
288
+ }
289
+ async function submitJob(steps, watch) {
290
+ try {
291
+ const id = await startJob(steps);
292
+ return { kind: "started", id, settled: await watch(id) };
293
+ } catch (error) {
294
+ if (isRequestFailure(error) && error.status === 409 && typeof error.body?.id === "string") {
295
+ return { kind: "adopted", settled: await watch(error.body.id) };
296
+ }
297
+ return { kind: "failed", error };
298
+ }
299
+ }
300
+ async function cancelJob() {
301
+ try {
302
+ await fetch(`${API}/cancel`, { method: "POST", credentials: "same-origin" });
303
+ } catch {
304
+ }
305
+ }
306
+
307
+ // src/client/hooks.ts
308
+ function useLocaleRevision(locale) {
309
+ const React = getReact();
310
+ const [revision, setRevision] = React.useState(0);
311
+ React.useEffect(() => {
312
+ const subscribe = locale?.subscribe;
313
+ if (locale === void 0 || locale === null || typeof subscribe !== "function") return void 0;
314
+ return subscribe.call(locale, () => setRevision((value) => value + 1));
315
+ }, [locale]);
316
+ return revision;
317
+ }
318
+ function activeLocaleId(locale) {
319
+ try {
320
+ const read = locale?.getLocale;
321
+ if (typeof read !== "function") return "en";
322
+ return String(read.call(locale)?.active ?? "en");
323
+ } catch {
324
+ return "en";
325
+ }
326
+ }
327
+ function useAlive() {
328
+ const React = getReact();
329
+ const alive = React.useRef(true);
330
+ React.useEffect(
331
+ () => () => {
332
+ alive.current = false;
333
+ },
334
+ []
335
+ );
336
+ return alive;
337
+ }
338
+
339
+ // src/client/styles.ts
340
+ var STYLE_ID = "dsh-plugin-upgrader-style";
341
+ var CSS = `
342
+ .dsh-pu { display: flex; flex-direction: column; gap: 14px; padding: 4px 2px 24px; color: var(--dsw-alias-label-primary); }
343
+ .dsh-pu-head { display: flex; align-items: flex-start; gap: 16px; flex-wrap: wrap; }
344
+ .dsh-pu-head-main { display: flex; flex-direction: column; gap: 4px; min-width: 220px; flex: 1; }
345
+ .dsh-pu-title { font-size: 15px; font-weight: 600; letter-spacing: .01em; }
346
+ .dsh-pu-sub { font-size: 12px; color: var(--dsw-alias-label-secondary); }
347
+ .dsh-pu-stats { display: flex; gap: 8px; font-size: 12px; color: var(--dsw-alias-label-secondary); align-items: center; flex-wrap: wrap; }
348
+ .dsh-pu-stat { display: inline-flex; align-items: center; gap: 6px; padding: 3px 9px; border-radius: 999px; background: var(--dsw-alias-bg-layer-2); border: 1px solid var(--dsw-alias-border-l1); }
349
+ .dsh-pu-stat b { color: var(--dsw-alias-label-primary); font-weight: 600; }
350
+ .dsh-pu-stat.is-warn b { color: var(--dsw-alias-brand-primary); }
351
+ .dsh-pu-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
352
+ .dsh-pu-btn { display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 12px; border-radius: 8px; font-size: 12px; font-weight: 500; cursor: pointer; border: 1px solid var(--dsw-alias-border-l1); background: var(--dsw-alias-bg-layer-1); color: var(--dsw-alias-label-primary); transition: background .15s ease, border-color .15s ease, opacity .15s ease; font-family: inherit; }
353
+ .dsh-pu-btn:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); border-color: var(--dsw-alias-border-l2); }
354
+ .dsh-pu-btn:disabled { opacity: .5; cursor: default; }
355
+ .dsh-pu-btn.is-primary { background: var(--dsw-alias-button-primary-fill); border-color: transparent; color: #fff; }
356
+ .dsh-pu-btn.is-primary:hover:not(:disabled) { background: var(--dsw-alias-button-primary-hover); }
357
+ .dsh-pu-btn.is-ghost { background: transparent; border-color: transparent; color: var(--dsw-alias-label-secondary); padding: 0 8px; }
358
+ .dsh-pu-btn.is-ghost:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-primary); }
359
+ .dsh-pu-btn.is-tiny { height: 24px; padding: 0 9px; font-size: 11px; border-radius: 6px; }
360
+ .dsh-pu-search { display: flex; align-items: center; gap: 8px; }
361
+ .dsh-pu-search input { height: 30px; width: 220px; border-radius: 8px; border: 1px solid var(--dsw-alias-border-l1); background: var(--dsw-alias-bg-layer-1); color: var(--dsw-alias-label-primary); padding: 0 10px; font-size: 12px; font-family: inherit; outline: none; }
362
+ .dsh-pu-search input:focus { border-color: var(--dsw-alias-brand-primary); box-shadow: 0 0 0 2px color-mix(in srgb, var(--dsw-alias-brand-primary) 22%, transparent); }
363
+ .dsh-pu-list { display: flex; flex-direction: column; gap: 8px; }
364
+ .dsh-pu-row { border: 1px solid var(--dsw-alias-border-l1); border-radius: 10px; background: var(--dsw-alias-bg-layer-1); overflow: hidden; transition: border-color .15s ease; }
365
+ .dsh-pu-row:hover { border-color: var(--dsw-alias-border-l2); }
366
+ .dsh-pu-row.is-open { border-color: var(--dsw-alias-border-l2); }
367
+ .dsh-pu-row.is-picked { border-color: var(--dsw-alias-brand-primary); box-shadow: 0 0 0 1px color-mix(in srgb, var(--dsw-alias-brand-primary) 32%, transparent); }
368
+ .dsh-pu-row-main { display: flex; align-items: center; gap: 12px; padding: 10px 12px; }
369
+ .dsh-pu-check { display: flex; align-items: center; justify-content: center; width: 16px; height: 16px; flex: none; cursor: pointer; border-radius: 4px; outline: none; }
370
+ .dsh-pu-check .dsh-pu-tick { width: 14px; height: 14px; border-radius: 4px; border: 1px solid var(--dsw-alias-border-l3); background: var(--dsw-alias-bg-layer-1); box-sizing: border-box; position: relative; transition: background .12s ease, border-color .12s ease; }
371
+ .dsh-pu-check.is-on .dsh-pu-tick { background: var(--dsw-alias-brand-primary); border-color: var(--dsw-alias-brand-primary); }
372
+ .dsh-pu-check.is-on .dsh-pu-tick::after { content: ''; position: absolute; left: 4px; top: 1px; width: 4px; height: 7px; border: solid var(--dsw-alias-label-primary-inverted, #fff); border-width: 0 2px 2px 0; transform: rotate(45deg); }
373
+ .dsh-pu-check.is-off { cursor: default; }
374
+ .dsh-pu-check.is-off .dsh-pu-tick { opacity: .45; }
375
+ .dsh-pu-check:not(.is-off):hover .dsh-pu-tick { border-color: var(--dsw-alias-brand-primary); }
376
+ .dsh-pu-check:not(.is-off):focus-visible .dsh-pu-tick { box-shadow: 0 0 0 2px color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, transparent); }
377
+ .dsh-pu-selbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 12px; color: var(--dsw-alias-label-secondary); padding: 7px 10px; border-radius: 8px; border: 1px dashed var(--dsw-alias-border-l1); background: var(--dsw-alias-bg-layer-2); }
378
+ .dsh-pu-selbar.is-active { border-style: solid; border-color: var(--dsw-alias-brand-primary); color: var(--dsw-alias-label-primary); }
379
+ .dsh-pu-selbar .dsh-pu-grow { flex: 1; min-width: 0; }
380
+ .dsh-pu-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; background: var(--dsw-alias-label-dimmed); }
381
+ .dsh-pu-dot.is-new { background: var(--dsw-alias-brand-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--dsw-alias-brand-primary) 22%, transparent); }
382
+ .dsh-pu-dot.is-err { background: var(--dsw-alias-state-error-primary); }
383
+ .dsh-pu-id { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
384
+ .dsh-pu-name { font-size: 13px; font-weight: 600; display: flex; align-items: center; gap: 8px; min-width: 0; }
385
+ .dsh-pu-name span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
386
+ .dsh-pu-desc { font-size: 11px; color: var(--dsw-alias-label-tertiary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
387
+ .dsh-pu-mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 11px; color: var(--dsw-alias-label-secondary); }
388
+ .dsh-pu-tag { font-size: 10px; line-height: 1; padding: 3px 6px; border-radius: 999px; border: 1px solid var(--dsw-alias-border-l1); color: var(--dsw-alias-label-secondary); font-weight: 500; white-space: nowrap; }
389
+ .dsh-pu-tag.is-new { border-color: var(--dsw-alias-brand-primary); color: var(--dsw-alias-brand-primary); background: color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent); }
390
+ .dsh-pu-tag.is-err { border-color: var(--dsw-alias-state-error-primary); color: var(--dsw-alias-state-error-primary); }
391
+ .dsh-pu-vers { display: flex; align-items: center; gap: 6px; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 11px; color: var(--dsw-alias-label-secondary); flex: none; }
392
+ .dsh-pu-vers b { color: var(--dsw-alias-label-primary); font-weight: 600; }
393
+ .dsh-pu-vers i { font-style: normal; color: var(--dsw-alias-label-tertiary); }
394
+ .dsh-pu-row-actions { display: flex; align-items: center; gap: 6px; flex: none; }
395
+ .dsh-pu-hist { border-top: 1px dashed var(--dsw-alias-border-l1); background: var(--dsw-alias-bg-layer-2); padding: 8px 12px 10px; }
396
+ .dsh-pu-hist-head { display: flex; align-items: center; gap: 8px; justify-content: flex-start; font-size: 11px; color: var(--dsw-alias-label-secondary); padding-bottom: 6px; }
397
+ .dsh-pu-hist-head .dsh-pu-mono { margin-left: auto; }
398
+ .dsh-pu-dim { color: var(--dsw-alias-label-tertiary); }
399
+ .dsh-pu-hist-list { display: flex; flex-direction: column; max-height: 260px; overflow: auto; }
400
+ .dsh-pu-hist-row { display: flex; align-items: center; gap: 10px; padding: 5px 6px; border-radius: 6px; font-size: 11px; }
401
+ .dsh-pu-hist-row:hover { background: var(--dsw-alias-interactive-bg-hover); }
402
+ .dsh-pu-hist-row.is-current { background: var(--dsw-alias-interactive-bg-hover); box-shadow: inset 2px 0 0 var(--dsw-alias-brand-primary); }
403
+ .dsh-pu-hist-row .dsh-pu-mono { flex: none; min-width: 108px; color: var(--dsw-alias-label-primary); }
404
+ .dsh-pu-link { display: inline-flex; align-items: center; gap: 4px; border: 0; padding: 0; margin: 0; background: none; cursor: pointer; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 11px; color: var(--dsw-alias-link); text-decoration: underline; text-decoration-color: color-mix(in srgb, var(--dsw-alias-link) 40%, transparent); text-underline-offset: 2px; }
405
+ .dsh-pu-link:hover { color: var(--dsw-alias-brand-primary); text-decoration-color: currentColor; }
406
+ .dsh-pu-link:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary); outline-offset: 2px; border-radius: 3px; }
407
+ .dsh-pu-hist-row time { color: var(--dsw-alias-label-tertiary); flex: none; min-width: 84px; }
408
+ .dsh-pu-hist-row .dsh-pu-note { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-state-warn-primary); }
409
+ .dsh-pu-empty { border: 1px dashed var(--dsw-alias-border-l1); border-radius: 10px; padding: 28px 16px; text-align: center; font-size: 12px; color: var(--dsw-alias-label-secondary); display: flex; flex-direction: column; gap: 6px; }
410
+ .dsh-pu-empty small { color: var(--dsw-alias-label-tertiary); font-size: 11px; }
411
+ .dsh-pu-job { border: 1px solid var(--dsw-alias-border-l1); border-radius: 10px; background: var(--dsw-alias-bg-layer-2); padding: 10px 12px; display: flex; flex-direction: column; gap: 8px; }
412
+ .dsh-pu-job-head { display: flex; align-items: center; gap: 10px; font-size: 12px; }
413
+ .dsh-pu-job-head .dsh-pu-grow { flex: 1; min-width: 0; }
414
+ .dsh-pu-bar { height: 4px; border-radius: 999px; background: var(--dsw-alias-bg-layer-3); overflow: hidden; }
415
+ .dsh-pu-bar div { height: 100%; border-radius: 999px; background: var(--dsw-alias-brand-primary); transition: width .25s ease; }
416
+ .dsh-pu-log { max-height: 160px; overflow: auto; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 10.5px; line-height: 1.55; color: var(--dsw-alias-label-secondary); white-space: pre-wrap; word-break: break-word; border-top: 1px dashed var(--dsw-alias-border-l1); padding-top: 8px; margin: 0; }
417
+ .dsh-pu-banner { display: flex; align-items: center; gap: 8px; font-size: 11.5px; padding: 7px 10px; border-radius: 8px; border: 1px solid var(--dsw-alias-border-l1); background: var(--dsw-alias-bg-layer-2); color: var(--dsw-alias-label-secondary); }
418
+ .dsh-pu-banner.is-warn { border-color: var(--dsw-alias-state-warn-primary); color: var(--dsw-alias-state-warn-primary); }
419
+ .dsh-pu-banner.is-err { border-color: var(--dsw-alias-state-error-primary); color: var(--dsw-alias-state-error-primary); }
420
+ .dsh-pu-foot { display: flex; align-items: center; gap: 10px; font-size: 11px; color: var(--dsw-alias-label-tertiary); flex-wrap: wrap; }
421
+ .dsh-pu-spin { width: 12px; height: 12px; border-radius: 50%; border: 2px solid var(--dsw-alias-border-l2); border-top-color: var(--dsw-alias-brand-primary); animation: dsh-pu-spin .8s linear infinite; flex: none; }
422
+ @keyframes dsh-pu-spin { to { transform: rotate(360deg); } }
423
+ @media (prefers-reduced-motion: reduce) { .dsh-pu-spin { animation-duration: 2.4s; } }
424
+ `;
425
+
426
+ // src/client/components/JobPanel.ts
427
+ function JobPanel(props) {
428
+ const h = getH();
429
+ const React = getReact();
430
+ const { job, t, onCancel, cancelling } = props;
431
+ const [showLog, setShowLog] = React.useState(true);
432
+ const total = job.total ?? 0;
433
+ const done = job.done ?? 0;
434
+ const percent = total === 0 ? 0 : Math.round(done / total * 100);
435
+ const running = job.status === "running" || job.status === "queued";
436
+ const failed = job.failed ?? 0;
437
+ return h(
438
+ "div",
439
+ { className: "dsh-pu-job" },
440
+ h(
441
+ "div",
442
+ { className: "dsh-pu-job-head" },
443
+ running ? h("span", { className: "dsh-pu-spin" }) : null,
444
+ h(
445
+ "span",
446
+ { className: "dsh-pu-grow" },
447
+ running ? t("progress", done, total) : job.status === "cancelled" ? t("jobCancelled") : t("jobDone"),
448
+ job.current === void 0 ? null : h("span", { className: "dsh-pu-mono" }, ` ${job.current}`)
449
+ ),
450
+ failed > 0 ? h("span", { className: "dsh-pu-tag is-err" }, t("failedSome", failed)) : null,
451
+ running ? h(
452
+ "button",
453
+ { type: "button", className: "dsh-pu-btn is-tiny", disabled: cancelling, onClick: onCancel },
454
+ cancelling ? t("cancelling") : t("cancel")
455
+ ) : null,
456
+ h(
457
+ "button",
458
+ { type: "button", className: "dsh-pu-btn is-ghost is-tiny", onClick: () => setShowLog((value) => !value) },
459
+ showLog ? t("hideLog") : t("showLog")
460
+ )
461
+ ),
462
+ h("div", { className: "dsh-pu-bar" }, h("div", { style: { width: `${percent}%` } })),
463
+ showLog && Array.isArray(job.log) ? h("pre", { className: "dsh-pu-log" }, job.log.join("\n")) : null
464
+ );
465
+ }
466
+
467
+ // src/client/components/VersionLine.ts
468
+ function VersionLine(props) {
469
+ const h = getH();
470
+ const { row, version, t, busy, onSwitch } = props;
471
+ const note = version.deprecated !== void 0 ? `\u26A0 ${version.deprecated}` : "";
472
+ const link = resolveVersionLink(row, version);
473
+ const linkLabel = link !== void 0 && /\/releases\/tag\//.test(link) ? t("githubVersion") : t("githubReleases");
474
+ const title = link === void 0 ? void 0 : `${linkLabel} \u2192${link}`;
475
+ return h(
476
+ "div",
477
+ { className: `dsh-pu-hist-row${version.current ? " is-current" : ""}` },
478
+ link !== void 0 ? h(
479
+ "button",
480
+ {
481
+ type: "button",
482
+ className: "dsh-pu-mono dsh-pu-link",
483
+ title,
484
+ onClick: () => openExternal(link)
485
+ },
486
+ version.version
487
+ ) : h("span", { className: "dsh-pu-mono" }, version.version),
488
+ h("time", null, formatDate(version.time)),
489
+ version.latest ? h("span", { className: "dsh-pu-tag is-new" }, t("latest")) : null,
490
+ version.current ? h("span", { className: "dsh-pu-tag" }, t("current")) : null,
491
+ h("span", { className: "dsh-pu-note" }, note),
492
+ link === void 0 ? null : h(
493
+ "button",
494
+ {
495
+ type: "button",
496
+ className: "dsh-pu-btn is-ghost is-tiny",
497
+ title,
498
+ "aria-label": linkLabel === t("githubVersion") ? `${linkLabel}: ${version.version}` : linkLabel,
499
+ onClick: () => openExternal(link)
500
+ },
501
+ "\u2192"
502
+ ),
503
+ h(
504
+ "button",
505
+ {
506
+ type: "button",
507
+ className: "dsh-pu-btn is-tiny",
508
+ disabled: busy || version.current === true,
509
+ onClick: () => onSwitch(row, version.version)
510
+ },
511
+ version.version === row.latest ? t("back") : t("switchTo")
512
+ )
513
+ );
514
+ }
515
+
516
+ // src/client/components/PluginRow.ts
517
+ function PluginRow(props) {
518
+ const h = getH();
519
+ const React = getReact();
520
+ const { row, t, busy, picked, onToggle, onUpgrade, onSwitch, onApprove, onOpenPage } = props;
521
+ const [open, setOpen] = React.useState(false);
522
+ const versions = Array.isArray(row.versions) ? row.versions : [];
523
+ const hasHistory = versions.length > 0;
524
+ const selectable = row.upgradable === true;
525
+ const status = row.error !== void 0 && row.error !== null ? "err" : row.upgradable ? "new" : "idle";
526
+ const versionNote = row.versionError !== void 0 ? row.versionError === "not a profile-managed package" ? t("sourcePath") : row.versionError : void 0;
527
+ const pendingBuilds = Array.isArray(row.pendingBuilds) ? row.pendingBuilds : [];
528
+ return h(
529
+ "div",
530
+ { className: `dsh-pu-row${open ? " is-open" : ""}${picked ? " is-picked" : ""}` },
531
+ h(
532
+ "div",
533
+ { className: "dsh-pu-row-main" },
534
+ h(
535
+ "span",
536
+ {
537
+ className: `dsh-pu-check${picked ? " is-on" : ""}${busy || !selectable ? " is-off" : ""}`,
538
+ role: "checkbox",
539
+ "aria-checked": picked === true ? "true" : "false",
540
+ "aria-disabled": busy || !selectable ? "true" : void 0,
541
+ "aria-label": `${t("selectRow")}: ${row.name}`,
542
+ tabIndex: busy || !selectable ? void 0 : 0,
543
+ title: selectable ? t("selectRow") : void 0,
544
+ onClick: busy || !selectable ? void 0 : () => onToggle(row.name),
545
+ onKeyDown: (event) => {
546
+ if (busy || !selectable) return;
547
+ if (event.key !== " " && event.key !== "Enter") return;
548
+ event.preventDefault();
549
+ onToggle(row.name);
550
+ }
551
+ },
552
+ h("span", { className: "dsh-pu-tick", "aria-hidden": "true" })
553
+ ),
554
+ h("span", { className: `dsh-pu-dot${status === "idle" ? "" : status === "new" ? " is-new" : " is-err"}` }),
555
+ h(
556
+ "div",
557
+ { className: "dsh-pu-id" },
558
+ h(
559
+ "div",
560
+ { className: "dsh-pu-name" },
561
+ h("span", { title: row.name }, row.title === row.name ? row.name : `${row.title} \xB7 ${row.name}`),
562
+ row.enabled === false ? h("span", { className: "dsh-pu-tag" }, "off") : null,
563
+ row.upgradable ? h("span", { className: "dsh-pu-tag is-new" }, t("upgradable")) : null
564
+ ),
565
+ h(
566
+ "div",
567
+ { className: "dsh-pu-desc" },
568
+ row.description !== void 0 && row.description !== "" ? row.description : versionNote ?? row.name
569
+ )
570
+ ),
571
+ h(
572
+ "div",
573
+ { className: "dsh-pu-vers" },
574
+ h("b", null, row.version ?? t("unknownVersion")),
575
+ row.upgradable ? h("i", null, "\u2192") : null,
576
+ row.upgradable ? h("b", null, row.latest) : null
577
+ ),
578
+ h(
579
+ "div",
580
+ { className: "dsh-pu-row-actions" },
581
+ row.upgradable ? h(
582
+ "button",
583
+ {
584
+ type: "button",
585
+ className: "dsh-pu-btn is-primary is-tiny",
586
+ disabled: busy,
587
+ onClick: () => onUpgrade(row)
588
+ },
589
+ t("upgrade")
590
+ ) : null,
591
+ typeof row.repoUrl === "string" ? h(
592
+ "button",
593
+ {
594
+ type: "button",
595
+ className: "dsh-pu-btn is-tiny",
596
+ title: `${t("github")} \u2192${row.repoUrl}`,
597
+ onClick: () => openExternal(row.repoUrl)
598
+ },
599
+ "GH"
600
+ ) : null,
601
+ hasHistory ? h(
602
+ "button",
603
+ {
604
+ type: "button",
605
+ className: "dsh-pu-btn is-tiny",
606
+ onClick: () => setOpen((value) => !value),
607
+ "aria-expanded": open
608
+ },
609
+ open ? t("hideHistory") : `${t("history")} (${versions.length})`
610
+ ) : null,
611
+ typeof onOpenPage === "function" ? h(
612
+ "button",
613
+ {
614
+ type: "button",
615
+ className: "dsh-pu-btn is-ghost is-tiny",
616
+ title: t("openPage"),
617
+ onClick: () => onOpenPage(row.name)
618
+ },
619
+ "\u2192"
620
+ ) : null
621
+ )
622
+ ),
623
+ pendingBuilds.length > 0 ? h(
624
+ "div",
625
+ { className: "dsh-pu-hist" },
626
+ h(
627
+ "div",
628
+ { className: "dsh-pu-banner is-warn" },
629
+ h("span", null, t("buildBlocked")),
630
+ h(
631
+ "button",
632
+ { type: "button", className: "dsh-pu-btn is-tiny", onClick: () => onApprove(row) },
633
+ t("approve")
634
+ )
635
+ )
636
+ ) : null,
637
+ open ? h(
638
+ "div",
639
+ { className: "dsh-pu-hist" },
640
+ h(
641
+ "div",
642
+ { className: "dsh-pu-hist-head" },
643
+ h("span", null, t("versions", row.totalVersions ?? versions.length)),
644
+ typeof row.totalVersions === "number" && row.totalVersions > versions.length ? h("span", { className: "dsh-pu-dim" }, t("showingNewest", versions.length)) : null,
645
+ h("span", { className: "dsh-pu-mono" }, row.name)
646
+ ),
647
+ h(
648
+ "div",
649
+ { className: "dsh-pu-hist-list" },
650
+ versions.map((version) => h(VersionLine, { key: version.version, row, version, t, busy, onSwitch }))
651
+ )
652
+ ) : null
653
+ );
654
+ }
655
+
656
+ // src/client/components/Tab.ts
657
+ function Tab(props) {
658
+ const h = getH();
659
+ const React = getReact();
660
+ const { t, locale } = props;
661
+ const revision = useLocaleRevision(locale);
662
+ const localeId = React.useMemo(() => activeLocaleId(locale), [locale, revision]);
663
+ const { data, error, loading, reload } = useOverview(localeId);
664
+ const [query, setQuery] = React.useState("");
665
+ const [picked, setPicked] = React.useState(() => /* @__PURE__ */ new Set());
666
+ const [job, setJob] = React.useState(void 0);
667
+ const [busyName, setBusyName] = React.useState(void 0);
668
+ const [cancelling, setCancelling] = React.useState(false);
669
+ const [banner, setBanner] = React.useState(void 0);
670
+ const alive = useAlive();
671
+ const watch = React.useCallback(
672
+ async (id) => {
673
+ const settled = await watchJob(id, { isAlive: () => alive.current, onSnapshot: (snapshot) => setJob(snapshot) });
674
+ if (!alive.current) return settled;
675
+ setBusyName(void 0);
676
+ setCancelling(false);
677
+ reload(false);
678
+ const failures = Array.isArray(settled?.results) ? settled.results.filter((result) => result.ok !== true) : [];
679
+ if (failures.length > 0) {
680
+ setBanner({
681
+ tone: "err",
682
+ text: failures.map((failure) => `${failure.name}: ${failure.message ?? failure.detail ?? "error"}`).join(" \xB7 ")
683
+ });
684
+ } else if (settled?.results?.some((result) => result.application === "restart-required") === true) {
685
+ setBanner({ tone: "warn", text: t("restart") });
686
+ }
687
+ return settled;
688
+ },
689
+ [alive, reload, t]
690
+ );
691
+ const adopted = React.useRef(void 0);
692
+ React.useEffect(() => {
693
+ const live = data?.active;
694
+ if (live === void 0 || live === null) return;
695
+ if (adopted.current === live.id) return;
696
+ adopted.current = live.id;
697
+ setJob(live);
698
+ setBusyName(live.current);
699
+ void watch(live.id);
700
+ }, [data, watch]);
701
+ const run = React.useCallback(
702
+ async (steps, label) => {
703
+ if (steps.length === 0) return;
704
+ setBanner(void 0);
705
+ setBusyName(label);
706
+ const outcome = await submitJob(steps, watch);
707
+ if (!alive.current) return;
708
+ if (outcome.kind === "started") {
709
+ setJob({
710
+ id: outcome.id,
711
+ status: "queued",
712
+ total: steps.length,
713
+ done: 0,
714
+ failed: 0,
715
+ log: []
716
+ });
717
+ return;
718
+ }
719
+ if (outcome.kind === "adopted") {
720
+ setBanner({ tone: "warn", text: t("busyAdopted") });
721
+ return;
722
+ }
723
+ setBusyName(void 0);
724
+ const failure = outcome.error;
725
+ setBanner({ tone: "err", text: failure instanceof Error ? failure.message : String(failure) });
726
+ },
727
+ [alive, t, watch]
728
+ );
729
+ const rows = Array.isArray(data?.bundles) ? data.bundles : [];
730
+ const upgradable = rows.filter((row) => row.upgradable === true);
731
+ const needle = query.trim().toLowerCase();
732
+ const filtered = needle === "" ? rows : rows.filter(
733
+ (row) => row.name.toLowerCase().includes(needle) || String(row.title ?? "").toLowerCase().includes(needle)
734
+ );
735
+ const busy = busyName !== void 0 || job !== void 0 && (job.status === "running" || job.status === "queued");
736
+ const pickedCount = upgradable.reduce((count, row) => picked.has(row.name) ? count + 1 : count, 0);
737
+ const stepOf = (row, version, action, approveBuilds) => ({
738
+ name: row.name,
739
+ version,
740
+ action,
741
+ enabled: row.enabled !== false,
742
+ ...approveBuilds === void 0 ? {} : { approveBuilds }
743
+ });
744
+ const upgradeOne = (row) => {
745
+ void run([stepOf(row, row.latest ?? row.version ?? "", "upgrade")], row.name);
746
+ };
747
+ const switchOne = (row, version) => {
748
+ void run([stepOf(row, version, "switch")], row.name);
749
+ };
750
+ const approveAndRetry = (row) => {
751
+ const pendingBuilds = Array.isArray(row.pendingBuilds) ? row.pendingBuilds : [];
752
+ void run([stepOf(row, row.latest ?? row.version ?? "", "upgrade", pendingBuilds)], row.name);
753
+ };
754
+ const upgradeAll = () => {
755
+ void run(
756
+ upgradable.map((row) => stepOf(row, row.latest ?? row.version ?? "", "upgrade")),
757
+ t("upgradeAll")
758
+ );
759
+ };
760
+ const togglePick = (name2) => {
761
+ setPicked((current) => {
762
+ const next = new Set(current);
763
+ if (next.has(name2)) next.delete(name2);
764
+ else next.add(name2);
765
+ return next;
766
+ });
767
+ };
768
+ const pickAll = () => setPicked(new Set(upgradable.map((row) => row.name)));
769
+ const clearPicked = () => setPicked(/* @__PURE__ */ new Set());
770
+ const upgradePicked = () => {
771
+ void run(
772
+ upgradable.filter((row) => picked.has(row.name)).map((row) => stepOf(row, row.latest ?? row.version ?? "", "upgrade")),
773
+ t("upgradeSelected")
774
+ );
775
+ };
776
+ const cancel = () => {
777
+ setCancelling(true);
778
+ void cancelJob();
779
+ };
780
+ return h(
781
+ "div",
782
+ { className: "dsh-pu" },
783
+ h("style", { key: STYLE_ID }, CSS),
784
+ h(
785
+ "div",
786
+ { className: "dsh-pu-head" },
787
+ h(
788
+ "div",
789
+ { className: "dsh-pu-head-main" },
790
+ h("div", { className: "dsh-pu-title" }, t("title")),
791
+ h("div", { className: "dsh-pu-sub" }, t("subtitle")),
792
+ h(
793
+ "div",
794
+ { className: "dsh-pu-stats" },
795
+ h("span", { className: "dsh-pu-stat" }, t("installed"), " ", h("b", null, String(rows.length))),
796
+ h(
797
+ "span",
798
+ { className: `dsh-pu-stat${upgradable.length > 0 ? " is-warn" : ""}` },
799
+ t("upgradable"),
800
+ " ",
801
+ h("b", null, String(upgradable.length))
802
+ ),
803
+ data?.registry === void 0 ? null : h("span", { className: "dsh-pu-mono" }, `${t("registry")}: ${data.registry}`)
804
+ )
805
+ ),
806
+ h(
807
+ "div",
808
+ { className: "dsh-pu-actions" },
809
+ h(
810
+ "div",
811
+ { className: "dsh-pu-search" },
812
+ h("input", {
813
+ type: "search",
814
+ placeholder: t("search"),
815
+ value: query,
816
+ onChange: (event) => setQuery(event.target.value),
817
+ "aria-label": t("search")
818
+ })
819
+ ),
820
+ h(
821
+ "button",
822
+ { type: "button", className: "dsh-pu-btn", disabled: loading === true || busy, onClick: () => reload(true) },
823
+ loading === true ? h("span", { className: "dsh-pu-spin" }) : null,
824
+ loading === true ? t("refreshing") : t("refresh")
825
+ ),
826
+ h(
827
+ "button",
828
+ {
829
+ type: "button",
830
+ className: "dsh-pu-btn is-primary",
831
+ disabled: upgradable.length === 0 || busy,
832
+ onClick: upgradeAll
833
+ },
834
+ t("upgradeAll"),
835
+ upgradable.length > 0 ? ` (${upgradable.length})` : ""
836
+ )
837
+ )
838
+ ),
839
+ banner === void 0 ? null : h(
840
+ "div",
841
+ {
842
+ className: `dsh-pu-banner${banner.tone === "err" ? " is-err" : banner.tone === "warn" ? " is-warn" : ""}`
843
+ },
844
+ h("span", null, banner.text),
845
+ banner.tone === "warn" ? h("span", null, t("restartHint")) : null,
846
+ h(
847
+ "button",
848
+ { type: "button", className: "dsh-pu-btn is-ghost is-tiny", onClick: () => setBanner(void 0) },
849
+ "\u2715"
850
+ )
851
+ ),
852
+ job === void 0 ? null : h(JobPanel, { job, t, onCancel: cancel, cancelling }),
853
+ rows.length === 0 ? null : h(
854
+ "div",
855
+ { className: `dsh-pu-selbar${pickedCount > 0 ? " is-active" : ""}` },
856
+ h("span", { className: "dsh-pu-grow" }, pickedCount > 0 ? t("selected", pickedCount) : t("nothingSelected")),
857
+ h(
858
+ "button",
859
+ {
860
+ type: "button",
861
+ className: "dsh-pu-btn is-tiny",
862
+ disabled: busy || upgradable.length === 0,
863
+ onClick: pickAll
864
+ },
865
+ t("selectAll")
866
+ ),
867
+ pickedCount > 0 ? h(
868
+ "button",
869
+ { type: "button", className: "dsh-pu-btn is-tiny", disabled: busy, onClick: clearPicked },
870
+ t("clearSelection")
871
+ ) : null,
872
+ h(
873
+ "button",
874
+ {
875
+ type: "button",
876
+ className: "dsh-pu-btn is-tiny is-primary",
877
+ disabled: busy || pickedCount === 0,
878
+ onClick: upgradePicked
879
+ },
880
+ t("upgradeSelected"),
881
+ pickedCount > 0 ? ` (${pickedCount})` : ""
882
+ )
883
+ ),
884
+ loading === true && rows.length === 0 ? h("div", { className: "dsh-pu-empty" }, h("span", null, t("loading"))) : null,
885
+ error !== void 0 && rows.length === 0 ? h(
886
+ "div",
887
+ { className: "dsh-pu-empty" },
888
+ h("span", null, `${t("loadFailed")} \u2192${error}`),
889
+ h(
890
+ "div",
891
+ null,
892
+ h("button", { type: "button", className: "dsh-pu-btn is-tiny", onClick: () => reload(true) }, t("retry"))
893
+ )
894
+ ) : null,
895
+ rows.length === 0 && loading !== true && error === void 0 ? h("div", { className: "dsh-pu-empty" }, h("span", null, t("empty")), h("small", null, t("emptyHint"))) : null,
896
+ filtered.length === 0 && rows.length > 0 ? h("div", { className: "dsh-pu-empty" }, h("span", null, t("noMatch"))) : null,
897
+ filtered.length === 0 ? null : h(
898
+ "div",
899
+ { className: "dsh-pu-list" },
900
+ filtered.map(
901
+ (row) => h(PluginRow, {
902
+ key: row.name,
903
+ row,
904
+ t,
905
+ busy,
906
+ picked: picked.has(row.name),
907
+ onToggle: togglePick,
908
+ onUpgrade: upgradeOne,
909
+ onSwitch: switchOne,
910
+ onApprove: approveAndRetry,
911
+ onOpenPage: typeof props.openPackage === "function" ? props.openPackage : void 0
912
+ })
913
+ )
914
+ ),
915
+ rows.length > 0 && upgradable.length === 0 ? h("div", { className: "dsh-pu-banner" }, h("span", null, t("upToDate"))) : null,
916
+ h(
917
+ "div",
918
+ { className: "dsh-pu-foot" },
919
+ data?.generatedAt === void 0 ? null : h("span", null, `${t("generated")} ${new Date(data.generatedAt).toLocaleTimeString()}`),
920
+ data?.registry === void 0 ? null : h("span", { className: "dsh-pu-mono" }, data.registry)
921
+ )
922
+ );
923
+ }
924
+
925
+ // src/client/plugin.ts
926
+ var NS = "plugin-upgrader";
927
+ var name = "plugin-upgrader";
928
+ var inject = ["slots", "locale"];
929
+ function apply(ctx) {
930
+ const locale = ctx.get("locale", false);
931
+ const active = () => activeLocaleId(locale);
932
+ let bound;
933
+ if (locale !== void 0 && locale !== null) {
934
+ try {
935
+ locale.register?.(NS, { zh: ZH, en: EN });
936
+ bound = locale.bind?.(NS);
937
+ } catch (error) {
938
+ console.warn("[plugin-upgrader] locale namespace unavailable, using built-in copy", error);
939
+ }
940
+ }
941
+ const t = makeT(bound, active);
942
+ ctx.slots.inject(
943
+ "plugins.tab",
944
+ () => ctx.slots.register(
945
+ { name: "plugins.tab", id: name, order: 5, label: () => t("tab") },
946
+ (ownerProps) => getH()(Tab, { ...ownerProps, t, locale })
947
+ )
948
+ );
949
+ }
950
+
951
+ // src/client/index.ts
952
+ function createPlugin(require2) {
953
+ installReact(require2("react"));
954
+ return { name, inject, apply: (ctx) => apply(ctx) };
955
+ }
956
+
957
+ return module.exports.createPlugin(require)
958
+ },
959
+ })