dsh-plugin-update 0.2.0 → 0.3.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/dist/panel.js ADDED
@@ -0,0 +1,1300 @@
1
+ // AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/panel.ts
2
+ import { buildPhoneNames, DEFAULT_PANEL_POLL_MS, MIN_PANEL_POLL_MS } from "./config.js";
3
+ import { COPY_BUDGET_CHARS, sanitizeForCopy } from "./redaction.js";
4
+ import { validReleaseVersion } from "./service.js";
5
+ import {
6
+ changelogForUpdate,
7
+ parseChangelog,
8
+ renderChangelogHTML
9
+ } from "./changelog.js";
10
+ const BLOCKED_COPY = {
11
+ "unknown-profile": {
12
+ title: "\u4F7F\u7528\u8303\u56F4\u6216\u63D2\u4EF6\u4F4D\u7F6E\u8BA4\u4E0D\u51FA",
13
+ action: "\u91CD\u5F00\u5BBF\u4E3B\u518D\u67E5\u4E00\u6B21\uFF1B\u4E00\u76F4\u8FD9\u6837\u5C31\u628A\u7248\u672C\u53F7\u4E0E\u65E5\u5FD7\u4EA4\u7ED9\u63D2\u4EF6\u4F5C\u8005\uFF1B\u8FD9\u79CD\u60C5\u5F62\u4E0D\u7ED9\u624B\u5DE5\u547D\u4EE4"
14
+ },
15
+ "source-install": {
16
+ title: "\u5F53\u524D\u662F\u4ECE\u6E90\u7801\u88C5\u7684\uFF0C\u4E0D\u662F\u6309\u7248\u672C\u53F7\u88C5\u7684",
17
+ action: "\u8FD9\u79CD\u60C5\u5F62\u4E0D\u7ED9\u624B\u5DE5\u547D\u4EE4\uFF1B\u60F3\u8D70\u66F4\u65B0\u5148\u6309\u7248\u672C\u53F7\u91CD\u88C5\u4E00\u6B21"
18
+ },
19
+ "invalid-installation": {
20
+ title: "\u5DF2\u88C5\u7684\u5305\u4E0D\u5B8C\u6574\uFF08\u540D\u5B57\u5BF9\u4E0D\u4E0A\u3001\u7248\u672C\u975E\u6CD5\u3001\u5165\u53E3\u6587\u4EF6\u7F3A\u5931\uFF09",
21
+ action: "\u91CD\u88C5\u5F53\u524D\u7248\u672C\uFF0C\u4FEE\u597D\u5DF2\u88C5\u76EE\u5F55\u518D\u67E5\u66F4\u65B0"
22
+ },
23
+ "installation-changed": {
24
+ title: "\u5B89\u88C5\u4F4D\u7F6E\u5728\u4F7F\u7528\u4E2D\u9014\u53D8\u4E86\uFF08\u6362\u4E86\u76EE\u5F55\u6216\u6362\u4E86\u5305\uFF09",
25
+ action: "\u91CD\u65B0\u6253\u5F00\u5BBF\u4E3B\u518D\u67E5\u4E00\u6B21\uFF1B\u8FD8\u51FA\u73B0\u5C31\u91CD\u88C5"
26
+ },
27
+ "pending-restart": {
28
+ title: "\u65B0\u7248\u5DF2\u88C5\u5230\u78C1\u76D8\uFF0C\u6B63\u5728\u8DD1\u7684\u8FD8\u662F\u65E7\u7248",
29
+ action: "\u91CD\u542F\u5BBF\u4E3B\uFF0C\u8BA9\u65B0\u7248\u8DD1\u8D77\u6765\uFF1B\u8FD9\u662F\u6B63\u5E38\u7EC8\u6001\uFF0C\u4E0D\u662F\u5931\u8D25"
30
+ },
31
+ "registry-conflict": {
32
+ title: "\u672C\u5730\u58F0\u660E\u7684\u7248\u672C\u4E0E\u78C1\u76D8\u5B9E\u9645\u7248\u672C\u4E92\u76F8\u77DB\u76FE",
33
+ action: "\u6253\u5F00\u4F7F\u7528\u8303\u56F4\u7684\u6E05\u5355\u6587\u4EF6\uFF0C\u628A\u76EE\u6807\u5305\u540D\u90A3\u4E00\u884C\u6539\u6210\u7248\u672C\u53F7\u518D\u8BD5"
34
+ },
35
+ "incompatible-node": {
36
+ title: "\u65B0\u7248\u8981\u6C42\u7684 Node \u4E0E\u5F53\u524D\u8FD0\u884C\u7684\u5BF9\u4E0D\u4E0A",
37
+ action: "\u5148\u5347\u7EA7 Node \u5230 22 \u6216\u66F4\u9AD8\uFF0C\u518D\u67E5\u66F4\u65B0"
38
+ },
39
+ "recovery-required": {
40
+ title: "\u4E0A\u6B21\u5B89\u88C5\u88AB\u6253\u65AD\uFF0C\u7559\u4E0B\u4E00\u4E2A\u534A\u622A\u4EFB\u52A1",
41
+ action: "\u91CD\u65B0\u70B9\u4E00\u6B21\u5B89\u88C5\uFF1B\u4E00\u76F4\u51FA\u73B0\u5C31\u6309\u7B2C 6 \u8282\u6392\u9519"
42
+ }
43
+ };
44
+ function blockedCopy(reason) {
45
+ if (reason === null || reason === void 0) return null;
46
+ return BLOCKED_COPY[reason] ?? null;
47
+ }
48
+ const PHONE_FAILURE_COPY = {
49
+ "check-failed": {
50
+ zh: "\u67E5\u65B0\u7248\u6CA1\u6210\u529F\uFF08\u8054\u7F51\u3001\u6E90\u3001\u9650\u6D41\u90FD\u53EF\u80FD\uFF09",
51
+ act: "\u8FC7\u4E00\u4F1A\u513F\u518D\u67E5\u4E00\u6B21\uFF1B\u4E00\u76F4\u5931\u8D25\u5C31\u628A\u590D\u5236\u8BCA\u65AD\u4EA4\u7ED9\u63D2\u4EF6\u4F5C\u8005"
52
+ },
53
+ "invalid-release": {
54
+ zh: "\u62FF\u5230\u7684\u53D1\u5E03\u4FE1\u606F\u4E0D\u5408\u6CD5\uFF08\u7248\u672C\u53F7\u975E\u6CD5\u6216\u5185\u5BB9\u5BF9\u4E0D\u4E0A\uFF09",
55
+ act: "\u68C0\u67E5\u6E05\u5355\u6587\u4EF6\u91CC\u7684\u5305\u540D\u4E0E\u7248\u672C\u5199\u6CD5\uFF0C\u518D\u67E5\u4E00\u6B21"
56
+ },
57
+ "check-expired": {
58
+ zh: "\u51ED\u8BC1\u8FC7\u671F\u4E86\uFF0C\u5B89\u88C5\u8BF7\u6C42\u88AB\u62D2",
59
+ act: "\u91CD\u65B0\u67E5\u4E00\u6B21\u65B0\u7248\u518D\u70B9\u5B89\u88C5\uFF0C\u4E0D\u8981\u91CD\u8BD5\u65E7\u7F16\u53F7"
60
+ },
61
+ "update-busy": {
62
+ zh: "\u540C\u4E00\u4F7F\u7528\u8303\u56F4\u6B63\u5728\u88C5\u53E6\u4E00\u4E2A",
63
+ act: "\u7B49\u5F53\u524D\u4EFB\u52A1\u79BB\u5F00 installing/verifying \u518D\u70B9\uFF1B\u6392\u961F\u4E2D\u53BB\u67E5\u72B6\u6001\u770B\u4F4D\u7F6E"
64
+ },
65
+ "install-failed": {
66
+ zh: "\u88C5\u4E0D\u4E0A\uFF08\u8BE6\u89C1\u8BCA\u65AD\u6458\u8981\uFF09",
67
+ act: "\u5148\u770B\u590D\u5236\u8BCA\u65AD\uFF1B\u5B98\u65B9\u684C\u9762\u7248\u628A\u8FD9\u6BB5\u4EA4\u7ED9\u63D2\u4EF6\u4F5C\u8005"
68
+ },
69
+ internal: {
70
+ zh: "\u51FA\u4E86\u70B9\u95EE\u9898\uFF0C\u8BA4\u4E0D\u51FA\u5177\u4F53\u539F\u56E0",
71
+ act: "\u5148\u91CD\u8BD5\u4E00\u6B21\uFF1B\u4E00\u76F4\u8FD9\u6837\u5C31\u628A\u590D\u5236\u8BCA\u65AD\u4EA4\u7ED9\u63D2\u4EF6\u4F5C\u8005"
72
+ }
73
+ };
74
+ const UNKNOWN_FAILURE_COPY = {
75
+ zh: "\u51FA\u4E86\u70B9\u95EE\u9898\uFF0C\u8BA4\u4E0D\u51FA\u5177\u4F53\u539F\u56E0",
76
+ act: "\u5148\u91CD\u8BD5\u4E00\u6B21\uFF1B\u4E00\u76F4\u8FD9\u6837\u5C31\u628A\u590D\u5236\u8BCA\u65AD\u4EA4\u7ED9\u63D2\u4EF6\u4F5C\u8005\uFF08\u5E26\u4E0A\u4F60\u770B\u5230\u7684\u7801\uFF09"
77
+ };
78
+ function isKnownFailureCode(code) {
79
+ if (typeof code !== "string" || !code) return false;
80
+ const c = code.trim();
81
+ if (!c) return false;
82
+ return c in BLOCKED_COPY || c in PHONE_FAILURE_COPY;
83
+ }
84
+ function failureCopy(code) {
85
+ if (typeof code !== "string") return null;
86
+ const c = code.trim();
87
+ if (!c) return null;
88
+ const blocked = BLOCKED_COPY[c];
89
+ if (blocked) return { zh: blocked.title, act: blocked.action };
90
+ const phone = PHONE_FAILURE_COPY[c];
91
+ if (phone) return phone;
92
+ return UNKNOWN_FAILURE_COPY;
93
+ }
94
+ function failureCodeOf(reply, fallback) {
95
+ const kind = reply && typeof reply.errorKind === "string" ? reply.errorKind.trim() : "";
96
+ if (kind) return kind;
97
+ const err = reply && typeof reply.error === "string" ? reply.error.trim() : "";
98
+ if (err) return err;
99
+ if (typeof fallback === "string" && fallback.trim()) return fallback.trim();
100
+ return "internal";
101
+ }
102
+ const PANEL_DIAG_MAX_CHARS = COPY_BUDGET_CHARS;
103
+ function redactForCopy(text) {
104
+ return sanitizeForCopy(text);
105
+ }
106
+ function buildDiagnosticText(input) {
107
+ const code = String(input.code || "check-failed");
108
+ const lines = [`[\u66F4\u65B0\u8BCA\u65AD] ${String(input.pluginId)} \u7A33\u5B9A\u7801\uFF1A${code}`];
109
+ const detail = redactForCopy(input.detail ?? "");
110
+ lines.push(`\u4EBA\u8BDD\uFF1A${detail || failureCopy(code)?.act || code}`);
111
+ const versions = [
112
+ `\u8FD0\u884C\u7248\uFF1A${input.runningVersion ?? "\u672A\u77E5"}`,
113
+ `\u5DF2\u88C5\uFF1A${input.installedVersion ?? "\u672A\u77E5"}`,
114
+ `\u8FDC\u7AEF\uFF1A${input.latestVersion ?? "\u672A\u67E5\u8FC7"}`
115
+ ].join(" / ");
116
+ lines.push(versions);
117
+ const queue = input.queuePosition === 0 ? "\u6B63\u5728\u5B89\u88C5" : typeof input.queuePosition === "number" ? `\u6392\u961F\u7B2C ${input.queuePosition} \u4F4D` : "\u4E0D\u5728\u961F\u5217\u91CC";
118
+ const hostLine = [`\u5BBF\u4E3B\uFF1A${input.hostKind ?? "\u672A\u77E5"}`];
119
+ if (typeof input.profileName === "string" && input.profileName) hostLine.push(`\u4F7F\u7528\u8303\u56F4\uFF1A${input.profileName}`);
120
+ hostLine.push(`\u961F\u5217\uFF1A${queue}`);
121
+ lines.push(hostLine.join(" / ") + (input.requestId ? ` / \u8BF7\u6C42\u7F16\u53F7\uFF1A${input.requestId}` : ""));
122
+ const manual = redactForCopy(input.manual ?? "");
123
+ if (manual) lines.push(`\u624B\u5DE5\u547D\u4EE4\uFF1A${manual}`);
124
+ return redactForCopy(lines.join("\n"));
125
+ }
126
+ const DIAG_KNOWN_TYPES = {
127
+ v: "number",
128
+ stage: "string",
129
+ route: "string",
130
+ method: "string",
131
+ httpStatus: "number",
132
+ exitCode: "number",
133
+ latencyMs: "number",
134
+ detail: "string",
135
+ targetPackageName: "string",
136
+ runningVersion: "string",
137
+ latestVersion: "string",
138
+ environmentKind: "string",
139
+ requestId: "string",
140
+ checkId: "string",
141
+ registryHost: "string",
142
+ action: "string"
143
+ };
144
+ function readDiagTolerant(diag) {
145
+ const empty = {
146
+ stage: null,
147
+ route: null,
148
+ method: null,
149
+ httpStatus: null,
150
+ exitCode: null,
151
+ latencyMs: null,
152
+ detail: null,
153
+ targetPackageName: null,
154
+ runningVersion: null,
155
+ latestVersion: null,
156
+ environmentKind: null,
157
+ requestId: null,
158
+ checkId: null,
159
+ registryHost: null,
160
+ action: null,
161
+ unknownKeys: []
162
+ };
163
+ if (!diag || typeof diag !== "object" || Array.isArray(diag)) return empty;
164
+ const raw = diag;
165
+ const out = { ...empty, unknownKeys: [] };
166
+ for (const key of Object.keys(DIAG_KNOWN_TYPES)) {
167
+ if (!Object.prototype.hasOwnProperty.call(raw, key)) continue;
168
+ const v = raw[key];
169
+ if (v === void 0 || v === null) continue;
170
+ if (typeof v !== DIAG_KNOWN_TYPES[key]) continue;
171
+ out[key] = v;
172
+ }
173
+ for (const key of Object.keys(raw)) {
174
+ if (!Object.prototype.hasOwnProperty.call(DIAG_KNOWN_TYPES, key)) out.unknownKeys.push(key);
175
+ }
176
+ return out;
177
+ }
178
+ function pickText(value, fallback) {
179
+ if (typeof value === "string" && value.trim()) return value.trim();
180
+ return fallback;
181
+ }
182
+ function queueTextOf(queuePosition, _diagQueuePos) {
183
+ void _diagQueuePos;
184
+ const pos = typeof queuePosition === "number" ? queuePosition : null;
185
+ if (pos === 0) return "\u6B63\u5728\u5B89\u88C5";
186
+ if (typeof pos === "number") return `\u6392\u961F\u7B2C ${pos} \u4F4D`;
187
+ return "\u4E0D\u5728\u961F\u5217\u91CC";
188
+ }
189
+ function buildUpdateDiagCopy(input) {
190
+ const rawCode = String(input.code ?? "").trim() || "internal";
191
+ const copy = failureCopy(rawCode) ?? UNKNOWN_FAILURE_COPY;
192
+ const diag = readDiagTolerant(input.diag);
193
+ const detailRaw = diag.detail ?? input.detail ?? "";
194
+ const detail = redactForCopy(detailRaw) || "\uFF08\u672C\u56DE\u5305\u6CA1\u6709\u5E26\u8BCA\u65AD\u6458\u8981\uFF0C\u7B49\u7535\u8BDD\u4FA7 diag \u843D\u5B9A\u540E\u8865\u9F50\uFF09";
195
+ const pluginName = pickText(diag.targetPackageName ?? input.pluginId, "(\u672A\u77E5\u5305)");
196
+ const runVer = pickText(diag.runningVersion ?? input.runningVersion, "?");
197
+ const instVer = pickText(input.installedVersion ?? diag.latestVersion, "?");
198
+ const host = pickText(diag.environmentKind ?? input.hostKind, "\u672A\u77E5");
199
+ const routeRaw = diag.route ?? input.route;
200
+ const requestRaw = diag.requestId ?? input.requestId;
201
+ const checkRaw = diag.checkId ?? input.checkId;
202
+ const queue = queueTextOf(input.queuePosition ?? null);
203
+ const prov = [`\u63D2\u4EF6=${pluginName}`, `\u7248\u672C=${runVer}\u2192${instVer}`, `\u5BBF\u4E3B=${host}`];
204
+ if (typeof routeRaw === "string" && routeRaw.trim()) prov.push(`\u8DEF\u7531=${routeRaw.trim()}`);
205
+ if (diag.stage) prov.push(`\u9636\u6BB5=${diag.stage}`);
206
+ if (diag.method) prov.push(`\u65B9\u6CD5=${diag.method}`);
207
+ if (typeof diag.httpStatus === "number") prov.push(`HTTP=${diag.httpStatus}`);
208
+ if (typeof diag.exitCode === "number") prov.push(`exit=${diag.exitCode}`);
209
+ if (typeof diag.latencyMs === "number") prov.push(`\u8017\u65F6=${diag.latencyMs}ms`);
210
+ if (diag.registryHost) prov.push(`\u6E90=${diag.registryHost}`);
211
+ else prov.push("\u6E90=\u672A\u77E5\uFF08\u975E\u5B98\u65B9\u6E90\u65F6\u7701\u7565\u672C\u8EAB\u5373\u4FE1\u606F\uFF09");
212
+ if (diag.action) prov.push(`\u5EFA\u8BAE=${diag.action}`);
213
+ if (typeof requestRaw === "string" && requestRaw.trim()) prov.push(`\u8BF7\u6C42=${requestRaw.trim()}`);
214
+ if (typeof checkRaw === "string" && checkRaw.trim()) prov.push(`\u68C0\u67E5=${checkRaw.trim()}`);
215
+ prov.push(`\u961F\u5217=${queue}`);
216
+ const format = input.format === "line" ? "line" : "block";
217
+ const provLine = redactForCopy(prov.join(" \xB7 "));
218
+ if (format === "line") {
219
+ const line = `[update-diag] code=${rawCode} \xB7 ${copy.zh} \xB7 \u6458\u8981=${detail} \xB7 ${provLine} \xB7 \u600E\u4E48\u529E=${copy.act}`;
220
+ return redactForCopy(line);
221
+ }
222
+ const block = [
223
+ `[update-diag] ${rawCode} \u2014 ${copy.zh}`,
224
+ ` \u6458\u8981\uFF1A${detail}`,
225
+ ` \u6765\u6E90\uFF1A${provLine}`,
226
+ ` \u600E\u4E48\u529E\uFF1A${copy.act}`
227
+ ].join("\n");
228
+ return block;
229
+ }
230
+ const PANEL_SKIPPED_MAX = 50;
231
+ function panelSkipKey(pluginId) {
232
+ return `dsh-upd-skipped/${String(pluginId)}`;
233
+ }
234
+ function normalizePanelSkipped(raw) {
235
+ if (!raw || typeof raw !== "object") return [];
236
+ const list = raw.skipped;
237
+ if (!Array.isArray(list)) return [];
238
+ const seen = /* @__PURE__ */ new Set();
239
+ const out = [];
240
+ for (const item of list) {
241
+ if (!item || typeof item !== "object") continue;
242
+ const version = item.version;
243
+ if (!validReleaseVersion(version) || seen.has(version)) continue;
244
+ seen.add(version);
245
+ const at = item.skippedAt;
246
+ out.push({ version, skippedAt: typeof at === "number" && Number.isFinite(at) ? at : 0 });
247
+ }
248
+ return out.slice(0, PANEL_SKIPPED_MAX);
249
+ }
250
+ function createMemorySkipStore(now = Date.now) {
251
+ let skipped = [];
252
+ function assertVersion(version) {
253
+ if (!validReleaseVersion(version)) {
254
+ throw new Error(`[dsh-plugin-update] \u8DF3\u8FC7\u7248\u672C\u53F7\u975E\u6CD5\uFF1A\u987B\u4E3A\u53D1\u884C\u7248\u672C\u53F7\uFF08\u6536\u5230 ${JSON.stringify(version)}\uFF09`);
255
+ }
256
+ return version;
257
+ }
258
+ return {
259
+ list: () => skipped.map((e) => e.version),
260
+ has: (version) => validReleaseVersion(version) ? skipped.some((e) => e.version === version) : false,
261
+ skip: (version) => {
262
+ const v = assertVersion(version);
263
+ const at = now();
264
+ skipped = [{ version: v, skippedAt: typeof at === "number" && Number.isFinite(at) ? at : 0 }].concat(skipped.filter((e) => e.version !== v)).slice(0, PANEL_SKIPPED_MAX);
265
+ },
266
+ reset: (version) => {
267
+ if (version === void 0) skipped = [];
268
+ else skipped = skipped.filter((e) => e.version !== version);
269
+ }
270
+ };
271
+ }
272
+ function probeStorage(candidate) {
273
+ try {
274
+ if (!candidate || typeof candidate !== "object") return null;
275
+ const s = candidate;
276
+ if (typeof s.getItem !== "function" || typeof s.setItem !== "function" || typeof s.removeItem !== "function") return null;
277
+ return s;
278
+ } catch {
279
+ return null;
280
+ }
281
+ }
282
+ function createBrowserSkipStore(pluginId, storage, now = Date.now) {
283
+ const memory = createMemorySkipStore(now);
284
+ const store = probeStorage(storage) ?? probeStorage(globalThis["localStorage"] ?? null) ?? null;
285
+ if (!store) return memory;
286
+ const active = store;
287
+ const key = panelSkipKey(pluginId);
288
+ function read() {
289
+ try {
290
+ const raw = active.getItem(key);
291
+ if (!raw) return [];
292
+ return normalizePanelSkipped(JSON.parse(raw));
293
+ } catch {
294
+ return [];
295
+ }
296
+ }
297
+ function write(entries) {
298
+ try {
299
+ active.setItem(key, JSON.stringify({ skipped: entries }));
300
+ } catch {
301
+ }
302
+ }
303
+ try {
304
+ for (const e of read()) memory.skip(e.version);
305
+ } catch {
306
+ }
307
+ return {
308
+ list: () => memory.list(),
309
+ has: (version) => memory.has(version),
310
+ skip: (version) => {
311
+ memory.skip(version);
312
+ write(normalizePanelSkipped({ skipped: memory.list().map((v) => ({ version: v, skippedAt: now() })) }));
313
+ },
314
+ reset: (version) => {
315
+ memory.reset(version);
316
+ if (version === void 0) {
317
+ try {
318
+ active.removeItem(key);
319
+ } catch {
320
+ }
321
+ } else {
322
+ write(normalizePanelSkipped({ skipped: memory.list().map((v) => ({ version: v, skippedAt: now() })) }));
323
+ }
324
+ }
325
+ };
326
+ }
327
+ const SEAL_BY_KIND = {
328
+ loading: { text: "\u5F85\u67E5", mini: "\u67E5", tone: "ink" },
329
+ idle: { text: "\u5F85\u67E5", mini: "\u67E5", tone: "ink" },
330
+ update: { text: "\u53EF\u88C5", mini: "\u88C5", tone: "green" },
331
+ busy: { text: "\u5B89\u88C5\u4E2D", mini: "\u88C5", tone: "yellow" },
332
+ restart: { text: "\u5F85\u91CD\u542F", mini: "\u542F", tone: "yellow" },
333
+ blocked: { text: "\u53D7\u963B", mini: "\u963B", tone: "red" },
334
+ failed: { text: "\u53D7\u963B", mini: "\u963B", tone: "red" },
335
+ done: { text: "\u5DF2\u6700\u65B0", mini: "\u5B9A", tone: "green" }
336
+ };
337
+ function messageCodeOf(message) {
338
+ if (typeof message !== "string" || !message) return "";
339
+ const at = message.indexOf(":");
340
+ return (at < 0 ? message : message.slice(0, at)).trim();
341
+ }
342
+ function panelViewModel(input) {
343
+ const view = panelViewModelCore(input);
344
+ return { ...view, seal: SEAL_BY_KIND[view.banner.kind] ?? SEAL_BY_KIND.idle };
345
+ }
346
+ function panelViewModelCore(input) {
347
+ const { snapshot, manual, queue, skippedLatest, lastError } = input;
348
+ const errorKind = input.errorKind;
349
+ if (!snapshot) {
350
+ const earlyCode = typeof errorKind === "string" && errorKind.trim() || lastError || "";
351
+ if (earlyCode) {
352
+ const copy = failureCopy(earlyCode);
353
+ return {
354
+ banner: {
355
+ kind: "failed",
356
+ title: `\u66F4\u65B0\u5931\u8D25\uFF08${earlyCode}\uFF09\uFF1A${copy?.zh ?? earlyCode}\u3002`,
357
+ action: copy?.act || "\u590D\u5236\u8BCA\u65AD\u53D1\u7ED9\u63D2\u4EF6\u4F5C\u8005\uFF1B\u6DF1\u6316\u770B\u65E5\u5FD7\u901A\u9053\u3002"
358
+ },
359
+ installEnabled: false,
360
+ installLabel: "\u91CD\u8BD5\u5B89\u88C5",
361
+ skippedLatest: false,
362
+ showManual: manual ? true : false,
363
+ showReset: false,
364
+ queueNote: null
365
+ };
366
+ }
367
+ return {
368
+ banner: { kind: "loading", title: "\u6B63\u5728\u8BFB\u53D6\u66F4\u65B0\u72B6\u6001\u2026", action: "" },
369
+ installEnabled: false,
370
+ installLabel: "\u5B89\u88C5\u66F4\u65B0",
371
+ skippedLatest: false,
372
+ showManual: false,
373
+ showReset: false,
374
+ queueNote: null
375
+ };
376
+ }
377
+ const job = snapshot.job ?? null;
378
+ const jobState = job?.state ?? null;
379
+ const queueNote = queue && queue.busy ? queue.position === 0 ? "\u6B63\u5728\u5B89\u88C5\uFF08\u672C\u63D2\u4EF6\u5728\u88C5\uFF09\u3002" : typeof queue.position === "number" ? `\u524D\u65B9\u6709\u5B89\u88C5\u5728\u8FDB\u884C\uFF0C\u672C\u63D2\u4EF6\u6392\u7B2C ${queue.position} \u4F4D\uFF0C\u5230\u961F\u9996\u518D\u70B9\u5B89\u88C5\u3002` : "\u524D\u65B9\u6709\u5176\u4ED6\u63D2\u4EF6\u5728\u5B89\u88C5\uFF0C\u7A0D\u540E\u91CD\u8BD5\u3002" : null;
380
+ if (skippedLatest && snapshot.latestVersion) {
381
+ return {
382
+ banner: {
383
+ kind: "idle",
384
+ title: `\u5DF2\u8DF3\u8FC7 ${snapshot.latestVersion}`,
385
+ action: "\u70B9\u201C\u6062\u590D\u201D\u53EF\u91CD\u65B0\u63D0\u9192\u8BE5\u7248\u672C\uFF1B\u6709\u66F4\u65B0\u7684\u65B0\u7248\u672C\u4F1A\u7167\u5E38\u63D0\u9192\u3002"
386
+ },
387
+ installEnabled: false,
388
+ installLabel: "\u5B89\u88C5\u66F4\u65B0",
389
+ skippedLatest: true,
390
+ showManual: false,
391
+ showReset: true,
392
+ queueNote
393
+ };
394
+ }
395
+ if (snapshot.blockedReason === "pending-restart") {
396
+ const latest = snapshot.latestVersion ?? snapshot.installedVersion ?? "";
397
+ return {
398
+ banner: {
399
+ kind: "restart",
400
+ // 文案照原型(d5-paper.html:329):不带 emoji——警示由横幅左侧的手绘 SVG 标承担,
401
+ // 印章在状态一侧,两者各司其职,不再三重标记。
402
+ title: `\u65B0\u7248 ${latest} \u5DF2\u5B89\u88C5\uFF0C\u91CD\u542F\u5BBF\u4E3B\u540E\u751F\u6548\u3002`,
403
+ action: BLOCKED_COPY["pending-restart"].action
404
+ },
405
+ installEnabled: false,
406
+ installLabel: "\u5B89\u88C5\u66F4\u65B0",
407
+ skippedLatest: false,
408
+ showManual: manual ? true : false,
409
+ showReset: false,
410
+ queueNote
411
+ };
412
+ }
413
+ if (jobState === "installing" || jobState === "verifying") {
414
+ return {
415
+ banner: {
416
+ kind: "busy",
417
+ title: `\u6B63\u5728\u5B89\u88C5${job?.targetVersion ? ` ${job.targetVersion}` : ""}\u2026\u5173\u95ED\u9762\u677F\u4E0D\u4F1A\u4E2D\u65AD\u3002`,
418
+ action: "\u8FDB\u5EA6\u6309\u8F6E\u8BE2\u81EA\u52A8\u5237\u65B0\uFF1B\u91CD\u5F00\u9762\u677F 1 \u79D2\u5185\u6062\u590D\u663E\u793A\u3002"
419
+ },
420
+ installEnabled: false,
421
+ installLabel: "\u5B89\u88C5\u4E2D\u2026",
422
+ skippedLatest: false,
423
+ showManual: false,
424
+ showReset: false,
425
+ queueNote
426
+ };
427
+ }
428
+ const jobCode = jobState === "failed" ? messageCodeOf(job?.message) || "install-failed" : "";
429
+ const failedCode = typeof errorKind === "string" && errorKind.trim() || lastError || jobCode || "";
430
+ if (failedCode || jobState === "failed" || jobState === "interrupted") {
431
+ const code = failedCode || "install-failed";
432
+ const copy = failureCopy(code);
433
+ return {
434
+ banner: {
435
+ kind: "failed",
436
+ title: `\u66F4\u65B0\u5931\u8D25\uFF08${code}\uFF09\uFF1A${copy?.zh ?? code}\u3002`,
437
+ action: copy?.act || "\u590D\u5236\u8BCA\u65AD\u53D1\u7ED9\u63D2\u4EF6\u4F5C\u8005\uFF1B\u6DF1\u6316\u770B\u65E5\u5FD7\u901A\u9053\u3002"
438
+ },
439
+ installEnabled: snapshot.canInstall,
440
+ installLabel: "\u91CD\u8BD5\u5B89\u88C5",
441
+ skippedLatest: false,
442
+ showManual: manual ? true : false,
443
+ showReset: false,
444
+ queueNote
445
+ };
446
+ }
447
+ if (snapshot.blockedReason) {
448
+ const copy = blockedCopy(snapshot.blockedReason);
449
+ return {
450
+ banner: {
451
+ kind: "blocked",
452
+ title: copy ? `${copy.title}\u3002` : `${snapshot.blockedReason}\u3002`,
453
+ action: copy?.action || ""
454
+ },
455
+ installEnabled: false,
456
+ installLabel: "\u5B89\u88C5\u66F4\u65B0",
457
+ skippedLatest: false,
458
+ showManual: manual ? true : false,
459
+ showReset: false,
460
+ queueNote
461
+ };
462
+ }
463
+ if (snapshot.canInstall && snapshot.latestVersion) {
464
+ return {
465
+ banner: {
466
+ kind: "update",
467
+ title: `\u6709\u65B0\u7248 ${snapshot.latestVersion} \u53EF\u88C5\uFF08\u5F53\u524D ${snapshot.runningVersion}\uFF09\u3002`,
468
+ action: "\u70B9\u5B89\u88C5\u5373\u8D70\u7CBE\u786E\u7248\u672C\u5B89\u88C5\uFF1B\u540C\u4E00\u4F7F\u7528\u8303\u56F4\u540C\u65F6\u53EA\u88C5\u4E00\u4E2A\u3002"
469
+ },
470
+ installEnabled: true,
471
+ installLabel: `\u5B89\u88C5 ${snapshot.latestVersion}`,
472
+ skippedLatest: false,
473
+ showManual: manual ? true : false,
474
+ showReset: false,
475
+ queueNote
476
+ };
477
+ }
478
+ return {
479
+ banner: { kind: "done", title: "\u5DF2\u662F\u6700\u65B0\uFF0C\u65E0\u9700\u66F4\u65B0\u3002", action: "" },
480
+ installEnabled: false,
481
+ installLabel: "\u5B89\u88C5\u66F4\u65B0",
482
+ skippedLatest: false,
483
+ showManual: false,
484
+ showReset: false,
485
+ queueNote
486
+ };
487
+ }
488
+ const UPDATE_PANEL_CSS = [
489
+ '.dsh-upd{font:14px/1.6 system-ui,"Microsoft YaHei",sans-serif;color:var(--dsh-upd-fg,#1f2937);',
490
+ "background:var(--dsh-upd-bg,#ffffff);border:1px solid var(--dsh-upd-line,#e5e7eb);border-radius:8px;padding:12px 14px;max-width:560px;",
491
+ // 横幅配色走变量(浅色默认 + 深色覆盖,见下方 dark 媒体块):硬编码浅色会让深色下
492
+ // 「浅底 + 浅字」读不出来(现场回归:默认主题深色模式更新横幅白底浅字)。
493
+ "--dsh-upd-ok-bg:#ecfdf5;--dsh-upd-ok-line:#059669;--dsh-upd-warn-bg:#fffbeb;--dsh-upd-warn-line:#d97706;",
494
+ "--dsh-upd-bad-bg:#fef2f2;--dsh-upd-bad-line:#dc2626;--dsh-upd-busy-bg:#eff6ff;--dsh-upd-busy-line:#2563eb}",
495
+ ".dsh-upd *{box-sizing:border-box}",
496
+ ".dsh-upd button{font:inherit;border:1px solid var(--dsh-upd-line,#d1d5db);border-radius:6px;background:var(--dsh-upd-btn,#f9fafb);",
497
+ "color:inherit;padding:4px 12px;cursor:pointer;margin:2px 6px 2px 0}",
498
+ ".dsh-upd button:disabled{opacity:.45;cursor:not-allowed}",
499
+ ".dsh-upd button:focus-visible{outline:2px solid var(--dsh-upd-focus,#2563eb);outline-offset:1px}",
500
+ '.dsh-upd button[data-primary="1"]{background:var(--dsh-upd-primary,#2563eb);border-color:var(--dsh-upd-primary,#2563eb);color:#fff}',
501
+ ".dsh-upd-banner{border-left:4px solid var(--dsh-upd-line,#9ca3af);padding:6px 10px;margin:0 0 8px;background:var(--dsh-upd-soft,#f3f4f6)}",
502
+ '.dsh-upd-banner[data-kind="restart"]{border-color:var(--dsh-upd-warn-line);background:var(--dsh-upd-warn-bg)}',
503
+ '.dsh-upd-banner[data-kind="failed"],.dsh-upd-banner[data-kind="blocked"]{border-color:var(--dsh-upd-bad-line);background:var(--dsh-upd-bad-bg)}',
504
+ '.dsh-upd-banner[data-kind="update"]{border-color:var(--dsh-upd-ok-line);background:var(--dsh-upd-ok-bg)}',
505
+ '.dsh-upd-banner[data-kind="busy"]{border-color:var(--dsh-upd-busy-line);background:var(--dsh-upd-busy-bg)}',
506
+ ".dsh-upd code{font-family:Consolas,Menlo,monospace;font-size:12px;word-break:break-all}",
507
+ ".dsh-upd-manual,.dsh-upd-queue,.dsh-upd-log{margin:8px 0;font-size:13px}",
508
+ ".dsh-upd-changelog-wrap{margin:8px 0;font-size:13px;border-top:1px solid var(--dsh-upd-line,#e5e7eb);padding-top:8px}",
509
+ ".dsh-upd-changelog-title{font-weight:700;margin:0 0 4px}",
510
+ ".dsh-upd-changelog-version{margin:6px 0}",
511
+ ".dsh-upd-changelog-catname{font-weight:600;margin:6px 0 2px}",
512
+ ".dsh-upd-changelog ul{margin:2px 0 6px 20px;padding:0}",
513
+ ".dsh-upd-changelog li{margin:2px 0}",
514
+ ".dsh-upd-changelog-fold{margin:4px 0}",
515
+ ".dsh-upd-changelog-fold>summary{cursor:pointer}",
516
+ ".dsh-upd-changelog-neutral{color:inherit;opacity:.8}",
517
+ ".dsh-upd-overlay{position:fixed;inset:0;background:rgba(0,0,0,.35);display:flex;align-items:center;justify-content:center;z-index:9999}",
518
+ ".dsh-upd-overlay .dsh-upd{background:var(--dsh-upd-bg,#ffffff);max-height:85vh;overflow:auto}",
519
+ // —— 档案头 / 版本条 / 章节 / 进度条 / 跳过行(原型 :208-245 的新结构,默认主题给最小可用样式)——
520
+ ".dsh-upd-head{display:flex;gap:12px;align-items:baseline;flex-wrap:wrap}",
521
+ // 卷宗抬头「插件更新 / 更新档案 卷」:D5 档案卷才画,默认(最小)主题不画。
522
+ // 两个主题共用同一份内核 HTML(见 renderUpdatePanelHTML 的注释),画不画是皮肤决定的事。
523
+ ".dsh-upd-masthead{display:none}",
524
+ ".dsh-upd-name{font-weight:700}",
525
+ ".dsh-upd-meta{font-size:12.5px;opacity:.75}",
526
+ ".dsh-upd-proftag{font-family:Consolas,Menlo,monospace;font-size:11px;border:1px solid var(--dsh-upd-line,#d1d5db);border-radius:3px;padding:0 5px;margin-left:6px;letter-spacing:.06em}",
527
+ ".dsh-upd-strip{display:flex;flex-wrap:wrap;margin:8px 0 0;border:1px solid var(--dsh-upd-line,#e5e7eb);border-radius:4px;overflow:hidden;font-size:12.5px}",
528
+ ".dsh-upd-strip>div{flex:1 1 110px;padding:6px 10px;border-left:1px solid var(--dsh-upd-line,#e5e7eb)}",
529
+ ".dsh-upd-strip>div:first-child{border-left:0}",
530
+ ".dsh-upd-strip-k{display:block;font-size:11px;letter-spacing:.14em;opacity:.7}",
531
+ ".dsh-upd-strip-v{font-family:Consolas,Menlo,monospace;font-size:12.5px}",
532
+ ".dsh-upd-chapter{margin-top:14px;padding-top:10px;border-top:1px solid var(--dsh-upd-line,#e5e7eb)}",
533
+ ".dsh-upd-chap-head{display:flex;align-items:baseline;gap:10px;margin-bottom:6px}",
534
+ ".dsh-upd-chap-no{font-size:13px;font-style:italic;opacity:.6}",
535
+ ".dsh-upd-chap-title{font-size:14px;margin:0}",
536
+ ".dsh-upd-chap-rule{flex:1;border-top:1px solid var(--dsh-upd-line,#e5e7eb);transform:translateY(-3px)}",
537
+ // 03 章标题行右端的开关(问题 3 定案:按钮形态、挪到标题行)。
538
+ ".dsh-upd-chap-note{flex:none;font-size:12px;opacity:.75}",
539
+ ".dsh-upd-chap-note button{margin:0}",
540
+ // 更新队列两行键值(用户定案的设计):结构两主题共用,皮肤各自收敛。
541
+ ".dsh-upd-qrow{display:flex;align-items:baseline;gap:10px;padding:6px 0}",
542
+ ".dsh-upd-qrow+.dsh-upd-qrow{border-top:1px solid var(--dsh-upd-line,#e5e7eb)}",
543
+ ".dsh-upd-qdot{width:8px;height:8px;border-radius:50%;flex:none;align-self:center;background:currentColor;opacity:.5}",
544
+ '.dsh-upd-qdot[data-tone="busy"]{background:var(--dsh-upd-warn-line,#d97706);opacity:1}',
545
+ '.dsh-upd-qdot[data-tone="you"]{background:var(--dsh-upd-primary,#2563eb);opacity:1}',
546
+ ".dsh-upd-qk{flex:none;width:5.5em;font-size:12px;opacity:.7}",
547
+ ".dsh-upd-qv{font-weight:600}",
548
+ ".dsh-upd-qn{margin-left:auto;font-size:12px;opacity:.7}",
549
+ ".dsh-upd-qseq{font-family:Consolas,Menlo,monospace;font-size:12px;word-break:break-all}",
550
+ ".dsh-upd-prog{height:8px;background:var(--dsh-upd-line,#e5e7eb);border-radius:4px;overflow:hidden;margin:10px 0 4px}",
551
+ ".dsh-upd-prog-bar{display:block;height:100%;background:var(--dsh-upd-primary,#2563eb);transition:width .3s}",
552
+ ".dsh-upd-progtxt{font-size:12.5px;opacity:.75}",
553
+ ".dsh-upd-skipline{font-size:13px;margin-top:8px}",
554
+ ".dsh-upd-tag{display:inline-block;border:1px dashed currentColor;border-radius:3px;padding:1px 8px;margin-right:8px;font-family:Consolas,Menlo,monospace;font-size:12px}",
555
+ ".dsh-upd-err{font-size:13px;margin:0 0 6px}",
556
+ "@media (prefers-color-scheme: dark){.dsh-upd{--dsh-upd-fg:#e5e7eb;--dsh-upd-bg:#111827;--dsh-upd-line:#374151;",
557
+ "--dsh-upd-btn:#1f2937;--dsh-upd-soft:#1f2937;--dsh-upd-primary:#3b82f6;--dsh-upd-focus:#93c5fd;",
558
+ // 横幅深色覆盖:底色用低透明度同色系(不是浅色原值),边线提亮,保证「深底浅字」可读。
559
+ "--dsh-upd-ok-bg:rgba(16,185,129,.14);--dsh-upd-ok-line:#34d399;",
560
+ "--dsh-upd-warn-bg:rgba(245,158,11,.16);--dsh-upd-warn-line:#fbbf24;",
561
+ "--dsh-upd-bad-bg:rgba(239,68,68,.16);--dsh-upd-bad-line:#f87171;",
562
+ "--dsh-upd-busy-bg:rgba(59,130,246,.16);--dsh-upd-busy-line:#60a5fa}}"
563
+ ].join("\n");
564
+ const UPDATE_PANEL_D5_CSS = [
565
+ "/* D5 \u6863\u6848\u5377\u53EF\u9009\u4E3B\u9898\uFF1A\u53EA\u6362\u989C\u8272/\u5B57\u4F53/\u95F4\u8DDD\uFF1B\u5185\u6838 DOM \u987A\u5E8F\u4E00\u5B57\u4E0D\u52A8\uFF0C\u4E0D\u65AD\u590D\u5236\u8BCA\u65AD\u3002 */",
566
+ '.dsh-upd[data-theme="d5-paper"]{--d5-bg:#f7f3ea;--d5-card:#fffdf6;--d5-ink:#1a1a1a;--d5-muted:#6f675a;',
567
+ "--d5-line:#e3d9c4;--d5-line-strong:#c4b896;--d5-accent:#c8402a;--d5-accent-deep:#9c2e1d;",
568
+ "--d5-ok:#1a7f37;--d5-ok-bg:#e9f4ea;--d5-warn:#8a5a00;--d5-warn-bg:#fbf0d0;",
569
+ "--d5-bad:#b3261e;--d5-bad-bg:#fbe9e5;",
570
+ '--d5-serif:Georgia,"Songti SC","STSong","SimSun","Noto Serif CJK SC","Source Han Serif SC",serif;',
571
+ '--d5-sans:system-ui,"PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif;',
572
+ '--d5-mono:ui-monospace,"SF Mono",SFMono-Regular,Consolas,"Noto Sans Mono",monospace;',
573
+ "--d5-shadow:0 1px 2px rgba(60,40,20,.08),0 12px 32px rgba(60,40,20,.10);",
574
+ "font-family:var(--d5-sans);color:var(--d5-ink);background:var(--d5-card);",
575
+ "border:1px solid var(--d5-line-strong);border-radius:4px;box-shadow:var(--d5-shadow)}",
576
+ '.dsh-upd[data-theme="d5-paper"] button{border-color:var(--d5-line-strong);background:transparent;color:var(--d5-ink);border-radius:3px;font-family:var(--d5-sans)}',
577
+ '.dsh-upd[data-theme="d5-paper"] button:hover:not(:disabled){border-color:var(--d5-accent);color:var(--d5-accent)}',
578
+ '.dsh-upd[data-theme="d5-paper"] button[data-primary="1"]{background:var(--d5-accent);border-color:var(--d5-accent);color:#fff}',
579
+ '.dsh-upd[data-theme="d5-paper"] button[data-primary="1"]:hover:not(:disabled){background:var(--d5-accent-deep);color:#fff}',
580
+ '.dsh-upd[data-theme="d5-paper"] button:focus-visible{outline:2px solid var(--d5-accent);outline-offset:2px}',
581
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner{background:var(--d5-bg);border-color:var(--d5-line-strong)}',
582
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner[data-kind="update"]{border-color:var(--d5-ok);background:var(--d5-ok-bg)}',
583
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner[data-kind="busy"]{border-color:var(--d5-warn);background:var(--d5-warn-bg)}',
584
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner[data-kind="restart"]{border-color:var(--d5-warn);background:var(--d5-warn-bg);font-family:var(--d5-serif);border-width:2px}',
585
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner[data-kind="failed"],.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner[data-kind="blocked"]{border-color:var(--d5-bad);background:var(--d5-bad-bg)}',
586
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner[data-kind="done"]{border-color:var(--d5-ok);background:var(--d5-ok-bg)}',
587
+ // —— 大印章(原型 :206 `.seal`:右上 88px、旋转 -7°、双细框;内容与色调来自根属性,不加节点)——
588
+ '.dsh-upd[data-theme="d5-paper"]{position:relative;padding:22px 26px 20px}',
589
+ '.dsh-upd[data-theme="d5-paper"]::before{content:attr(data-seal);position:absolute;top:20px;right:24px;width:88px;height:88px;',
590
+ "display:flex;align-items:center;justify-content:center;text-align:center;letter-spacing:.18em;text-indent:.18em;line-height:1.35;",
591
+ "border:3px solid currentColor;border-radius:14px;transform:rotate(-7deg);font-family:var(--d5-serif);font-weight:700;font-size:21px;",
592
+ "background:color-mix(in srgb,currentColor 8%,transparent);user-select:none;pointer-events:none;",
593
+ "box-shadow:inset 0 0 0 5px var(--d5-card),inset 0 0 0 6px currentColor,0 2px 6px rgba(0,0,0,.12)}",
594
+ '.dsh-upd[data-theme="d5-paper"][data-seal-tone="ink"]::before{color:var(--d5-muted)}',
595
+ '.dsh-upd[data-theme="d5-paper"][data-seal-tone="green"]::before{color:var(--d5-ok)}',
596
+ '.dsh-upd[data-theme="d5-paper"][data-seal-tone="yellow"]::before{color:var(--d5-warn)}',
597
+ '.dsh-upd[data-theme="d5-paper"][data-seal-tone="red"]::before{color:var(--d5-bad)}',
598
+ // 印章占位:首行(横幅/状态行)右侧留出 120px,文字不许压到印章上(原型 .filehead padding-right:120px)
599
+ // —— 卷宗抬头(原型 :195-203 的刊头,主题切换按钮按用户口径去掉):只有 D5 档案卷才显示 ——
600
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-masthead{display:block;padding:0 0 10px;margin:0 0 12px;border-bottom:1px solid var(--d5-line)}',
601
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-masthead-kicker{display:block;font-size:11px;letter-spacing:.35em;color:var(--d5-muted);margin-bottom:3px}',
602
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-masthead-title{font-family:var(--d5-serif);font-size:26px;font-weight:700;line-height:1.2}',
603
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-masthead-title i{color:var(--d5-accent);font-style:normal}',
604
+ // 横幅不再给大印章留 124px:实测(headless 量盒子)印章盒底边 y=109,横幅正文顶边 y=108、
605
+ // 状态行那句在 y=159——印章只压到横幅顶部的留白带,压不到正文。留着反而把 27px 那句话挤成两行
606
+ // (27px 单行需 428px,留白后只剩 366px)。档案头那 120px 保留:那里是真的重叠。
607
+ // —— 更新队列(03 章)D5 皮肤 ——
608
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-qrow{padding:8px 0}',
609
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-qk{width:66px;letter-spacing:.18em}',
610
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-qv{font-family:var(--d5-serif);font-size:16px}',
611
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-qn{font-family:var(--d5-mono);font-size:11.5px;color:var(--d5-muted)}',
612
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-qseq{color:var(--d5-muted)}',
613
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-chap-note{color:var(--d5-muted)}',
614
+ // —— 小印章(原型 :215 `.sealmini`:30px、旋转 -5°、一字)——
615
+ // 待重启横幅一律不画印章:那一档的标记是左侧手绘 SVG(原型 :446 的 .mark 只有 SVG)。
616
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner[data-kind="loading"]::before,.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner[data-kind="idle"]::before,',
617
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner[data-kind="update"]::before,.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner[data-kind="busy"]::before,',
618
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner[data-kind="blocked"]::before,.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner[data-kind="failed"]::before,',
619
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner[data-kind="done"]::before{content:attr(data-mini);display:inline-flex;align-items:center;justify-content:center;',
620
+ "width:30px;height:30px;margin-right:10px;vertical-align:middle;border:2px solid currentColor;border-radius:7px;",
621
+ "font-family:var(--d5-serif);font-weight:700;font-size:16px;line-height:26px;transform:rotate(-5deg);flex:none;color:var(--d5-muted)}",
622
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner[data-kind="update"]::before{color:var(--d5-ok)}',
623
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner[data-kind="busy"]::before{color:var(--d5-warn)}',
624
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner[data-kind="blocked"]::before,.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner[data-kind="failed"]::before{color:var(--d5-bad)}',
625
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner[data-kind="done"]::before{color:var(--d5-ok)}',
626
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-log code{font-family:var(--d5-mono);font-size:11px;color:var(--d5-muted);border:1px solid var(--d5-line-strong);border-radius:3px;padding:0 6px;letter-spacing:.06em}',
627
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-manual code{display:block;background:var(--d5-ink);color:var(--d5-bg);font-family:var(--d5-mono);font-size:12.5px;padding:12px 14px;border-radius:4px;white-space:pre-wrap;word-break:break-all}',
628
+ // 待重启标记:手绘 SVG 当**独立 flex 标记**放在文字块左侧(原型 :446 `.mark` 是独立节点),
629
+ // 不能用行内背景——那样换行时三角会落在句子中间把话劈开(现场回归)。
630
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner[data-kind="restart"]>div:first-child{display:flex;gap:10px;align-items:flex-start}',
631
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner[data-kind="restart"]>div:first-child::before{content:"";flex:none;width:20px;height:20px;margin-top:3px;',
632
+ 'background:url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2720%27 height=%2720%27 viewBox=%270 0 24 24%27 fill=%27none%27 stroke=%27%238a5a00%27 stroke-width=%272%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27%3E%3Cpath d=%27M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z%27/%3E%3Cline x1=%2712%27 y1=%279%27 x2=%2712%27 y2=%2713%27/%3E%3Cline x1=%2712%27 y1=%2717%27 x2=%2712.01%27 y2=%2717%27/%3E%3C/svg%3E") no-repeat center/20px 20px}',
633
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner>div:first-child{overflow:hidden;text-overflow:ellipsis}',
634
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-actions{flex-wrap:wrap}',
635
+ // —— 档案头(原型 :208-211 `.filehead`:serif 插件名 22px + 使用范围 + profile 牌;右侧留章位)——
636
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-head{display:flex;gap:16px;align-items:baseline;flex-wrap:wrap;padding-right:120px}',
637
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-name{font-family:var(--d5-serif);font-size:22px;font-weight:700}',
638
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-meta{width:100%;font-size:12.5px;color:var(--d5-muted)}',
639
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-meta b{color:var(--d5-ink);font-weight:600}',
640
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-proftag{font-family:var(--d5-mono);font-size:11px;color:var(--d5-muted);border:1px solid var(--d5-line-strong);border-radius:3px;padding:0 6px;margin-left:8px;letter-spacing:.06em}',
641
+ // —— 版本条(原型 :216 `.strip`:三格,格间一线,左上小写标签 + 等宽值)——
642
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-strip{display:flex;flex-wrap:wrap;margin:10px 0 0;border:1px solid var(--d5-line);border-radius:4px;overflow:hidden;font-size:12.5px}',
643
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-strip>div{flex:1 1 120px;padding:8px 12px;border-left:1px solid var(--d5-line)}',
644
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-strip>div:first-child{border-left:0}',
645
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-strip-k{display:block;font-size:11px;letter-spacing:.2em;color:var(--d5-muted)}',
646
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-strip-v{font-family:var(--d5-mono);font-size:13px}',
647
+ // —— 章节(原型 :218-245:01–05 编号 + 衬线标题 + 细线)——
648
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-chapter{margin-top:26px;padding-top:16px;border-top:1px solid var(--d5-line)}',
649
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-chap-head{display:flex;align-items:baseline;gap:12px;margin-bottom:10px}',
650
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-chap-no{font-family:var(--d5-serif);font-style:italic;font-size:15px;color:var(--d5-muted)}',
651
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-chap-title{font-family:var(--d5-serif);font-size:17px;margin:0;letter-spacing:.1em}',
652
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-chap-rule{flex:1;border-top:1px solid var(--d5-line);transform:translateY(-4px)}',
653
+ // —— 横幅即状态行 / 待重启横幅(原型 :81-85 `.restart-banner`:2px 边框、圆角 4、内边距 12/16、衬线;右侧留章位)——
654
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner{border:2px solid var(--d5-line-strong);border-radius:4px;padding:12px 16px;font-size:14.5px;font-family:var(--d5-serif);display:flex;gap:10px;align-items:center;flex-wrap:wrap}',
655
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner>div:first-child{flex:1 1 auto;min-width:0}',
656
+ // 状态行字号照原型 .status-line=27px(实测去掉横幅右侧占位后可写 486px > 428px,一行放得下)
657
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner>div:first-child strong{font-family:var(--d5-serif);font-size:27px;font-weight:700;line-height:1.35}',
658
+ // —— 进度条 / 跳过行(原型 :132-133 `.prog`、:129-131 `.skipline .tag`)——
659
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-prog{height:8px;background:var(--d5-line);border-radius:4px;overflow:hidden;margin:10px 0 4px}',
660
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-prog-bar{display:block;height:100%;background:var(--d5-accent);transition:width .3s}',
661
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-progtxt{font-size:12.5px;color:var(--d5-muted)}',
662
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-skipline{font-size:13px;margin-top:8px}',
663
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-tag{display:inline-block;border:1px dashed var(--d5-line-strong);border-radius:3px;padding:1px 8px;margin-right:8px;font-family:var(--d5-mono);font-size:12px}',
664
+ '.dsh-upd[data-theme="d5-paper"] .dsh-upd-err{font-size:13px;margin:0 0 6px;color:var(--d5-muted)}',
665
+ '@media (max-width:640px){.dsh-upd[data-theme="d5-paper"]{padding:10px 12px}.dsh-upd[data-theme="d5-paper"]::before{top:12px;right:12px;width:56px;height:56px;font-size:15px;box-shadow:inset 0 0 0 4px var(--d5-card),inset 0 0 0 5px currentColor}.dsh-upd[data-theme="d5-paper"] .dsh-upd-masthead-title{font-size:19px}.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner{padding-right:16px}.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner::before{width:24px;height:24px;font-size:14px;line-height:20px;flex:none}.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner>div:first-child{white-space:normal}}',
666
+ '@media (prefers-color-scheme: dark){.dsh-upd[data-theme="d5-paper"]{--d5-bg:#141210;--d5-card:#1e1a15;--d5-ink:#ece5d3;--d5-muted:#a89c83;',
667
+ "--d5-line:#3a3226;--d5-line-strong:#5c4e3b;--d5-accent:#e0684e;--d5-accent-deep:#f0866b;",
668
+ "--d5-ok:#8fd6a4;--d5-ok-bg:rgba(80,180,120,.12);--d5-warn:#e8c15a;--d5-warn-bg:rgba(232,193,90,.12);",
669
+ "--d5-bad:#ef8a7d;--d5-bad-bg:rgba(239,138,125,.12);--d5-shadow:0 1px 2px rgba(0,0,0,.4),0 12px 32px rgba(0,0,0,.45)}}",
670
+ '@media (prefers-color-scheme: dark){.dsh-upd[data-theme="d5-paper"] button[data-primary="1"]{color:#141210}.dsh-upd[data-theme="d5-paper"] button:focus-visible{outline-color:var(--d5-accent-deep)}}',
671
+ '@media (prefers-color-scheme: dark){.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner[data-kind="restart"]>div:first-child::before{background-image:url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2720%27 height=%2720%27 viewBox=%270 0 24 24%27 fill=%27none%27 stroke=%27%23e8c15a%27 stroke-width=%272%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27%3E%3Cpath d=%27M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z%27/%3E%3Cline x1=%2712%27 y1=%279%27 x2=%2712%27 y2=%2713%27/%3E%3Cline x1=%2712%27 y1=%2717%27 x2=%2712.01%27 y2=%2717%27/%3E%3C/svg%3E")}}',
672
+ '@media (forced-colors: active){.dsh-upd[data-theme="d5-paper"]{box-shadow:none}.dsh-upd[data-theme="d5-paper"]::before{background:none;box-shadow:none;border-color:CanvasText;color:CanvasText}.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner{border:1px solid CanvasText}.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner::before{border-color:CanvasText;color:CanvasText;background:Canvas}.dsh-upd[data-theme="d5-paper"] button{border:1px solid ButtonText}.dsh-upd[data-theme="d5-paper"] button[data-primary="1"]{background:ButtonFace;color:ButtonText;border-color:ButtonText}.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner[data-kind="restart"]>div:first-child::before{background-image:none;content:"\u26A0"}}',
673
+ '@media (prefers-reduced-motion: reduce){.dsh-upd[data-theme="d5-paper"] *{transition:none !important;animation:none !important}}'
674
+ ].join("\n");
675
+ function escapeHtml(text) {
676
+ return String(text).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
677
+ }
678
+ const CHAPTER_TITLES = ["\u68C0\u67E5\u4E0E\u5B89\u88C5", "\u66F4\u65B0\u65E5\u5FD7", "\u66F4\u65B0\u961F\u5217", "\u9519\u8BEF\u4FE1\u606F", "\u624B\u5DE5\u547D\u4EE4"];
679
+ function chapterOf(index, inner, note = "") {
680
+ const no = String(index).padStart(2, "0");
681
+ const title = CHAPTER_TITLES[index - 1];
682
+ return `<section class="dsh-upd-chapter" data-chapter="${no}"><div class="dsh-upd-chap-head"><span class="dsh-upd-chap-no">${no}</span><h3 class="dsh-upd-chap-title">${escapeHtml(title)}</h3><span class="dsh-upd-chap-rule"></span>${note}</div>${inner}</section>`;
683
+ }
684
+ function versionStrip(snapshot) {
685
+ const cell = (k, v) => `<div><span class="dsh-upd-strip-k">${escapeHtml(k)}</span><span class="dsh-upd-strip-v">${escapeHtml(v ?? "\u672A\u77E5")}</span></div>`;
686
+ if (!snapshot) return "";
687
+ return `<div class="dsh-upd-strip">` + cell("\u8FD0\u884C", snapshot.runningVersion) + cell("\u78C1\u76D8", snapshot.installedVersion) + cell("\u8FDC\u7AEF", snapshot.latestVersion) + `</div>`;
688
+ }
689
+ function progressBar(snapshot) {
690
+ const job = snapshot?.job;
691
+ if (!job) return "";
692
+ const state = String(job.state);
693
+ if (state !== "installing" && state !== "verifying") return "";
694
+ const width = state === "installing" ? 60 : 90;
695
+ const text = state === "installing" ? "\u6B63\u5728\u5B89\u88C5\u65B0\u7248\u2026" : "\u6B63\u5728\u6821\u9A8C\u5B89\u88C5\u7ED3\u679C\u2026";
696
+ return `<div class="dsh-upd-prog" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${width}"><i class="dsh-upd-prog-bar" style="width:${width}%"></i></div><div class="dsh-upd-progtxt">${escapeHtml(text)}</div>`;
697
+ }
698
+ function renderUpdatePanelKernel(input, view) {
699
+ const { snapshot, manual, queue, mode, showOthers, pluginId, copyNotice } = input;
700
+ const changelogMarkdown = input.changelogMarkdown ?? null;
701
+ const profileName = typeof input.profileName === "string" && input.profileName ? input.profileName : null;
702
+ const b = view.banner;
703
+ const seal = view.seal;
704
+ const parts = [];
705
+ parts.push(
706
+ '<div class="dsh-upd-masthead"><span class="dsh-upd-masthead-kicker">\u63D2\u4EF6\u66F4\u65B0</span><span class="dsh-upd-masthead-title">\u66F4\u65B0\u6863\u6848 <i>\u5377</i></span></div>'
707
+ );
708
+ parts.push(
709
+ `<div class="dsh-upd-head"><span class="dsh-upd-name">${escapeHtml(pluginId)}</span><span class="dsh-upd-meta">\u4F7F\u7528\u8303\u56F4 <b>${escapeHtml(profileName ?? "\u672A\u77E5")}</b><span class="dsh-upd-proftag">profile</span></span></div>`
710
+ );
711
+ parts.push(`<div class="dsh-upd-banner" data-kind="${b.kind}" data-mini="${escapeHtml(seal.mini)}" role="status" aria-live="polite">`);
712
+ parts.push(`<div><strong>${escapeHtml(b.title)}</strong></div>`);
713
+ if (b.action) parts.push(`<div>${escapeHtml(b.action)}</div>`);
714
+ parts.push("</div>");
715
+ parts.push(versionStrip(snapshot));
716
+ const showActions = input.actions !== "none";
717
+ const actions = [];
718
+ if (showActions) {
719
+ actions.push('<div class="dsh-upd-actions">');
720
+ actions.push(
721
+ `<button type="button" data-action="check">\u67E5\u65B0\u7248</button><button type="button" data-action="install" data-primary="1"${view.installEnabled ? "" : " disabled"}>${escapeHtml(view.installLabel)}</button>`
722
+ );
723
+ if (snapshot?.latestVersion && !view.skippedLatest && view.banner.kind === "update") {
724
+ actions.push(`<button type="button" data-action="skip">\u8DF3\u8FC7\u8BE5\u7248\u672C</button>`);
725
+ }
726
+ if (view.showReset && snapshot?.latestVersion) {
727
+ actions.push(`<button type="button" data-action="reset-skip">\u6062\u590D\uFF08${escapeHtml(snapshot.latestVersion)}\uFF09</button>`);
728
+ }
729
+ if (view.showManual && manual) {
730
+ actions.push(`<button type="button" data-action="copy-manual">\u590D\u5236\u624B\u5DE5\u547D\u4EE4</button>`);
731
+ }
732
+ if (b.kind === "restart") {
733
+ actions.push(`<button type="button" data-action="restart-hint" data-primary="1">\u91CD\u542F\u5BBF\u4E3B</button>`);
734
+ }
735
+ if (snapshot) {
736
+ actions.push(`<button type="button" data-action="copy-diag">\u590D\u5236\u8BCA\u65AD</button>`);
737
+ }
738
+ if (mode === "dialog") {
739
+ actions.push(`<button type="button" data-action="close-view">\u5173\u95ED</button>`);
740
+ }
741
+ actions.push("</div>");
742
+ }
743
+ actions.push(progressBar(snapshot));
744
+ if (view.skippedLatest && snapshot?.latestVersion) {
745
+ actions.push(
746
+ `<div class="dsh-upd-skipline"><span class="dsh-upd-tag">\u5DF2\u8DF3\u8FC7 ${escapeHtml(snapshot.latestVersion)}</span>\u70B9\u300C\u6062\u590D\u300D\u53EF\u64A4\u9500\uFF0C\u4E4B\u540E\u8FD9\u4E00\u7248\u8FD8\u4F1A\u518D\u63D0\u9192\u3002</div>`
747
+ );
748
+ }
749
+ parts.push(chapterOf(1, actions.join("")));
750
+ {
751
+ let inner = "";
752
+ if (snapshot && snapshot.latestVersion) {
753
+ try {
754
+ const mdText = typeof changelogMarkdown === "string" ? changelogMarkdown : "";
755
+ const entries = parseChangelog(mdText);
756
+ const ranged = changelogForUpdate(
757
+ entries,
758
+ snapshot.runningVersion,
759
+ snapshot.latestVersion,
760
+ snapshot.installedVersion
761
+ );
762
+ const changelogHTML = renderChangelogHTML(ranged);
763
+ const fromText = String(snapshot.runningVersion ?? "");
764
+ const toText = String(snapshot.latestVersion ?? "");
765
+ const rangeTitle = fromText && toText ? `\u66F4\u65B0\u8BF4\u660E\uFF08${fromText} \u2192 ${toText}\uFF09\uFF1A` : "\u66F4\u65B0\u8BF4\u660E\uFF1A";
766
+ inner = `<div class="dsh-upd-changelog-wrap"><div>${escapeHtml(rangeTitle)}</div>
767
+ ${changelogHTML}
768
+ </div>`;
769
+ } catch {
770
+ inner = "";
771
+ }
772
+ }
773
+ if (!inner) {
774
+ inner = `<div class="dsh-upd-changelog-wrap"><div class="dsh-upd-changelog-neutral">${escapeHtml(
775
+ snapshot && snapshot.latestVersion ? "\u65E5\u5FD7\u8BFB\u4E0D\u51FA\u6765\uFF0C\u5B89\u88C5\u4E0D\u53D7\u5F71\u54CD\u3002" : "\u8FD8\u6CA1\u67E5\u5230\u65B0\u7248\uFF1B\u67E5\u5230\u540E\u518D\u663E\u793A\u65E5\u5FD7\u3002"
776
+ )}</div></div>`;
777
+ }
778
+ parts.push(chapterOf(2, inner));
779
+ }
780
+ {
781
+ const queued = !!queue && (queue.busy || queue.waiting.length > 0);
782
+ if (queued && queue) {
783
+ const busy = queue.busy;
784
+ const ownerRaw = queue.owner;
785
+ const named = ownerRaw && "pluginId" in ownerRaw && ownerRaw.pluginId ? String(ownerRaw.pluginId) : null;
786
+ const version = ownerRaw && "targetVersion" in ownerRaw && ownerRaw.targetVersion ? `@${String(ownerRaw.targetVersion)}` : "";
787
+ const aboutSelf = named !== null && named === pluginId;
788
+ const reveal = showOthers === true;
789
+ const ownerShown = !busy ? "\u7A7A\u95F2" : named === null ? "\u5176\u4ED6\u63D2\u4EF6" : aboutSelf ? "\u672C\u63D2\u4EF6" : reveal ? named : "\u5176\u4ED6\u63D2\u4EF6";
790
+ const ownerVer = busy && named !== null && (aboutSelf || reveal) ? version : "";
791
+ const pos = typeof queue.position === "number" ? queue.position : null;
792
+ const posText = pos === null ? "\u672A\u6392\u961F" : `\u7B2C ${pos} \u4F4D`;
793
+ const posNote = pos === null ? "" : pos === 1 ? "\u4E0B\u4E00\u4E2A\u5C31\u662F\u4F60" : `\u524D\u65B9 ${pos - 1} \u4E2A`;
794
+ const rows = [
795
+ `<div class="dsh-upd-qrow"><span class="dsh-upd-qdot" data-tone="${busy ? "busy" : "idle"}"></span><span class="dsh-upd-qk">\u6B63\u5728\u5B89\u88C5</span><span class="dsh-upd-qv">${escapeHtml(ownerShown + ownerVer)}</span><span class="dsh-upd-qn">${busy ? "\u88C5\u5B8C\u81EA\u52A8\u8F6E\u5230\u4F60" : "\u540C\u4E00\u4F7F\u7528\u8303\u56F4\u4E00\u6B21\u53EA\u88C5\u4E00\u4E2A"}</span></div>`,
796
+ `<div class="dsh-upd-qrow"><span class="dsh-upd-qdot" data-tone="you"></span><span class="dsh-upd-qk">\u4F60\u7684\u987A\u4F4D</span><span class="dsh-upd-qv">${escapeHtml(posText)}</span><span class="dsh-upd-qn">${escapeHtml(posNote)}</span></div>`
797
+ ];
798
+ if (reveal && queue.waiting.length > 0) {
799
+ const seq = queue.waiting.map((e) => `${e.pluginId}${e.targetVersion ? `@${e.targetVersion}` : ""}`).join(" \u2192 ");
800
+ rows.push(
801
+ `<div class="dsh-upd-qrow"><span class="dsh-upd-qdot"></span><span class="dsh-upd-qk">\u6392\u961F\u987A\u5E8F</span><span class="dsh-upd-qseq">${escapeHtml(seq)}</span></div>`
802
+ );
803
+ }
804
+ const note = showActions ? `<span class="dsh-upd-chap-note"><button type="button" data-action="toggle-queue">${reveal ? "\u9690\u85CF\u4ED6\u4EBA\u660E\u7EC6" : "\u663E\u793A\u5176\u4ED6\u63D2\u4EF6"}</button></span>` : "";
805
+ parts.push(chapterOf(3, `<div class="dsh-upd-queue">${rows.join("")}</div>`, note));
806
+ } else {
807
+ parts.push(
808
+ chapterOf(
809
+ 3,
810
+ `<div class="dsh-upd-queue"><div class="dsh-upd-changelog-neutral">${escapeHtml(
811
+ view.queueNote ?? "\u5F53\u524D\u6CA1\u6709\u6392\u961F\u4EFB\u52A1\uFF0C\u540C\u4E00\u4F7F\u7528\u8303\u56F4\u4E00\u6B21\u53EA\u88C5\u4E00\u4E2A\u3002"
812
+ )}</div></div>`
813
+ )
814
+ );
815
+ }
816
+ }
817
+ {
818
+ const errLines = [];
819
+ if (b.kind === "failed" || b.kind === "blocked") {
820
+ errLines.push(
821
+ `<div class="dsh-upd-err">\u7A33\u5B9A\u7801 <code>${escapeHtml(String(b.kind === "blocked" ? snapshot?.blockedReason ?? b.kind : "install-failed"))}</code>\uFF1A\u4E0A\u4E00\u6761\u4E2D\u6587\u8BF4\u660E\u5C31\u662F\u8981\u7528\u6237\u505A\u7684\u4E8B\uFF1B\u8981\u5F80\u4E0A\u6E38\u62A5\uFF0C\u7528\u300C\u590D\u5236\u8BCA\u65AD\u300D\u6574\u6BB5\u7C98\uFF08\u5DF2\u8131\u654F\uFF09\u3002</div>`
822
+ );
823
+ }
824
+ errLines.push(
825
+ `<div class="dsh-upd-log">\u6DF1\u6316\u770B\u65E5\u5FD7\uFF1A\u6309\u63D2\u4EF6\u6807\u8BC6 <code>${escapeHtml(pluginId)}</code> \u8FC7\u6EE4 <code>host.call</code>\u3001<code>host.call.fail</code>\u3001<code>update.install.exec</code> \u4E09\u4E2A\u4E8B\u4EF6\u3002</div>`
826
+ );
827
+ if (copyNotice) errLines.push(`<div class="dsh-upd-copy" role="status">${escapeHtml(copyNotice)}</div>`);
828
+ parts.push(chapterOf(4, errLines.join("")));
829
+ }
830
+ parts.push(
831
+ chapterOf(
832
+ 5,
833
+ view.showManual && manual ? `<div class="dsh-upd-manual"><div>\u624B\u5DE5\u515C\u5E95\u547D\u4EE4\uFF08\u590D\u5236\u6574\u884C\u6267\u884C\uFF09\uFF1A</div><code>${escapeHtml(manual)}</code></div>` : `<div class="dsh-upd-manual"><div class="dsh-upd-changelog-neutral">\u5F53\u524D\u6CA1\u6709\u53EF\u7528\u7684\u624B\u5DE5\u547D\u4EE4\uFF08\u8BA4\u4E0D\u51FA\u4F7F\u7528\u8303\u56F4\u6216\u5C5E\u6E90\u7801\u5B89\u88C5\u65F6\u4E0D\u7ED9\uFF09\u3002</div></div>`
834
+ )
835
+ );
836
+ return parts.join("\n");
837
+ }
838
+ function renderUpdatePanelHTML(input) {
839
+ const view = panelViewModel(input);
840
+ const kernel = renderUpdatePanelKernel(input, view);
841
+ const d5 = input.theme === "d5-paper";
842
+ const attr = d5 ? ' data-theme="d5-paper"' : "";
843
+ const sealAttr = ` data-seal="${escapeHtml(view.seal.text)}" data-seal-tone="${view.seal.tone}"`;
844
+ const body = input.mode === "dialog" ? `<div class="dsh-upd-overlay" data-mode="dialog"><div class="dsh-upd" data-mode="dialog" data-plugin="${escapeHtml(input.pluginId)}"${sealAttr}${attr}>
845
+ ${kernel}
846
+ </div></div>` : `<div class="dsh-upd" data-mode="embedded" data-plugin="${escapeHtml(input.pluginId)}"${sealAttr}${attr}>
847
+ ${kernel}
848
+ </div>`;
849
+ const css = d5 ? `${UPDATE_PANEL_CSS}
850
+ ${UPDATE_PANEL_D5_CSS}` : UPDATE_PANEL_CSS;
851
+ return `<style>${css}</style>
852
+ ${body}`;
853
+ }
854
+ function isObject(value) {
855
+ return !!value && typeof value === "object" && !Array.isArray(value);
856
+ }
857
+ function asSnapshot(value) {
858
+ if (!isObject(value)) return null;
859
+ const s = value;
860
+ if (typeof s["runningVersion"] !== "string") return null;
861
+ if (typeof s["canInstall"] !== "boolean") return null;
862
+ return value;
863
+ }
864
+ function asQueue(value) {
865
+ if (!isObject(value) || typeof value["busy"] !== "boolean") return null;
866
+ return value;
867
+ }
868
+ function randomRequestId() {
869
+ try {
870
+ const rand = Math.floor(Math.random() * 16777215).toString(36);
871
+ return `r${Date.now().toString(36)}${rand}`.slice(0, 64);
872
+ } catch {
873
+ return `r${Date.now()}`;
874
+ }
875
+ }
876
+ function getTimer() {
877
+ const g = globalThis;
878
+ const set = g["setInterval"];
879
+ const clear = g["clearInterval"];
880
+ if (typeof set === "function" && typeof clear === "function") {
881
+ return {
882
+ set: (fn, ms) => set(fn, ms),
883
+ clear: (h) => clear(h)
884
+ };
885
+ }
886
+ const setT = g["setTimeout"];
887
+ const clearT = g["clearTimeout"];
888
+ return {
889
+ set: (fn, ms) => setT(fn, ms),
890
+ clear: (h) => clearT(h)
891
+ };
892
+ }
893
+ async function defaultCopyText(text) {
894
+ try {
895
+ const holder = globalThis["navigator"];
896
+ const clip = holder?.clipboard;
897
+ const write = clip?.writeText;
898
+ if (clip && typeof write === "function") {
899
+ await write.call(clip, text);
900
+ return;
901
+ }
902
+ } catch {
903
+ }
904
+ }
905
+ function mountUpdatePanel(container, options) {
906
+ if (!container || typeof container.innerHTML !== "string") {
907
+ throw new Error("[dsh-plugin-update] \u6302\u8F7D\u9762\u677F\u9700\u8981\u4E00\u4E2A\u6709 innerHTML \u7684\u5BB9\u5668");
908
+ }
909
+ if (!options || typeof options !== "object") {
910
+ throw new Error("[dsh-plugin-update] \u6302\u8F7D\u9762\u677F\u7F3A\u5C11\u914D\u7F6E\uFF1A\u63D2\u4EF6\u6807\u8BC6 pluginId \u5FC5\u586B");
911
+ }
912
+ const pluginId = options.pluginId;
913
+ if (typeof pluginId !== "string" || !pluginId) {
914
+ throw new Error(`[dsh-plugin-update] \u63D2\u4EF6\u6807\u8BC6 pluginId \u5FC5\u586B\uFF1A\u987B\u4E3A\u975E\u7A7A\u5B57\u7B26\u4E32\uFF08\u6536\u5230 ${JSON.stringify(pluginId)}\uFF09`);
915
+ }
916
+ if (typeof options.call !== "function") {
917
+ throw new Error("[dsh-plugin-update] \u6302\u8F7D\u9762\u677F\u9700\u8981\u4F20\u8F93\u51FD\u6570 call\uFF08\u9762\u677F\u8C03\u5BBF\u4E3B\u7535\u8BDD\u7684\u552F\u4E00\u63A5\u89E6\u9762\uFF09");
918
+ }
919
+ const prefix = options.prefix === void 0 ? "wf" : options.prefix;
920
+ const phoneNames = buildPhoneNames(prefix);
921
+ const pollMs = options.pollMs === void 0 ? DEFAULT_PANEL_POLL_MS : options.pollMs;
922
+ if (typeof pollMs !== "number" || !Number.isFinite(pollMs) || pollMs < MIN_PANEL_POLL_MS) {
923
+ throw new Error(`[dsh-plugin-update] \u9762\u677F\u8F6E\u8BE2\u95F4\u9694\u975E\u6CD5\uFF1A\u4E0D\u5F97\u5C0F\u4E8E 250 \u6BEB\u79D2\uFF08\u6536\u5230 ${JSON.stringify(options.pollMs)}\uFF09`);
924
+ }
925
+ let mode = options.mode ?? "embedded";
926
+ if (mode !== "embedded" && mode !== "dialog") {
927
+ throw new Error(`[dsh-plugin-update] \u6446\u653E\u5F62\u6001\u975E\u6CD5\uFF1A\u53EA\u6536 embedded \u6216 dialog\uFF08\u6536\u5230 ${JSON.stringify(options.mode)}\uFF09`);
928
+ }
929
+ let theme = options.theme ?? "default";
930
+ if (theme !== "default" && theme !== "d5-paper") {
931
+ throw new Error(`[dsh-plugin-update] \u4E3B\u9898\u975E\u6CD5\uFF1A\u53EA\u6536 default \u6216 d5-paper\uFF08\u6536\u5230 ${JSON.stringify(options.theme)}\uFF09`);
932
+ }
933
+ let showOthers = options.showOthers === true;
934
+ const call = options.call;
935
+ const onRestartRequested = options.onRestartRequested;
936
+ const copyText = options.copyText ?? defaultCopyText;
937
+ const skipStore = options.skipStore ?? createBrowserSkipStore(pluginId);
938
+ const hostKind = typeof options.hostKind === "string" && options.hostKind ? options.hostKind : null;
939
+ const profileNameOption = typeof options.profileName === "string" && options.profileName ? options.profileName : null;
940
+ const diagCopyFormat = options.diagCopyFormat === "line" ? "line" : "block";
941
+ let changelogMarkdown = typeof options.changelogMarkdown === "string" ? options.changelogMarkdown : null;
942
+ let snapshot = null;
943
+ let manual = null;
944
+ let queue = null;
945
+ let receipt = null;
946
+ let requestId = null;
947
+ let lastError = null;
948
+ let lastErrorKind = null;
949
+ let lastDiag = null;
950
+ let copyNotice = null;
951
+ let mounted = true;
952
+ let envProfileName = null;
953
+ let envHostKind = null;
954
+ function queueArgs() {
955
+ return { includeQueue: true, includeEnv: true, showOthers, ...requestId ? { requestId } : {} };
956
+ }
957
+ function render() {
958
+ if (!mounted) return;
959
+ const latest = snapshot?.latestVersion ?? null;
960
+ const skippedLatest = !!latest && validReleaseVersion(latest) && skipStore.has(latest);
961
+ container.innerHTML = renderUpdatePanelHTML({
962
+ snapshot,
963
+ manual,
964
+ queue,
965
+ skippedLatest,
966
+ lastError,
967
+ errorKind: lastErrorKind,
968
+ changelogMarkdown,
969
+ mode,
970
+ showOthers,
971
+ pluginId,
972
+ copyNotice,
973
+ theme,
974
+ // 使用范围与宿主种类:调用方显式传的优先,否则用宿主回的真值。
975
+ profileName: profileNameOption ?? envProfileName,
976
+ hostKind: hostKind ?? envHostKind
977
+ });
978
+ }
979
+ function applyStatusReply(reply) {
980
+ if (!isObject(reply)) return;
981
+ if (reply["ok"] === true) {
982
+ const s = asSnapshot(reply["snapshot"]);
983
+ if (s) snapshot = s;
984
+ manual = typeof reply["manual"] === "string" ? reply["manual"] : null;
985
+ if (reply["receipt"] && isObject(reply["receipt"]) && typeof reply["receipt"]["checkId"] === "string") {
986
+ receipt = { checkId: reply["receipt"]["checkId"] };
987
+ }
988
+ const q = asQueue(reply["queue"]);
989
+ if (q) queue = q;
990
+ const env = reply["env"];
991
+ if (isObject(env)) {
992
+ const pn = env["profileName"];
993
+ const hk = env["environmentKind"];
994
+ if (typeof pn === "string" && pn) envProfileName = pn;
995
+ if (typeof hk === "string" && hk) envHostKind = hk;
996
+ }
997
+ lastError = null;
998
+ lastErrorKind = null;
999
+ lastDiag = Object.prototype.hasOwnProperty.call(reply, "diag") ? reply["diag"] : null;
1000
+ } else {
1001
+ lastError = typeof reply["error"] === "string" ? reply["error"] : "check-failed";
1002
+ lastErrorKind = typeof reply["errorKind"] === "string" && reply["errorKind"].trim() ? reply["errorKind"].trim() : null;
1003
+ lastDiag = Object.prototype.hasOwnProperty.call(reply, "diag") ? reply["diag"] : null;
1004
+ }
1005
+ }
1006
+ async function refresh() {
1007
+ if (!mounted) return;
1008
+ try {
1009
+ const reply = await call(phoneNames.updateStatus, queueArgs());
1010
+ if (!mounted) return;
1011
+ applyStatusReply(reply);
1012
+ } catch {
1013
+ if (!mounted) return;
1014
+ lastError = "check-failed";
1015
+ lastErrorKind = null;
1016
+ lastDiag = null;
1017
+ }
1018
+ render();
1019
+ }
1020
+ async function act(kind, arg) {
1021
+ if (!mounted) return;
1022
+ copyNotice = null;
1023
+ switch (kind) {
1024
+ case "check": {
1025
+ try {
1026
+ const reply = await call(phoneNames.updateCheck, queueArgs());
1027
+ if (!mounted) return;
1028
+ applyStatusReply(reply);
1029
+ } catch {
1030
+ if (!mounted) return;
1031
+ lastError = "check-failed";
1032
+ lastErrorKind = null;
1033
+ lastDiag = null;
1034
+ }
1035
+ render();
1036
+ return;
1037
+ }
1038
+ case "install": {
1039
+ try {
1040
+ if (!receipt) {
1041
+ const checked = await call(phoneNames.updateCheck, queueArgs());
1042
+ if (!mounted) return;
1043
+ applyStatusReply(checked);
1044
+ }
1045
+ if (!mounted) return;
1046
+ if (!receipt) {
1047
+ lastError = "check-expired";
1048
+ lastErrorKind = "check-expired";
1049
+ lastDiag = null;
1050
+ render();
1051
+ return;
1052
+ }
1053
+ requestId = randomRequestId();
1054
+ const reply = await call(phoneNames.updateInstall, {
1055
+ checkId: receipt.checkId,
1056
+ requestId,
1057
+ ...queueArgs()
1058
+ });
1059
+ if (!mounted) return;
1060
+ applyStatusReply(reply);
1061
+ } catch {
1062
+ if (!mounted) return;
1063
+ lastError = "check-failed";
1064
+ lastErrorKind = null;
1065
+ lastDiag = null;
1066
+ }
1067
+ render();
1068
+ return;
1069
+ }
1070
+ case "skip": {
1071
+ const v = snapshot?.latestVersion ?? null;
1072
+ if (v && validReleaseVersion(v)) {
1073
+ try {
1074
+ skipStore.skip(v);
1075
+ } catch {
1076
+ }
1077
+ }
1078
+ render();
1079
+ return;
1080
+ }
1081
+ case "reset-skip": {
1082
+ try {
1083
+ skipStore.reset(arg ?? snapshot?.latestVersion ?? void 0);
1084
+ } catch {
1085
+ }
1086
+ await refresh();
1087
+ return;
1088
+ }
1089
+ case "copy-manual": {
1090
+ if (manual) {
1091
+ try {
1092
+ await copyText(manual);
1093
+ copyNotice = "\u624B\u5DE5\u547D\u4EE4\u5DF2\u590D\u5236\uFF0C\u7C98\u5230\u7EC8\u7AEF\u6574\u884C\u6267\u884C\u5373\u53EF\u3002";
1094
+ } catch {
1095
+ copyNotice = "\u590D\u5236\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u9009\u4E2D\u4E0A\u9762\u7684\u547D\u4EE4\u3002";
1096
+ }
1097
+ }
1098
+ render();
1099
+ return;
1100
+ }
1101
+ case "copy-diag": {
1102
+ const jobCode = snapshot?.job?.state === "failed" ? messageCodeOf(snapshot.job.message) || "install-failed" : "";
1103
+ const code = failureCodeOf(
1104
+ { error: lastError, errorKind: lastErrorKind },
1105
+ jobCode || snapshot?.blockedReason || "check-failed"
1106
+ );
1107
+ const detail = snapshot?.job?.message && snapshot.job.message.includes(":") ? snapshot.job.message.slice(snapshot.job.message.indexOf(":") + 1) : snapshot?.job?.message;
1108
+ const text = buildUpdateDiagCopy({
1109
+ pluginId,
1110
+ code,
1111
+ detail,
1112
+ runningVersion: snapshot?.runningVersion ?? null,
1113
+ installedVersion: snapshot?.installedVersion ?? null,
1114
+ latestVersion: snapshot?.latestVersion ?? null,
1115
+ hostKind,
1116
+ queuePosition: queue?.position ?? null,
1117
+ requestId,
1118
+ checkId: receipt?.checkId ?? null,
1119
+ route: null,
1120
+ diag: lastDiag,
1121
+ manual,
1122
+ format: diagCopyFormat
1123
+ });
1124
+ try {
1125
+ await copyText(text);
1126
+ copyNotice = "\u8BCA\u65AD\u5DF2\u590D\u5236\uFF0C\u76F4\u63A5\u7C98\u7ED9\u63D2\u4EF6\u4F5C\u8005\u5373\u53EF\uFF08\u5DF2\u8131\u654F\uFF09\u3002";
1127
+ } catch {
1128
+ copyNotice = "\u590D\u5236\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u9009\u4E2D\u4E0A\u9762\u7684\u4FE1\u606F\u3002";
1129
+ }
1130
+ render();
1131
+ return;
1132
+ }
1133
+ case "toggle-queue": {
1134
+ showOthers = !showOthers;
1135
+ await refresh();
1136
+ return;
1137
+ }
1138
+ // 「重启宿主」:宿主没有重启自己的电话,所以只做入口——
1139
+ // 调用方给了 onRestartRequested 就交给它;没给就如实说“请手动重启”,不假装。
1140
+ case "restart-hint": {
1141
+ try {
1142
+ if (typeof onRestartRequested === "function") {
1143
+ await onRestartRequested();
1144
+ copyNotice = "\u5DF2\u6309\u8C03\u7528\u65B9\u7684\u91CD\u542F\u6D41\u7A0B\u5904\u7406\uFF1B\u91CD\u542F\u540E\u65B0\u7248\u751F\u6548\u3002";
1145
+ } else {
1146
+ copyNotice = "\u672C\u5BBF\u4E3B\u672A\u63D0\u4F9B\u91CD\u542F\u5165\u53E3\uFF1A\u8BF7\u624B\u52A8\u91CD\u542F\u5BBF\u4E3B\uFF0C\u91CD\u542F\u540E\u65B0\u7248\u751F\u6548\u3002";
1147
+ }
1148
+ } catch {
1149
+ copyNotice = "\u91CD\u542F\u5165\u53E3\u8C03\u7528\u5931\u8D25\uFF1A\u8BF7\u624B\u52A8\u91CD\u542F\u5BBF\u4E3B\uFF0C\u91CD\u542F\u540E\u65B0\u7248\u751F\u6548\u3002";
1150
+ }
1151
+ render();
1152
+ return;
1153
+ }
1154
+ case "close-view": {
1155
+ if (mode === "dialog") unmount();
1156
+ return;
1157
+ }
1158
+ }
1159
+ }
1160
+ async function setMode(next) {
1161
+ if (next !== "embedded" && next !== "dialog") {
1162
+ throw new Error(`[dsh-plugin-update] \u6446\u653E\u5F62\u6001\u975E\u6CD5\uFF1A\u53EA\u6536 embedded \u6216 dialog\uFF08\u6536\u5230 ${JSON.stringify(next)}\uFF09`);
1163
+ }
1164
+ mode = next;
1165
+ render();
1166
+ }
1167
+ async function setTheme(next) {
1168
+ if (next !== "default" && next !== "d5-paper") {
1169
+ throw new Error(`[dsh-plugin-update] \u4E3B\u9898\u975E\u6CD5\uFF1A\u53EA\u6536 default \u6216 d5-paper\uFF08\u6536\u5230 ${JSON.stringify(next)}\uFF09`);
1170
+ }
1171
+ theme = next;
1172
+ render();
1173
+ }
1174
+ async function setShowOthers(show) {
1175
+ showOthers = show === true;
1176
+ await refresh();
1177
+ }
1178
+ function onClick(ev) {
1179
+ try {
1180
+ const t = ev;
1181
+ const btn = t?.target && typeof t.target.closest === "function" ? t.target.closest("[data-action]") : null;
1182
+ const kind = btn?.getAttribute ? btn.getAttribute("data-action") : null;
1183
+ if (!kind) return;
1184
+ void act(kind);
1185
+ } catch {
1186
+ }
1187
+ }
1188
+ function setChangelogMarkdown(markdown) {
1189
+ changelogMarkdown = typeof markdown === "string" ? markdown : null;
1190
+ render();
1191
+ }
1192
+ function unmount() {
1193
+ if (!mounted) return;
1194
+ mounted = false;
1195
+ try {
1196
+ timer.clear(handle);
1197
+ } catch {
1198
+ }
1199
+ try {
1200
+ container.removeEventListener?.("click", onClick);
1201
+ } catch {
1202
+ }
1203
+ }
1204
+ render();
1205
+ try {
1206
+ container.addEventListener?.("click", onClick);
1207
+ } catch {
1208
+ }
1209
+ const timer = getTimer();
1210
+ const handle = timer.set(() => {
1211
+ void refresh();
1212
+ }, pollMs);
1213
+ try {
1214
+ const h = handle;
1215
+ if (h && typeof h.unref === "function") h.unref();
1216
+ } catch {
1217
+ }
1218
+ void refresh();
1219
+ return { refresh, act, setMode, setTheme, setShowOthers, setChangelogMarkdown, unmount };
1220
+ }
1221
+ import { buildPhoneNames as buildPhoneNames2 } from "./config.js";
1222
+ const PANEL_POLL = {
1223
+ defaultMs: DEFAULT_PANEL_POLL_MS,
1224
+ minMs: MIN_PANEL_POLL_MS
1225
+ };
1226
+ import {
1227
+ QUEUE_INTENT_TTL_MS,
1228
+ emptyQueueState,
1229
+ normalizeQueueState,
1230
+ queuePositionOf,
1231
+ isHeadOfQueue,
1232
+ isQueueBusy,
1233
+ visibleQueueFor as visibleQueueFor2
1234
+ } from "./queue.js";
1235
+ import {
1236
+ CHANGELOG_ALL_CATEGORIES,
1237
+ CHANGELOG_FILENAME,
1238
+ CHANGELOG_FOLDED,
1239
+ CHANGELOG_MAX_BULLETS_PER_SECTION,
1240
+ CHANGELOG_MAX_BULLET_CHARS,
1241
+ CHANGELOG_MAX_CHARS,
1242
+ CHANGELOG_MAX_ENTRIES,
1243
+ CHANGELOG_MUST_SHOW,
1244
+ CHANGELOG_NEUTRAL_HINT,
1245
+ CHANGELOG_NEUTRAL_LINE,
1246
+ changelogForUpdate as changelogForUpdate2,
1247
+ hasVisibleSections,
1248
+ isUnreleasedVersion,
1249
+ parseChangelog as parseChangelog2,
1250
+ renderChangelogHTML as renderChangelogHTML2,
1251
+ renderChangelogNeutral,
1252
+ renderChangelogSection,
1253
+ selectChangelogEntries
1254
+ } from "./changelog.js";
1255
+ export {
1256
+ CHANGELOG_ALL_CATEGORIES,
1257
+ CHANGELOG_FILENAME,
1258
+ CHANGELOG_FOLDED,
1259
+ CHANGELOG_MAX_BULLETS_PER_SECTION,
1260
+ CHANGELOG_MAX_BULLET_CHARS,
1261
+ CHANGELOG_MAX_CHARS,
1262
+ CHANGELOG_MAX_ENTRIES,
1263
+ CHANGELOG_MUST_SHOW,
1264
+ CHANGELOG_NEUTRAL_HINT,
1265
+ CHANGELOG_NEUTRAL_LINE,
1266
+ PANEL_DIAG_MAX_CHARS,
1267
+ PANEL_POLL,
1268
+ QUEUE_INTENT_TTL_MS,
1269
+ UPDATE_PANEL_CSS,
1270
+ UPDATE_PANEL_D5_CSS,
1271
+ blockedCopy,
1272
+ buildDiagnosticText,
1273
+ buildPhoneNames2 as buildPanelPhoneNames,
1274
+ buildUpdateDiagCopy,
1275
+ changelogForUpdate2 as changelogForUpdate,
1276
+ createBrowserSkipStore,
1277
+ createMemorySkipStore,
1278
+ emptyQueueState,
1279
+ failureCodeOf,
1280
+ failureCopy,
1281
+ hasVisibleSections,
1282
+ isHeadOfQueue,
1283
+ isKnownFailureCode,
1284
+ isQueueBusy,
1285
+ isUnreleasedVersion,
1286
+ mountUpdatePanel,
1287
+ normalizeQueueState,
1288
+ panelViewModel,
1289
+ parseChangelog2 as parseChangelog,
1290
+ queuePositionOf,
1291
+ readDiagTolerant,
1292
+ redactForCopy,
1293
+ renderChangelogHTML2 as renderChangelogHTML,
1294
+ renderChangelogNeutral,
1295
+ renderChangelogSection,
1296
+ renderUpdatePanelHTML,
1297
+ renderUpdatePanelKernel,
1298
+ selectChangelogEntries,
1299
+ visibleQueueFor2 as visibleQueueFor
1300
+ };