dsh-plugin-update 0.5.8 → 0.7.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 CHANGED
@@ -10,92 +10,156 @@ import {
10
10
  yankedBannerHTML
11
11
  } from "./changelog.js";
12
12
  import { LOG_EVENT_CALL, LOG_EVENT_CALL_FAIL, LOG_EVENT_INSTALL_EXEC } from "./log-events.js";
13
+ import { copyText } from "./bilingual.js";
14
+ import { normalizeLangTag, resolveLang, subscribeLang } from "./lang.js";
13
15
  function normalizePanelTheme(value) {
14
- return value === "archive" || value === "d5-paper" ? "archive" : "default";
16
+ return value === "archive" ? "archive" : "default";
15
17
  }
16
- const BLOCKED_COPY = {
17
- "unknown-profile": {
18
- title: "\u4F7F\u7528\u8303\u56F4\u6216\u63D2\u4EF6\u4F4D\u7F6E\u8BA4\u4E0D\u51FA",
19
- 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"
20
- },
21
- "source-install": {
22
- title: "\u5F53\u524D\u662F\u4ECE\u6E90\u7801\u88C5\u7684\uFF0C\u4E0D\u662F\u6309\u7248\u672C\u53F7\u88C5\u7684",
23
- 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"
24
- },
25
- "invalid-installation": {
26
- 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",
27
- action: "\u91CD\u88C5\u5F53\u524D\u7248\u672C\uFF0C\u4FEE\u597D\u5DF2\u88C5\u76EE\u5F55\u518D\u67E5\u66F4\u65B0"
28
- },
29
- "installation-changed": {
30
- title: "\u5B89\u88C5\u4F4D\u7F6E\u5728\u4F7F\u7528\u4E2D\u9014\u53D8\u4E86\uFF08\u6362\u4E86\u76EE\u5F55\u6216\u6362\u4E86\u5305\uFF09",
31
- action: "\u91CD\u65B0\u6253\u5F00\u5BBF\u4E3B\u518D\u67E5\u4E00\u6B21\uFF1B\u8FD8\u51FA\u73B0\u5C31\u91CD\u88C5"
32
- },
33
- "pending-restart": {
34
- title: "\u65B0\u7248\u5DF2\u88C5\u5230\u78C1\u76D8\uFF0C\u6B63\u5728\u8DD1\u7684\u8FD8\u662F\u65E7\u7248",
35
- action: "\u91CD\u542F\u5BBF\u4E3B\uFF0C\u8BA9\u65B0\u7248\u8DD1\u8D77\u6765\uFF1B\u8FD9\u662F\u6B63\u5E38\u7EC8\u6001\uFF0C\u4E0D\u662F\u5931\u8D25"
36
- },
37
- "registry-conflict": {
38
- title: "\u672C\u5730\u58F0\u660E\u7684\u7248\u672C\u4E0E\u78C1\u76D8\u5B9E\u9645\u7248\u672C\u4E92\u76F8\u77DB\u76FE",
39
- 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"
40
- },
41
- "incompatible-node": {
42
- title: "\u65B0\u7248\u8981\u6C42\u7684 Node \u4E0E\u5F53\u524D\u8FD0\u884C\u7684\u5BF9\u4E0D\u4E0A",
43
- action: "\u5148\u5347\u7EA7 Node \u5230 22 \u6216\u66F4\u9AD8\uFF0C\u518D\u67E5\u66F4\u65B0"
44
- },
45
- "recovery-required": {
46
- title: "\u4E0A\u6B21\u5B89\u88C5\u88AB\u6253\u65AD\uFF0C\u7559\u4E0B\u4E00\u4E2A\u534A\u622A\u4EFB\u52A1",
47
- action: "\u91CD\u65B0\u70B9\u4E00\u6B21\u5B89\u88C5\uFF1B\u4E00\u76F4\u51FA\u73B0\u5C31\u6309\u7B2C 6 \u8282\u6392\u9519"
18
+ const THEME_TOKEN_VARS = {
19
+ text: "--dsh-update-text",
20
+ textMuted: "--dsh-update-text-muted",
21
+ bg: "--dsh-update-bg",
22
+ bgSoft: "--dsh-update-bg-soft",
23
+ border: "--dsh-update-border",
24
+ borderStrong: "--dsh-update-border-strong",
25
+ buttonBg: "--dsh-update-button-bg",
26
+ primary: "--dsh-update-primary",
27
+ primaryDeep: "--dsh-update-primary-deep",
28
+ focus: "--dsh-update-focus",
29
+ okBg: "--dsh-update-ok-bg",
30
+ okBorder: "--dsh-update-ok-border",
31
+ okText: "--dsh-update-ok-text",
32
+ warnBg: "--dsh-update-warn-bg",
33
+ warnBorder: "--dsh-update-warn-border",
34
+ warnText: "--dsh-update-warn-text",
35
+ badBg: "--dsh-update-bad-bg",
36
+ badBorder: "--dsh-update-bad-border",
37
+ badText: "--dsh-update-bad-text",
38
+ busyBg: "--dsh-update-busy-bg",
39
+ busyBorder: "--dsh-update-busy-border",
40
+ busyText: "--dsh-update-busy-text",
41
+ fontSans: "--dsh-update-font-sans",
42
+ fontSerif: "--dsh-update-font-serif",
43
+ fontMono: "--dsh-update-font-mono",
44
+ shadow: "--dsh-update-shadow",
45
+ radiusPanel: "--dsh-update-radius-panel",
46
+ radiusButton: "--dsh-update-radius-button",
47
+ radiusBadge: "--dsh-update-radius-badge",
48
+ entryFontSize: "--dsh-update-entry-font-size",
49
+ entryPadding: "--dsh-update-entry-padding",
50
+ entryBorderRadius: "--dsh-update-entry-border-radius",
51
+ entryScale: "--dsh-update-entry-scale"
52
+ };
53
+ const THEME_TOKEN_COLOR_KEYS = /* @__PURE__ */ new Set([
54
+ "text",
55
+ "textMuted",
56
+ "bg",
57
+ "bgSoft",
58
+ "border",
59
+ "borderStrong",
60
+ "buttonBg",
61
+ "primary",
62
+ "primaryDeep",
63
+ "focus",
64
+ "okBg",
65
+ "okBorder",
66
+ "okText",
67
+ "warnBg",
68
+ "warnBorder",
69
+ "warnText",
70
+ "badBg",
71
+ "badBorder",
72
+ "badText",
73
+ "busyBg",
74
+ "busyBorder",
75
+ "busyText"
76
+ ]);
77
+ function themeTokensError(raw) {
78
+ return new Error(`[dsh-plugin-update] \u4E3B\u9898\u53C2\u6570 themeTokens \u975E\u6CD5\uFF1A\u53EA\u6536\u5DF2\u77E5 token \u952E\uFF08\u989C\u8272\u7528 hex \u6216\u82F1\u6587\u540D\uFF0C\u5B57\u4F53/\u5706\u89D2/\u9634\u5F71/\u5C3A\u5BF8\u4E3A\u5B89\u5168 CSS \u503C\uFF0CentryScale \u4E3A\u5927\u4E8E 0 \u7684\u6709\u9650\u6570\uFF09\uFF08\u6536\u5230 ${JSON.stringify(raw ?? null)})`);
79
+ }
80
+ function isSafeThemeCssValue(value) {
81
+ const v = value.trim();
82
+ if (!v || v.length > 200) return false;
83
+ if (/[;"'<>\`{}!&]/.test(v)) return false;
84
+ if (/url\s*\(/i.test(v)) return false;
85
+ if (/expression\s*\(/i.test(v)) return false;
86
+ if (/javascript\s*:/i.test(v)) return false;
87
+ return true;
88
+ }
89
+ function isThemeColorValue(value) {
90
+ const v = value.trim();
91
+ if (!v || v.length > 100) return false;
92
+ return /^(?:#[0-9a-fA-F]{3}|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{8}|[a-zA-Z]+)$/.test(v);
93
+ }
94
+ function themeTokensStyleFor(tokens) {
95
+ if (tokens === void 0 || tokens === null) return "";
96
+ if (typeof tokens !== "object" || Array.isArray(tokens)) throw themeTokensError(tokens);
97
+ for (const key of Object.keys(tokens)) {
98
+ if (!Object.prototype.hasOwnProperty.call(THEME_TOKEN_VARS, key)) throw themeTokensError(tokens);
48
99
  }
100
+ const parts = [];
101
+ for (const key of Object.keys(THEME_TOKEN_VARS)) {
102
+ const value = tokens[key];
103
+ if (value === void 0) continue;
104
+ if (key === "entryScale") {
105
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) throw themeTokensError(tokens);
106
+ parts.push(THEME_TOKEN_VARS[key] + ":" + String(value));
107
+ continue;
108
+ }
109
+ if (typeof value !== "string") throw themeTokensError(tokens);
110
+ const text = value.trim();
111
+ if (THEME_TOKEN_COLOR_KEYS.has(key)) {
112
+ if (!isThemeColorValue(text)) throw themeTokensError(tokens);
113
+ } else if (!isSafeThemeCssValue(text)) throw themeTokensError(tokens);
114
+ parts.push(THEME_TOKEN_VARS[key] + ":" + text);
115
+ }
116
+ return parts.join(";");
117
+ }
118
+ const BLOCKED_KEYS = {
119
+ "unknown-profile": { title: "panel.blocked.unknown-profile.title", action: "panel.blocked.unknown-profile.action" },
120
+ "source-install": { title: "panel.blocked.source-install.title", action: "panel.blocked.source-install.action" },
121
+ "invalid-installation": { title: "panel.blocked.invalid-installation.title", action: "panel.blocked.invalid-installation.action" },
122
+ "installation-changed": { title: "panel.blocked.installation-changed.title", action: "panel.blocked.installation-changed.action" },
123
+ "pending-restart": { title: "panel.blocked.pending-restart.title", action: "panel.blocked.pending-restart.action" },
124
+ "registry-conflict": { title: "panel.blocked.registry-conflict.title", action: "panel.blocked.registry-conflict.action" },
125
+ "incompatible-node": { title: "panel.blocked.incompatible-node.title", action: "panel.blocked.incompatible-node.action" },
126
+ "recovery-required": { title: "panel.blocked.recovery-required.title", action: "panel.blocked.recovery-required.action" }
49
127
  };
50
- function blockedCopy(reason) {
128
+ function blockedCopy(reason, lang) {
51
129
  if (reason === null || reason === void 0) return null;
52
- return BLOCKED_COPY[reason] ?? null;
130
+ const keys = BLOCKED_KEYS[reason];
131
+ if (!keys) return null;
132
+ const l = normalizeLangTag(lang ?? "zh");
133
+ return { title: copyText(keys.title, l), action: copyText(keys.action, l) };
53
134
  }
54
- const PHONE_FAILURE_COPY = {
55
- "check-failed": {
56
- zh: "\u67E5\u65B0\u7248\u6CA1\u6210\u529F\uFF08\u8054\u7F51\u3001\u6E90\u3001\u9650\u6D41\u90FD\u53EF\u80FD\uFF09",
57
- 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"
58
- },
59
- "invalid-release": {
60
- zh: "\u62FF\u5230\u7684\u53D1\u5E03\u4FE1\u606F\u4E0D\u5408\u6CD5\uFF08\u7248\u672C\u53F7\u975E\u6CD5\u6216\u5185\u5BB9\u5BF9\u4E0D\u4E0A\uFF09",
61
- act: "\u68C0\u67E5\u6E05\u5355\u6587\u4EF6\u91CC\u7684\u5305\u540D\u4E0E\u7248\u672C\u5199\u6CD5\uFF0C\u518D\u67E5\u4E00\u6B21"
62
- },
63
- "check-expired": {
64
- zh: "\u51ED\u8BC1\u8FC7\u671F\u4E86\uFF0C\u5B89\u88C5\u8BF7\u6C42\u88AB\u62D2",
65
- act: "\u91CD\u65B0\u67E5\u4E00\u6B21\u65B0\u7248\u518D\u70B9\u5B89\u88C5\uFF0C\u4E0D\u8981\u91CD\u8BD5\u65E7\u7F16\u53F7"
66
- },
67
- "update-busy": {
68
- zh: "\u540C\u4E00\u4F7F\u7528\u8303\u56F4\u6B63\u5728\u88C5\u53E6\u4E00\u4E2A",
69
- act: "\u7B49\u5F53\u524D\u4EFB\u52A1\u79BB\u5F00 installing/verifying \u518D\u70B9\uFF1B\u6392\u961F\u4E2D\u53BB\u67E5\u72B6\u6001\u770B\u4F4D\u7F6E"
70
- },
71
- "install-failed": {
72
- zh: "\u88C5\u4E0D\u4E0A\uFF08\u8BE6\u89C1\u8BCA\u65AD\u6458\u8981\uFF09",
73
- act: "\u5148\u770B\u590D\u5236\u8BCA\u65AD\uFF1B\u5B98\u65B9\u684C\u9762\u7248\u628A\u8FD9\u6BB5\u4EA4\u7ED9\u63D2\u4EF6\u4F5C\u8005"
74
- },
75
- internal: {
76
- zh: "\u51FA\u4E86\u70B9\u95EE\u9898\uFF0C\u8BA4\u4E0D\u51FA\u5177\u4F53\u539F\u56E0",
77
- act: "\u5148\u91CD\u8BD5\u4E00\u6B21\uFF1B\u4E00\u76F4\u8FD9\u6837\u5C31\u628A\u590D\u5236\u8BCA\u65AD\u4EA4\u7ED9\u63D2\u4EF6\u4F5C\u8005"
78
- }
135
+ const PHONE_FAILURE_KEYS = {
136
+ "check-failed": { title: "panel.failure.check-failed.title", action: "panel.failure.check-failed.action" },
137
+ "invalid-release": { title: "panel.failure.invalid-release.title", action: "panel.failure.invalid-release.action" },
138
+ "check-expired": { title: "panel.failure.check-expired.title", action: "panel.failure.check-expired.action" },
139
+ "update-busy": { title: "panel.failure.update-busy.title", action: "panel.failure.update-busy.action" },
140
+ "install-failed": { title: "panel.failure.install-failed.title", action: "panel.failure.install-failed.action" },
141
+ internal: { title: "panel.failure.internal.title", action: "panel.failure.internal.action" }
79
142
  };
80
- const UNKNOWN_FAILURE_COPY = {
81
- zh: "\u51FA\u4E86\u70B9\u95EE\u9898\uFF0C\u8BA4\u4E0D\u51FA\u5177\u4F53\u539F\u56E0",
82
- 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"
143
+ const UNKNOWN_FAILURE_KEYS = {
144
+ title: "panel.failure.unknown.title",
145
+ action: "panel.failure.unknown.action"
83
146
  };
84
147
  function isKnownFailureCode(code) {
85
148
  if (typeof code !== "string" || !code) return false;
86
149
  const c = code.trim();
87
150
  if (!c) return false;
88
- return c in BLOCKED_COPY || c in PHONE_FAILURE_COPY;
151
+ return c in BLOCKED_KEYS || c in PHONE_FAILURE_KEYS;
89
152
  }
90
- function failureCopy(code) {
153
+ function failureCopy(code, lang) {
91
154
  if (typeof code !== "string") return null;
92
155
  const c = code.trim();
93
156
  if (!c) return null;
94
- const blocked = BLOCKED_COPY[c];
95
- if (blocked) return { zh: blocked.title, act: blocked.action };
96
- const phone = PHONE_FAILURE_COPY[c];
97
- if (phone) return phone;
98
- return UNKNOWN_FAILURE_COPY;
157
+ const l = normalizeLangTag(lang ?? "zh");
158
+ const blocked = BLOCKED_KEYS[c];
159
+ if (blocked) return { zh: copyText(blocked.title, l), act: copyText(blocked.action, l) };
160
+ const phone = PHONE_FAILURE_KEYS[c];
161
+ if (phone) return { zh: copyText(phone.title, l), act: copyText(phone.action, l) };
162
+ return { zh: copyText(UNKNOWN_FAILURE_KEYS.title, l), act: copyText(UNKNOWN_FAILURE_KEYS.action, l) };
99
163
  }
100
164
  function failureCodeOf(reply, fallback) {
101
165
  const kind = reply && typeof reply.errorKind === "string" ? reply.errorKind.trim() : "";
@@ -109,24 +173,27 @@ const PANEL_DIAG_MAX_CHARS = COPY_BUDGET_CHARS;
109
173
  function redactForCopy(text) {
110
174
  return sanitizeForCopy(text);
111
175
  }
112
- function buildDiagnosticText(input) {
176
+ function buildDiagnosticText(input, langOverride) {
177
+ const l = normalizeLangTag(langOverride ?? input.lang ?? "zh");
178
+ const unknown = copyText("panel.diag.copy.unknown", l);
113
179
  const code = String(input.code || "check-failed");
114
- const lines = [`[\u66F4\u65B0\u8BCA\u65AD] ${String(input.pluginId)} \u7A33\u5B9A\u7801\uFF1A${code}`];
180
+ const lines = [copyText("panel.diag.header", l, { pluginId: String(input.pluginId), code })];
115
181
  const detail = redactForCopy(input.detail ?? "");
116
- lines.push(`\u4EBA\u8BDD\uFF1A${detail || failureCopy(code)?.act || code}`);
182
+ const fallbackAct = failureCopy(code, l)?.act || code;
183
+ lines.push(copyText("panel.diag.label.human", l, { detail: detail || fallbackAct }));
117
184
  const versions = [
118
- `\u8FD0\u884C\u7248\uFF1A${input.runningVersion ?? "\u672A\u77E5"}`,
119
- `\u5DF2\u88C5\uFF1A${input.installedVersion ?? "\u672A\u77E5"}`,
120
- `\u8FDC\u7AEF\uFF1A${input.latestVersion ?? "\u672A\u67E5\u8FC7"}`
185
+ copyText("panel.diag.label.running", l, { version: input.runningVersion ?? unknown }),
186
+ copyText("panel.diag.label.installed", l, { version: input.installedVersion ?? unknown }),
187
+ copyText("panel.diag.label.latest", l, { version: input.latestVersion ?? (l === "en" ? "Not checked" : "\u672A\u67E5\u8FC7") })
121
188
  ].join(" / ");
122
189
  lines.push(versions);
123
- const queue = input.queuePosition === 0 ? "\u6B63\u5728\u5B89\u88C5" : typeof input.queuePosition === "number" ? `\u6392\u961F\u7B2C ${input.queuePosition} \u4F4D` : "\u4E0D\u5728\u961F\u5217\u91CC";
124
- const hostLine = [`\u5BBF\u4E3B\uFF1A${input.hostKind ?? "\u672A\u77E5"}`];
125
- if (typeof input.profileName === "string" && input.profileName) hostLine.push(`\u4F7F\u7528\u8303\u56F4\uFF1A${input.profileName}`);
126
- hostLine.push(`\u961F\u5217\uFF1A${queue}`);
127
- lines.push(hostLine.join(" / ") + (input.requestId ? ` / \u8BF7\u6C42\u7F16\u53F7\uFF1A${input.requestId}` : ""));
190
+ const queue = queueTextOf(input.queuePosition ?? null, l);
191
+ const hostLine = [copyText("panel.diag.label.host", l, { host: input.hostKind ?? unknown })];
192
+ if (typeof input.profileName === "string" && input.profileName) hostLine.push(copyText("panel.diag.label.profile", l, { profile: input.profileName }));
193
+ hostLine.push(copyText("panel.diag.label.queue", l, { queue }));
194
+ lines.push(hostLine.join(" / ") + (input.requestId ? ` / ${copyText("panel.diag.label.request", l, { requestId: input.requestId })}` : ""));
128
195
  const manual = redactForCopy(input.manual ?? "");
129
- if (manual) lines.push(`\u624B\u5DE5\u547D\u4EE4\uFF1A${manual}`);
196
+ if (manual) lines.push(copyText("panel.diag.label.manual", l, { manual }));
130
197
  return redactForCopy(lines.join("\n"));
131
198
  }
132
199
  const DIAG_KNOWN_TYPES = {
@@ -185,52 +252,56 @@ function pickText(value, fallback) {
185
252
  if (typeof value === "string" && value.trim()) return value.trim();
186
253
  return fallback;
187
254
  }
188
- function queueTextOf(queuePosition, _diagQueuePos) {
255
+ function queueTextOf(queuePosition, lang, _diagQueuePos) {
189
256
  void _diagQueuePos;
257
+ const l = normalizeLangTag(lang ?? "zh");
190
258
  const pos = typeof queuePosition === "number" ? queuePosition : null;
191
- if (pos === 0) return "\u6B63\u5728\u5B89\u88C5";
192
- if (typeof pos === "number") return `\u6392\u961F\u7B2C ${pos} \u4F4D`;
193
- return "\u4E0D\u5728\u961F\u5217\u91CC";
259
+ if (pos === 0) return copyText("panel.diag.queue.installing", l);
260
+ if (typeof pos === "number") return copyText("panel.diag.queue.position", l, { n: pos });
261
+ return copyText("panel.diag.queue.absent", l);
194
262
  }
195
- function buildUpdateDiagCopy(input) {
263
+ function buildUpdateDiagCopy(input, langOverride) {
264
+ const l = normalizeLangTag(langOverride ?? input.lang ?? "zh");
265
+ const unknown = copyText("panel.diag.copy.unknown", l);
266
+ const unknownPkg = copyText("panel.diag.copy.unknown-package", l);
196
267
  const rawCode = String(input.code ?? "").trim() || "internal";
197
- const copy = failureCopy(rawCode) ?? UNKNOWN_FAILURE_COPY;
268
+ const copy = failureCopy(rawCode, l) ?? failureCopy("unknown", l);
198
269
  const diag = readDiagTolerant(input.diag);
199
270
  const detailRaw = diag.detail ?? input.detail ?? "";
200
- 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";
201
- const pluginName = pickText(diag.targetPackageName ?? input.pluginId, "(\u672A\u77E5\u5305)");
271
+ const detail = redactForCopy(detailRaw) || copyText("panel.diag.copy.no-detail", l);
272
+ const pluginName = pickText(diag.targetPackageName ?? input.pluginId, unknownPkg);
202
273
  const runVer = pickText(diag.runningVersion ?? input.runningVersion, "?");
203
274
  const instVer = pickText(input.installedVersion ?? diag.latestVersion, "?");
204
- const host = pickText(diag.environmentKind ?? input.hostKind, "\u672A\u77E5");
275
+ const host = pickText(diag.environmentKind ?? input.hostKind, unknown);
205
276
  const routeRaw = diag.route ?? input.route;
206
277
  const requestRaw = diag.requestId ?? input.requestId;
207
278
  const checkRaw = diag.checkId ?? input.checkId;
208
- const queue = queueTextOf(input.queuePosition ?? null);
209
- const prov = [`\u63D2\u4EF6=${pluginName}`, `\u7248\u672C=${runVer}\u2192${instVer}`, `\u5BBF\u4E3B=${host}`];
210
- prov.push(`\u4F7F\u7528\u8303\u56F4=${pickText(input.profileName, "\u672A\u77E5")}`);
211
- if (typeof routeRaw === "string" && routeRaw.trim()) prov.push(`\u8DEF\u7531=${routeRaw.trim()}`);
212
- if (diag.stage) prov.push(`\u9636\u6BB5=${diag.stage}`);
213
- if (diag.method) prov.push(`\u65B9\u6CD5=${diag.method}`);
279
+ const queue = queueTextOf(input.queuePosition ?? null, l);
280
+ const prov = [copyText("panel.diag.copy.field.plugin", l, { plugin: pluginName }), copyText("panel.diag.copy.field.version", l, { run: runVer, inst: instVer }), copyText("panel.diag.copy.field.host", l, { host })];
281
+ prov.push(copyText("panel.diag.copy.field.profile", l, { profile: pickText(input.profileName, unknown) }));
282
+ if (typeof routeRaw === "string" && routeRaw.trim()) prov.push(copyText("panel.diag.copy.field.route", l, { route: routeRaw.trim() }));
283
+ if (diag.stage) prov.push(copyText("panel.diag.copy.field.stage", l, { stage: diag.stage }));
284
+ if (diag.method) prov.push(copyText("panel.diag.copy.field.method", l, { method: diag.method }));
214
285
  if (typeof diag.httpStatus === "number") prov.push(`HTTP=${diag.httpStatus}`);
215
286
  if (typeof diag.exitCode === "number") prov.push(`exit=${diag.exitCode}`);
216
- if (typeof diag.latencyMs === "number") prov.push(`\u8017\u65F6=${diag.latencyMs}ms`);
217
- if (diag.registryHost) prov.push(`\u6E90=${diag.registryHost}`);
218
- else prov.push("\u6E90=\u672A\u77E5\uFF08\u975E\u5B98\u65B9\u6E90\u65F6\u7701\u7565\u672C\u8EAB\u5373\u4FE1\u606F\uFF09");
219
- if (diag.action) prov.push(`\u5EFA\u8BAE=${diag.action}`);
220
- if (typeof requestRaw === "string" && requestRaw.trim()) prov.push(`\u8BF7\u6C42=${requestRaw.trim()}`);
221
- if (typeof checkRaw === "string" && checkRaw.trim()) prov.push(`\u68C0\u67E5=${checkRaw.trim()}`);
222
- prov.push(`\u961F\u5217=${queue}`);
287
+ if (typeof diag.latencyMs === "number") prov.push(`${copyText("panel.diag.copy.field.latency", l, { latency: diag.latencyMs })}ms`);
288
+ if (diag.registryHost) prov.push(copyText("panel.diag.copy.field.registry", l, { host: diag.registryHost }));
289
+ else prov.push(copyText("panel.diag.copy.field.registry-unknown", l));
290
+ if (diag.action) prov.push(copyText("panel.diag.copy.field.action", l, { action: diag.action }));
291
+ if (typeof requestRaw === "string" && requestRaw.trim()) prov.push(copyText("panel.diag.copy.field.request", l, { request: requestRaw.trim() }));
292
+ if (typeof checkRaw === "string" && checkRaw.trim()) prov.push(copyText("panel.diag.copy.field.check", l, { check: checkRaw.trim() }));
293
+ prov.push(copyText("panel.diag.copy.field.queue", l, { queue }));
223
294
  const format = input.format === "line" ? "line" : "block";
224
295
  const provLine = redactForCopy(prov.join(" \xB7 "));
225
296
  if (format === "line") {
226
- const line = `[update-diag] code=${rawCode} \xB7 ${copy.zh} \xB7 \u6458\u8981=${detail} \xB7 ${provLine} \xB7 \u600E\u4E48\u529E=${copy.act}`;
297
+ const line = `[update-diag] code=${rawCode} \xB7 ${copy.zh} \xB7 ${copyText("panel.diag.copy.line.summary", l, { summary: detail })} \xB7 ${provLine} \xB7 ${copyText("panel.diag.copy.line.remedy", l, { remedy: copy.act })}`;
227
298
  return redactForCopy(line);
228
299
  }
229
300
  const block = [
230
301
  `[update-diag] ${rawCode} \u2014 ${copy.zh}`,
231
- ` \u6458\u8981\uFF1A${detail}`,
232
- ` \u6765\u6E90\uFF1A${provLine}`,
233
- ` \u600E\u4E48\u529E\uFF1A${copy.act}`
302
+ ` ${copyText("panel.diag.copy.block.summary", l, { summary: detail })}`,
303
+ ` ${copyText("panel.diag.copy.block.source", l, { source: provLine })}`,
304
+ ` ${copyText("panel.diag.copy.block.remedy", l, { remedy: copy.act })}`
234
305
  ].join("\n");
235
306
  return block;
236
307
  }
@@ -331,40 +402,47 @@ function createBrowserSkipStore(pluginId, storage, now = Date.now) {
331
402
  }
332
403
  };
333
404
  }
334
- const SEAL_BY_KIND = {
335
- loading: { text: "\u5F85\u67E5", mini: "\u67E5", tone: "ink" },
336
- idle: { text: "\u5F85\u67E5", mini: "\u67E5", tone: "ink" },
337
- update: { text: "\u53EF\u88C5", mini: "\u88C5", tone: "green" },
338
- busy: { text: "\u5B89\u88C5\u4E2D", mini: "\u88C5", tone: "yellow" },
339
- restart: { text: "\u5F85\u91CD\u542F", mini: "\u542F", tone: "yellow" },
340
- blocked: { text: "\u53D7\u963B", mini: "\u963B", tone: "red" },
341
- failed: { text: "\u53D7\u963B", mini: "\u963B", tone: "red" },
342
- done: { text: "\u5DF2\u6700\u65B0", mini: "\u5B9A", tone: "green" }
405
+ const SEAL_KEYS = {
406
+ loading: { text: "panel.seal.loading.text", mini: "panel.seal.loading.mini", tone: "ink" },
407
+ idle: { text: "panel.seal.idle.text", mini: "panel.seal.idle.mini", tone: "ink" },
408
+ update: { text: "panel.seal.update.text", mini: "panel.seal.update.mini", tone: "green" },
409
+ busy: { text: "panel.seal.busy.text", mini: "panel.seal.busy.mini", tone: "yellow" },
410
+ restart: { text: "panel.seal.restart.text", mini: "panel.seal.restart.mini", tone: "yellow" },
411
+ blocked: { text: "panel.seal.blocked.text", mini: "panel.seal.blocked.mini", tone: "red" },
412
+ failed: { text: "panel.seal.failed.text", mini: "panel.seal.failed.mini", tone: "red" },
413
+ done: { text: "panel.seal.done.text", mini: "panel.seal.done.mini", tone: "green" }
343
414
  };
415
+ function panelSealFor(kind, lang) {
416
+ const keys = SEAL_KEYS[kind] ?? SEAL_KEYS.idle;
417
+ const l = normalizeLangTag(lang ?? "zh");
418
+ return { text: copyText(keys.text, l), mini: copyText(keys.mini, l), tone: keys.tone };
419
+ }
344
420
  function messageCodeOf(message) {
345
421
  if (typeof message !== "string" || !message) return "";
346
422
  const at = message.indexOf(":");
347
423
  return (at < 0 ? message : message.slice(0, at)).trim();
348
424
  }
349
- function panelViewModel(input) {
350
- const view = panelViewModelCore(input);
351
- return { ...view, seal: SEAL_BY_KIND[view.banner.kind] ?? SEAL_BY_KIND.idle };
425
+ function panelViewModel(input, lang) {
426
+ const l = normalizeLangTag(lang ?? input.lang ?? "zh");
427
+ const view = panelViewModelCore(input, l);
428
+ return { ...view, seal: panelSealFor(view.banner.kind, l) };
352
429
  }
353
- function panelViewModelCore(input) {
430
+ function panelViewModelCore(input, lang) {
354
431
  const { snapshot, manual, queue, skippedLatest, lastError } = input;
355
432
  const errorKind = input.errorKind;
433
+ const l = normalizeLangTag(lang ?? "zh");
356
434
  if (!snapshot) {
357
435
  const earlyCode = typeof errorKind === "string" && errorKind.trim() || lastError || "";
358
436
  if (earlyCode) {
359
- const copy = failureCopy(earlyCode);
437
+ const copy = failureCopy(earlyCode, l);
360
438
  return {
361
439
  banner: {
362
440
  kind: "failed",
363
- title: `\u66F4\u65B0\u5931\u8D25\uFF08${earlyCode}\uFF09\uFF1A${copy?.zh ?? earlyCode}\u3002`,
364
- action: copy?.act || "\u590D\u5236\u8BCA\u65AD\u53D1\u7ED9\u63D2\u4EF6\u4F5C\u8005\uFF1B\u6DF1\u6316\u770B\u65E5\u5FD7\u901A\u9053\u3002"
441
+ title: copyText("panel.banner.error.title", l, { code: earlyCode, detail: copy?.zh ?? earlyCode }),
442
+ action: copy?.act || copyText("panel.banner.error.action-fallback", l)
365
443
  },
366
444
  installEnabled: false,
367
- installLabel: "\u91CD\u8BD5\u5B89\u88C5",
445
+ installLabel: copyText("panel.action.retry-install", l),
368
446
  skippedLatest: false,
369
447
  showManual: manual ? true : false,
370
448
  showReset: false,
@@ -372,9 +450,9 @@ function panelViewModelCore(input) {
372
450
  };
373
451
  }
374
452
  return {
375
- banner: { kind: "loading", title: "\u6B63\u5728\u8BFB\u53D6\u66F4\u65B0\u72B6\u6001\u2026", action: "" },
453
+ banner: { kind: "loading", title: copyText("panel.banner.loading", l), action: "" },
376
454
  installEnabled: false,
377
- installLabel: "\u5B89\u88C5\u66F4\u65B0",
455
+ installLabel: copyText("panel.action.install", l),
378
456
  skippedLatest: false,
379
457
  showManual: false,
380
458
  showReset: false,
@@ -383,16 +461,16 @@ function panelViewModelCore(input) {
383
461
  }
384
462
  const job = snapshot.job ?? null;
385
463
  const jobState = job?.state ?? null;
386
- 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;
464
+ const queueNote = queue && queue.busy ? queue.position === 0 ? copyText("panel.queue.busy-self", l) : typeof queue.position === "number" ? copyText("panel.queue.busy-queued", l, { n: queue.position }) : copyText("panel.queue.busy-other", l) : null;
387
465
  if (skippedLatest && snapshot.latestVersion) {
388
466
  return {
389
467
  banner: {
390
468
  kind: "idle",
391
- title: `\u5DF2\u8DF3\u8FC7 ${snapshot.latestVersion}`,
392
- 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"
469
+ title: copyText("panel.skip.skipped-title", l, { latest: snapshot.latestVersion }),
470
+ action: copyText("panel.skip.skipped-action", l)
393
471
  },
394
472
  installEnabled: false,
395
- installLabel: "\u5B89\u88C5\u66F4\u65B0",
473
+ installLabel: copyText("panel.action.install", l),
396
474
  skippedLatest: true,
397
475
  showManual: false,
398
476
  showReset: true,
@@ -404,13 +482,13 @@ function panelViewModelCore(input) {
404
482
  return {
405
483
  banner: {
406
484
  kind: "restart",
407
- // 文案照原型(d5-paper.html:329):不带 emoji——警示由横幅左侧的手绘 SVG 标承担,
485
+ // 文案照原型(archive.html:329):不带 emoji——警示由横幅左侧的手绘 SVG 标承担,
408
486
  // 印章在状态一侧,两者各司其职,不再三重标记。
409
- title: `\u65B0\u7248 ${latest} \u5DF2\u5B89\u88C5\uFF0C\u91CD\u542F\u5BBF\u4E3B\u540E\u751F\u6548\u3002`,
410
- action: BLOCKED_COPY["pending-restart"].action
487
+ title: copyText("panel.banner.restart-title", l, { latest }),
488
+ action: blockedCopy("pending-restart", l)?.action ?? ""
411
489
  },
412
490
  installEnabled: false,
413
- installLabel: "\u5B89\u88C5\u66F4\u65B0",
491
+ installLabel: copyText("panel.action.install", l),
414
492
  skippedLatest: false,
415
493
  showManual: manual ? true : false,
416
494
  showReset: false,
@@ -418,14 +496,17 @@ function panelViewModelCore(input) {
418
496
  };
419
497
  }
420
498
  if (jobState === "installing" || jobState === "verifying") {
499
+ const ver = typeof job?.targetVersion === "string" && job.targetVersion.trim() ? job.targetVersion.trim() : "";
500
+ let installingTitle = copyText("panel.banner.installing-title", l, { version: ver || " " });
501
+ if (!ver) installingTitle = installingTitle.replace(" \u2026", "\u2026").replace(" ...", "...");
421
502
  return {
422
503
  banner: {
423
504
  kind: "busy",
424
- title: `\u6B63\u5728\u5B89\u88C5${job?.targetVersion ? ` ${job.targetVersion}` : ""}\u2026\u5173\u95ED\u9762\u677F\u4E0D\u4F1A\u4E2D\u65AD\u3002`,
425
- action: "\u8FDB\u5EA6\u6309\u8F6E\u8BE2\u81EA\u52A8\u5237\u65B0\uFF1B\u91CD\u5F00\u9762\u677F 1 \u79D2\u5185\u6062\u590D\u663E\u793A\u3002"
505
+ title: installingTitle,
506
+ action: copyText("panel.banner.installing-action", l)
426
507
  },
427
508
  installEnabled: false,
428
- installLabel: "\u5B89\u88C5\u4E2D\u2026",
509
+ installLabel: copyText("panel.action.installing", l),
429
510
  skippedLatest: false,
430
511
  showManual: false,
431
512
  showReset: false,
@@ -436,15 +517,15 @@ function panelViewModelCore(input) {
436
517
  const failedCode = typeof errorKind === "string" && errorKind.trim() || lastError || jobCode || "";
437
518
  if (failedCode || jobState === "failed" || jobState === "interrupted") {
438
519
  const code = failedCode || "install-failed";
439
- const copy = failureCopy(code);
520
+ const copy = failureCopy(code, l);
440
521
  return {
441
522
  banner: {
442
523
  kind: "failed",
443
- title: `\u66F4\u65B0\u5931\u8D25\uFF08${code}\uFF09\uFF1A${copy?.zh ?? code}\u3002`,
444
- action: copy?.act || "\u590D\u5236\u8BCA\u65AD\u53D1\u7ED9\u63D2\u4EF6\u4F5C\u8005\uFF1B\u6DF1\u6316\u770B\u65E5\u5FD7\u901A\u9053\u3002"
524
+ title: copyText("panel.banner.error.title", l, { code, detail: copy?.zh ?? code }),
525
+ action: copy?.act || copyText("panel.banner.error.action-fallback", l)
445
526
  },
446
527
  installEnabled: snapshot.canInstall,
447
- installLabel: "\u91CD\u8BD5\u5B89\u88C5",
528
+ installLabel: copyText("panel.action.retry-install", l),
448
529
  skippedLatest: false,
449
530
  showManual: manual ? true : false,
450
531
  showReset: false,
@@ -452,15 +533,16 @@ function panelViewModelCore(input) {
452
533
  };
453
534
  }
454
535
  if (snapshot.blockedReason) {
455
- const copy = blockedCopy(snapshot.blockedReason);
536
+ const copy = blockedCopy(snapshot.blockedReason, l);
537
+ const stop = l === "en" ? "." : "\u3002";
456
538
  return {
457
539
  banner: {
458
540
  kind: "blocked",
459
- title: copy ? `${copy.title}\u3002` : `${snapshot.blockedReason}\u3002`,
541
+ title: copy ? `${copy.title}${stop}` : `${snapshot.blockedReason}${stop}`,
460
542
  action: copy?.action || ""
461
543
  },
462
544
  installEnabled: false,
463
- installLabel: "\u5B89\u88C5\u66F4\u65B0",
545
+ installLabel: copyText("panel.action.install", l),
464
546
  skippedLatest: false,
465
547
  showManual: manual ? true : false,
466
548
  showReset: false,
@@ -471,11 +553,11 @@ function panelViewModelCore(input) {
471
553
  return {
472
554
  banner: {
473
555
  kind: "update",
474
- title: `\u6709\u65B0\u7248 ${snapshot.latestVersion} \u53EF\u88C5\uFF08\u5F53\u524D ${snapshot.runningVersion}\uFF09\u3002`,
475
- 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"
556
+ title: copyText("panel.banner.update-title", l, { latest: snapshot.latestVersion, running: snapshot.runningVersion }),
557
+ action: copyText("panel.banner.update-action", l)
476
558
  },
477
559
  installEnabled: true,
478
- installLabel: `\u5B89\u88C5 ${snapshot.latestVersion}`,
560
+ installLabel: copyText("panel.action.install-version", l, { latest: snapshot.latestVersion }),
479
561
  skippedLatest: false,
480
562
  showManual: manual ? true : false,
481
563
  showReset: false,
@@ -483,9 +565,9 @@ function panelViewModelCore(input) {
483
565
  };
484
566
  }
485
567
  return {
486
- banner: { kind: "done", title: "\u5DF2\u662F\u6700\u65B0\uFF0C\u65E0\u9700\u66F4\u65B0\u3002", action: "" },
568
+ banner: { kind: "done", title: copyText("panel.banner.done", l), action: "" },
487
569
  installEnabled: false,
488
- installLabel: "\u5B89\u88C5\u66F4\u65B0",
570
+ installLabel: copyText("panel.action.install", l),
489
571
  skippedLatest: false,
490
572
  showManual: false,
491
573
  showReset: false,
@@ -493,23 +575,23 @@ function panelViewModelCore(input) {
493
575
  };
494
576
  }
495
577
  const UPDATE_PANEL_CSS = [
496
- '.dsh-upd{font:14px/1.6 system-ui,"Microsoft YaHei",sans-serif;color:var(--dsh-upd-fg,#1f2937);',
497
- "background:var(--dsh-upd-bg,#ffffff);border:1px solid var(--dsh-upd-line,#e5e7eb);border-radius:8px;padding:12px 14px;max-width:560px;",
578
+ '.dsh-upd{font:14px/1.6 system-ui,"Microsoft YaHei",sans-serif;color:var(--dsh-update-text,#1f2937);',
579
+ "background:var(--dsh-update-bg,#ffffff);border:1px solid var(--dsh-update-border,#e5e7eb);border-radius:8px;padding:12px 14px;max-width:560px;",
498
580
  // 横幅配色走变量(浅色默认 + 深色覆盖,见下方 dark 媒体块):硬编码浅色会让深色下
499
581
  // 「浅底 + 浅字」读不出来(现场回归:默认主题深色模式更新横幅白底浅字)。
500
- "--dsh-upd-ok-bg:#ecfdf5;--dsh-upd-ok-line:#059669;--dsh-upd-warn-bg:#fffbeb;--dsh-upd-warn-line:#d97706;",
501
- "--dsh-upd-bad-bg:#fef2f2;--dsh-upd-bad-line:#dc2626;--dsh-upd-busy-bg:#eff6ff;--dsh-upd-busy-line:#2563eb}",
582
+ "--dsh-update-ok-bg:#ecfdf5;--dsh-update-ok-border:#059669;--dsh-update-warn-bg:#fffbeb;--dsh-update-warn-border:#d97706;",
583
+ "--dsh-update-bad-bg:#fef2f2;--dsh-update-bad-border:#dc2626;--dsh-update-busy-bg:#eff6ff;--dsh-update-busy-border:#2563eb}",
502
584
  ".dsh-upd *{box-sizing:border-box}",
503
- ".dsh-upd button{font:inherit;border:1px solid var(--dsh-upd-line,#d1d5db);border-radius:6px;background:var(--dsh-upd-btn,#f9fafb);",
585
+ ".dsh-upd button{font:inherit;border:1px solid var(--dsh-update-border-strong,#d1d5db);border-radius:var(--dsh-update-radius-button,6px);background:var(--dsh-update-button-bg,#f9fafb);",
504
586
  "color:inherit;padding:4px 12px;cursor:pointer;margin:2px 6px 2px 0}",
505
587
  ".dsh-upd button:disabled{opacity:.45;cursor:not-allowed}",
506
- ".dsh-upd button:focus-visible{outline:2px solid var(--dsh-upd-focus,#2563eb);outline-offset:1px}",
507
- '.dsh-upd button[data-primary="1"]{background:var(--dsh-upd-primary,#2563eb);border-color:var(--dsh-upd-primary,#2563eb);color:#fff}',
508
- ".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)}",
509
- '.dsh-upd-banner[data-kind="restart"]{border-color:var(--dsh-upd-warn-line);background:var(--dsh-upd-warn-bg)}',
510
- '.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)}',
511
- '.dsh-upd-banner[data-kind="update"]{border-color:var(--dsh-upd-ok-line);background:var(--dsh-upd-ok-bg)}',
512
- '.dsh-upd-banner[data-kind="busy"]{border-color:var(--dsh-upd-busy-line);background:var(--dsh-upd-busy-bg)}',
588
+ ".dsh-upd button:focus-visible{outline:2px solid var(--dsh-update-focus,#2563eb);outline-offset:1px}",
589
+ '.dsh-upd button[data-primary="1"]{background:var(--dsh-update-primary,#2563eb);border-color:var(--dsh-update-primary,#2563eb);color:#fff}',
590
+ ".dsh-upd-banner{border-left:4px solid var(--dsh-update-border,#9ca3af);padding:6px 10px;margin:0 0 8px;background:var(--dsh-update-bg-soft,#f3f4f6)}",
591
+ '.dsh-upd-banner[data-kind="restart"]{border-color:var(--dsh-update-warn-border);background:var(--dsh-update-warn-bg)}',
592
+ '.dsh-upd-banner[data-kind="failed"],.dsh-upd-banner[data-kind="blocked"]{border-color:var(--dsh-update-bad-border);background:var(--dsh-update-bad-bg)}',
593
+ '.dsh-upd-banner[data-kind="update"]{border-color:var(--dsh-update-ok-border);background:var(--dsh-update-ok-bg)}',
594
+ '.dsh-upd-banner[data-kind="busy"]{border-color:var(--dsh-update-busy-border);background:var(--dsh-update-busy-bg)}',
513
595
  ".dsh-upd code{font-family:Consolas,Menlo,monospace;font-size:12px;word-break:break-all}",
514
596
  ".dsh-upd-manual,.dsh-upd-queue,.dsh-upd-log{margin:4px 0;font-size:13px}",
515
597
  ".dsh-upd-changelog-wrap{margin:4px 0 0;font-size:13px}",
@@ -521,71 +603,71 @@ const UPDATE_PANEL_CSS = [
521
603
  ".dsh-upd-changelog-fold{margin:4px 0}",
522
604
  ".dsh-upd-changelog-fold>summary{cursor:pointer}",
523
605
  ".dsh-upd-changelog-count{font-size:12px;opacity:.7;margin:2px 0 4px}",
524
- ".dsh-upd-changelog-yanked{border-left:4px solid var(--dsh-upd-warn-line,#d97706);background:var(--dsh-upd-warn-bg,#fffbeb);padding:6px 10px;margin:6px 0;font-size:13px}",
606
+ ".dsh-upd-changelog-yanked{border-left:4px solid var(--dsh-update-warn-border,#d97706);background:var(--dsh-update-warn-bg,#fffbeb);padding:6px 10px;margin:6px 0;font-size:13px}",
525
607
  ".dsh-upd-breaking-badge{display:inline-block;font-size:11px;font-weight:700;border:1px solid currentColor;border-radius:3px;padding:0 5px;margin-right:6px;vertical-align:baseline}",
526
608
  ".dsh-upd-changelog-security-more{margin:4px 0 6px}",
527
609
  ".dsh-upd-changelog-security-more>summary{cursor:pointer;font-size:12px;opacity:.8}",
528
610
  ".dsh-upd-changelog-more-note{font-size:12px;opacity:.7;margin:2px 0 4px}",
529
611
  ".dsh-upd-changelog-neutral{color:inherit;opacity:.8}",
530
612
  ".dsh-upd-overlay{position:fixed;inset:0;background:rgba(0,0,0,.35);display:flex;align-items:center;justify-content:center;z-index:9999}",
531
- ".dsh-upd-overlay .dsh-upd{background:var(--dsh-upd-bg,#ffffff);max-height:85vh;display:flex;flex-direction:column;overflow:hidden}",
613
+ ".dsh-upd-overlay .dsh-upd{background:var(--dsh-update-bg,#ffffff);max-height:85vh;display:flex;flex-direction:column;overflow:hidden}",
532
614
  // —— 弹窗分栏滚动:头(抬头/档案头/横幅/版本条)与尾固定,只有 01–05 章节区滚动 ——
533
615
  ".dsh-upd-body{min-height:0}",
534
- ".dsh-upd-overlay .dsh-upd-body{flex:1 1 auto;overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:var(--dsh-upd-line,#e5e7eb) transparent}",
616
+ ".dsh-upd-overlay .dsh-upd-body{flex:1 1 auto;overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:var(--dsh-update-border,#e5e7eb) transparent}",
535
617
  ".dsh-upd-overlay .dsh-upd-body::-webkit-scrollbar{width:8px}",
536
- ".dsh-upd-overlay .dsh-upd-body::-webkit-scrollbar-thumb{background:var(--dsh-upd-line,#e5e7eb);border-radius:4px}",
618
+ ".dsh-upd-overlay .dsh-upd-body::-webkit-scrollbar-thumb{background:var(--dsh-update-border,#e5e7eb);border-radius:4px}",
537
619
  ".dsh-upd-overlay .dsh-upd-body::-webkit-scrollbar-track{background:transparent}",
538
- // —— 章节标题磁吸:滚动时节标题贴顶(纯 CSS sticky;背景跟随主题,D5 另覆)——
539
- ".dsh-upd-overlay .dsh-upd-body .dsh-upd-chap-head{position:sticky;top:0;z-index:1;background:var(--dsh-upd-bg,#ffffff);padding-top:2px}",
620
+ // —— 章节标题磁吸:滚动时节标题贴顶(纯 CSS sticky;背景跟随主题,Archive 另覆)——
621
+ ".dsh-upd-overlay .dsh-upd-body .dsh-upd-chap-head{position:sticky;top:0;z-index:1;background:var(--dsh-update-bg,#ffffff);padding-top:2px}",
540
622
  // —— 档案头 / 版本条 / 章节 / 进度条 / 跳过行(原型 :208-245 的新结构,默认主题给最小可用样式)——
541
623
  ".dsh-upd-head{display:flex;gap:12px;align-items:baseline;flex-wrap:wrap}",
542
- // 卷宗抬头「插件更新 / 更新档案 卷」:D5 档案卷才画,默认(最小)主题不画。
624
+ // 卷宗抬头「插件更新 / 更新档案 卷」:Archive 档案卷才画,默认(最小)主题不画。
543
625
  // 两个主题共用同一份内核 HTML(见 renderUpdatePanelHTML 的注释),画不画是皮肤决定的事。
544
626
  ".dsh-upd-masthead{display:none}",
545
627
  ".dsh-upd-name{font-weight:700}",
546
628
  ".dsh-upd-meta{font-size:12.5px;opacity:.75}",
547
- ".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}",
548
- ".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}",
549
- ".dsh-upd-strip>div{flex:1 1 110px;padding:6px 10px;border-left:1px solid var(--dsh-upd-line,#e5e7eb)}",
629
+ ".dsh-upd-proftag{font-family:Consolas,Menlo,monospace;font-size:11px;border:1px solid var(--dsh-update-border,#d1d5db);border-radius:3px;padding:0 5px;margin-left:6px;letter-spacing:.06em}",
630
+ ".dsh-upd-strip{display:flex;flex-wrap:wrap;margin:8px 0 0;border:1px solid var(--dsh-update-border,#e5e7eb);border-radius:4px;overflow:hidden;font-size:12.5px}",
631
+ ".dsh-upd-strip>div{flex:1 1 110px;padding:6px 10px;border-left:1px solid var(--dsh-update-border,#e5e7eb)}",
550
632
  ".dsh-upd-strip>div:first-child{border-left:0}",
551
633
  ".dsh-upd-strip-k{display:block;font-size:11px;letter-spacing:.14em;opacity:.7}",
552
634
  ".dsh-upd-strip-v{font-family:Consolas,Menlo,monospace;font-size:12.5px}",
553
- ".dsh-upd-chapter{margin-top:14px;padding-top:10px;border-top:1px solid var(--dsh-upd-line,#e5e7eb)}",
635
+ ".dsh-upd-chapter{margin-top:14px;padding-top:10px;border-top:1px solid var(--dsh-update-border,#e5e7eb)}",
554
636
  // 右下角独立 footer 区(#47 定案 A):与第一章 actions 脱钩,右对齐,一次找到。
555
- ".dsh-upd-footer{display:flex;align-items:center;justify-content:flex-end;gap:10px;margin-top:14px;padding-top:10px;border-top:1px solid var(--dsh-upd-line,#e5e7eb)}",
637
+ ".dsh-upd-footer{display:flex;align-items:center;justify-content:flex-end;gap:10px;margin-top:14px;padding-top:10px;border-top:1px solid var(--dsh-update-border,#e5e7eb)}",
556
638
  ".dsh-upd-foot-note{margin-right:auto;font-size:12px;opacity:.7}",
557
639
  ".dsh-upd-footer button{margin:0}",
558
640
  ".dsh-upd-chap-head{display:flex;align-items:baseline;gap:10px;margin-bottom:6px}",
559
641
  ".dsh-upd-chap-no{font-size:13px;font-style:italic;opacity:.6}",
560
642
  ".dsh-upd-chap-title{font-size:14px;margin:0}",
561
- ".dsh-upd-chap-rule{flex:1;border-top:1px solid var(--dsh-upd-line,#e5e7eb);transform:translateY(-3px)}",
643
+ ".dsh-upd-chap-rule{flex:1;border-top:1px solid var(--dsh-update-border,#e5e7eb);transform:translateY(-3px)}",
562
644
  // 03 章标题行右端的开关(问题 3 定案:按钮形态、挪到标题行)。
563
645
  ".dsh-upd-chap-note{flex:none;font-size:12px;opacity:.75}",
564
646
  ".dsh-upd-chap-note button{margin:0}",
565
647
  // 更新队列两行键值(用户定案的设计):结构两主题共用,皮肤各自收敛。
566
648
  ".dsh-upd-qrow{display:flex;align-items:baseline;gap:10px;padding:6px 0}",
567
- ".dsh-upd-qrow+.dsh-upd-qrow{border-top:1px solid var(--dsh-upd-line,#e5e7eb)}",
649
+ ".dsh-upd-qrow+.dsh-upd-qrow{border-top:1px solid var(--dsh-update-border,#e5e7eb)}",
568
650
  ".dsh-upd-qdot{width:8px;height:8px;border-radius:50%;flex:none;align-self:center;background:currentColor;opacity:.5}",
569
- '.dsh-upd-qdot[data-tone="busy"]{background:var(--dsh-upd-warn-line,#d97706);opacity:1}',
570
- '.dsh-upd-qdot[data-tone="you"]{background:var(--dsh-upd-primary,#2563eb);opacity:1}',
651
+ '.dsh-upd-qdot[data-tone="busy"]{background:var(--dsh-update-warn-border,#d97706);opacity:1}',
652
+ '.dsh-upd-qdot[data-tone="you"]{background:var(--dsh-update-primary,#2563eb);opacity:1}',
571
653
  ".dsh-upd-qk{flex:none;width:5.5em;font-size:12px;opacity:.7}",
572
654
  ".dsh-upd-qv{font-weight:600}",
573
655
  ".dsh-upd-qn{margin-left:auto;font-size:12px;opacity:.7}",
574
656
  ".dsh-upd-qseq{font-family:Consolas,Menlo,monospace;font-size:12px;word-break:break-all}",
575
- ".dsh-upd-prog{height:8px;background:var(--dsh-upd-line,#e5e7eb);border-radius:4px;overflow:hidden;margin:10px 0 4px}",
576
- ".dsh-upd-prog-bar{display:block;height:100%;background:var(--dsh-upd-primary,#2563eb);transition:width .3s}",
657
+ ".dsh-upd-prog{height:8px;background:var(--dsh-update-border,#e5e7eb);border-radius:4px;overflow:hidden;margin:10px 0 4px}",
658
+ ".dsh-upd-prog-bar{display:block;height:100%;background:var(--dsh-update-primary,#2563eb);transition:width .3s}",
577
659
  ".dsh-upd-progtxt{font-size:12.5px;opacity:.75}",
578
660
  ".dsh-upd-skipline{font-size:13px;margin-top:8px}",
579
661
  // 骨架微光:只在首帧 loading 出现;reduced-motion 下静止占位,不断语义。
580
662
  ".dsh-upd-skv{display:inline-block;min-width:64px;border-radius:3px;color:transparent !important;user-select:none;",
581
- "background:linear-gradient(90deg,var(--dsh-upd-line,#e5e7eb) 25%,var(--dsh-upd-soft,#f3f4f6) 50%,var(--dsh-upd-line,#e5e7eb) 75%);",
663
+ "background:linear-gradient(90deg,var(--dsh-update-border,#e5e7eb) 25%,var(--dsh-update-bg-soft,#f3f4f6) 50%,var(--dsh-update-border,#e5e7eb) 75%);",
582
664
  "background-size:200% 100%;animation:dsh-upd-shimmer 1.2s linear infinite}",
583
665
  "@keyframes dsh-upd-shimmer{to{background-position:-200% 0}}",
584
666
  ".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}",
585
667
  ".dsh-upd-err{font-size:13px;margin:0 0 6px}",
586
668
  // —— 全按钮交互反馈(#36:悬停/按下/过渡/在途忙态;浅深双主题通用写法,不碰上面的既有串)——
587
669
  ".dsh-upd button{transition:background-color .15s ease,border-color .15s ease,color .15s ease,transform .06s ease}",
588
- ".dsh-upd button:hover:not(:disabled){border-color:var(--dsh-upd-focus,#2563eb)}",
670
+ ".dsh-upd button:hover:not(:disabled){border-color:var(--dsh-update-focus,#2563eb)}",
589
671
  '.dsh-upd button[data-primary="1"]:hover:not(:disabled){filter:brightness(.93)}',
590
672
  ".dsh-upd button:active:not(:disabled){transform:translateY(1px)}",
591
673
  '.dsh-upd button[aria-busy="true"]{cursor:wait;animation:dsh-upd-pulse 1s ease-in-out infinite}',
@@ -602,72 +684,81 @@ const UPDATE_PANEL_CSS = [
602
684
  "@keyframes dsh-upd-spin{to{transform:rotate(360deg)}}",
603
685
  '@media (prefers-reduced-motion: reduce){.dsh-upd button{transition:none}.dsh-upd button:active:not(:disabled){transform:none}.dsh-upd button[aria-busy="true"]{animation:none}.dsh-upd-skv{animation:none}.dsh-upd-banner{animation:none}.dsh-upd-changelog-foldbox{transition:none}}',
604
686
  "@media (forced-colors: active){.dsh-upd-overlay .dsh-upd-body .dsh-upd-chap-head{background:Canvas}}",
605
- "@media (prefers-color-scheme: dark){.dsh-upd{--dsh-upd-fg:#e5e7eb;--dsh-upd-bg:#111827;--dsh-upd-line:#374151;",
606
- "--dsh-upd-btn:#1f2937;--dsh-upd-soft:#1f2937;--dsh-upd-primary:#3b82f6;--dsh-upd-focus:#93c5fd;",
687
+ // —— 查新版布局稳定:按钮预留宽度 + 动态区最小高度 + 锚定不漂(只稳布局,不改文案语义)——
688
+ ".dsh-upd{overflow-anchor:none}",
689
+ ".dsh-upd-body{overflow-anchor:none}",
690
+ ".dsh-upd-actions{display:flex;flex-wrap:wrap;align-items:center;min-height:34px}",
691
+ ".dsh-upd-actions button:first-child{min-width:8em;text-align:center}",
692
+ '.dsh-upd-actions button[data-primary="1"]{min-width:7em;text-align:center}',
693
+ '.dsh-upd-actions button:first-child:not([aria-busy="true"])::after{content:"";display:inline-block;width:11px;height:11px;margin-left:8px;visibility:hidden}',
694
+ ".dsh-upd-banner{min-height:1.2em}",
695
+ ".dsh-upd-strip{min-height:48px}",
696
+ "@media (prefers-color-scheme: dark){.dsh-upd{--dsh-update-text:#e5e7eb;--dsh-update-bg:#111827;--dsh-update-border:#374151;",
697
+ "--dsh-update-button-bg:#1f2937;--dsh-update-bg-soft:#1f2937;--dsh-update-primary:#3b82f6;--dsh-update-focus:#93c5fd;",
607
698
  // 横幅深色覆盖:底色用低透明度同色系(不是浅色原值),边线提亮,保证「深底浅字」可读。
608
- "--dsh-upd-ok-bg:rgba(16,185,129,.14);--dsh-upd-ok-line:#34d399;",
609
- "--dsh-upd-warn-bg:rgba(245,158,11,.16);--dsh-upd-warn-line:#fbbf24;",
610
- "--dsh-upd-bad-bg:rgba(239,68,68,.16);--dsh-upd-bad-line:#f87171;",
611
- "--dsh-upd-busy-bg:rgba(59,130,246,.16);--dsh-upd-busy-line:#60a5fa}}"
699
+ "--dsh-update-ok-bg:rgba(16,185,129,.14);--dsh-update-ok-border:#34d399;",
700
+ "--dsh-update-warn-bg:rgba(245,158,11,.16);--dsh-update-warn-border:#fbbf24;",
701
+ "--dsh-update-bad-bg:rgba(239,68,68,.16);--dsh-update-bad-border:#f87171;",
702
+ "--dsh-update-busy-bg:rgba(59,130,246,.16);--dsh-update-busy-border:#60a5fa}}"
612
703
  ].join("\n");
613
- const UPDATE_PANEL_D5_CSS = [
614
- "/* 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 */",
615
- '.dsh-upd[data-theme="archive"]{--d5-bg:#f7f3ea;--d5-card:#fffdf6;--d5-ink:#1a1a1a;--d5-muted:#6f675a;',
616
- "--d5-line:#e3d9c4;--d5-line-strong:#c4b896;--d5-accent:#c8402a;--d5-accent-deep:#9c2e1d;",
617
- "--d5-ok:#1a7f37;--d5-ok-bg:#e9f4ea;--d5-warn:#8a5a00;--d5-warn-bg:#fbf0d0;",
618
- "--d5-bad:#b3261e;--d5-bad-bg:#fbe9e5;",
619
- '--d5-serif:Georgia,"Songti SC","STSong","SimSun","Noto Serif CJK SC","Source Han Serif SC",serif;',
620
- '--d5-sans:system-ui,"PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif;',
621
- '--d5-mono:ui-monospace,"SF Mono",SFMono-Regular,Consolas,"Noto Sans Mono",monospace;',
622
- "--d5-shadow:0 1px 2px rgba(60,40,20,.08),0 12px 32px rgba(60,40,20,.10);",
623
- "font-family:var(--d5-sans);color:var(--d5-ink);background:var(--d5-card);",
624
- "border:1px solid var(--d5-line-strong);border-radius:4px;box-shadow:var(--d5-shadow)}",
704
+ const UPDATE_PANEL_ARCHIVE_CSS = [
705
+ "/* Archive \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 */",
706
+ '.dsh-upd[data-theme="archive"]{--dsh-update-bg-soft:#f7f3ea;--dsh-update-bg:#fffdf6;--dsh-update-text:#1a1a1a;--dsh-update-text-muted:#6f675a;',
707
+ "--dsh-update-border:#e3d9c4;--dsh-update-border-strong:#c4b896;--dsh-update-primary:#c8402a;--dsh-update-primary-deep:#9c2e1d;",
708
+ "--dsh-update-ok-text:#1a7f37;--dsh-update-ok-bg:#e9f4ea;--dsh-update-warn-text:#8a5a00;--dsh-update-warn-bg:#fbf0d0;",
709
+ "--dsh-update-bad-text:#b3261e;--dsh-update-bad-bg:#fbe9e5;",
710
+ '--dsh-update-font-serif:Georgia,"Songti SC","STSong","SimSun","Noto Serif CJK SC","Source Han Serif SC",serif;',
711
+ '--dsh-update-font-sans:system-ui,"PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif;',
712
+ '--dsh-update-font-mono:ui-monospace,"SF Mono",SFMono-Regular,Consolas,"Noto Sans Mono",monospace;',
713
+ "--dsh-update-shadow:0 1px 2px rgba(60,40,20,.08),0 12px 32px rgba(60,40,20,.10);",
714
+ "font-family:var(--dsh-update-font-sans);color:var(--dsh-update-text);background:var(--dsh-update-bg);",
715
+ "border:1px solid var(--dsh-update-border-strong);border-radius:var(--dsh-update-radius-panel,4px);box-shadow:var(--dsh-update-shadow)}",
625
716
  // 按钮脸自己不透明(宿主底色未知时也读得出;卡片上渲染与 transparent 逐字同色)。
626
- '.dsh-upd[data-theme="archive"] button{border-color:var(--d5-line-strong);background:var(--d5-card);color:var(--d5-ink);border-radius:3px;font-family:var(--d5-sans)}',
627
- '.dsh-upd[data-theme="archive"] button:hover:not(:disabled){border-color:var(--d5-accent);color:var(--d5-accent)}',
628
- '.dsh-upd[data-theme="archive"] button[data-primary="1"]{background:var(--d5-accent);border-color:var(--d5-accent);color:#fff}',
629
- '.dsh-upd[data-theme="archive"] button[data-primary="1"]:hover:not(:disabled){background:var(--d5-accent-deep);color:#fff}',
630
- '.dsh-upd[data-theme="archive"] button:focus-visible{outline:2px solid var(--d5-accent);outline-offset:2px}',
631
- '.dsh-upd[data-theme="archive"] .dsh-upd-banner{background:var(--d5-bg);border-color:var(--d5-line-strong)}',
632
- '.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="update"]{border-color:var(--d5-ok);background:var(--d5-ok-bg)}',
633
- '.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="busy"]{border-color:var(--d5-warn);background:var(--d5-warn-bg)}',
634
- '.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="restart"]{border-color:var(--d5-warn);background:var(--d5-warn-bg);font-family:var(--d5-serif);border-width:2px}',
635
- '.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="failed"],.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="blocked"]{border-color:var(--d5-bad);background:var(--d5-bad-bg)}',
636
- '.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="done"]{border-color:var(--d5-ok);background:var(--d5-ok-bg)}',
637
- '.dsh-upd[data-theme="archive"] .dsh-upd-changelog-yanked{border-color:var(--d5-warn);background:var(--d5-warn-bg);color:var(--d5-ink)}',
638
- '.dsh-upd[data-theme="archive"] .dsh-upd-breaking-badge{color:var(--d5-accent);border-color:var(--d5-accent)}',
639
- '.dsh-upd[data-theme="archive"] .dsh-upd-changelog-count{color:var(--d5-muted)}',
640
- '.dsh-upd[data-theme="archive"] .dsh-upd-changelog-more-note{color:var(--d5-muted)}',
717
+ '.dsh-upd[data-theme="archive"] button{border-color:var(--dsh-update-border-strong);background:var(--dsh-update-button-bg);color:var(--dsh-update-text);border-radius:var(--dsh-update-radius-button,3px);font-family:var(--dsh-update-font-sans)}',
718
+ '.dsh-upd[data-theme="archive"] button:hover:not(:disabled){border-color:var(--dsh-update-primary);color:var(--dsh-update-primary)}',
719
+ '.dsh-upd[data-theme="archive"] button[data-primary="1"]{background:var(--dsh-update-primary);border-color:var(--dsh-update-primary);color:#fff}',
720
+ '.dsh-upd[data-theme="archive"] button[data-primary="1"]:hover:not(:disabled){background:var(--dsh-update-primary-deep);color:#fff}',
721
+ '.dsh-upd[data-theme="archive"] button:focus-visible{outline:2px solid var(--dsh-update-primary);outline-offset:2px}',
722
+ '.dsh-upd[data-theme="archive"] .dsh-upd-banner{background:var(--dsh-update-bg-soft);border-color:var(--dsh-update-border-strong)}',
723
+ '.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="update"]{border-color:var(--dsh-update-ok-border);background:var(--dsh-update-ok-bg)}',
724
+ '.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="busy"]{border-color:var(--dsh-update-busy-border);background:var(--dsh-update-busy-bg)}',
725
+ '.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="restart"]{border-color:var(--dsh-update-warn-border);background:var(--dsh-update-warn-bg);font-family:var(--dsh-update-font-serif);border-width:2px}',
726
+ '.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="failed"],.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="blocked"]{border-color:var(--dsh-update-bad-border);background:var(--dsh-update-bad-bg)}',
727
+ '.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="done"]{border-color:var(--dsh-update-ok-border);background:var(--dsh-update-ok-bg)}',
728
+ '.dsh-upd[data-theme="archive"] .dsh-upd-changelog-yanked{border-color:var(--dsh-update-warn-text);background:var(--dsh-update-warn-bg);color:var(--dsh-update-text)}',
729
+ '.dsh-upd[data-theme="archive"] .dsh-upd-breaking-badge{color:var(--dsh-update-primary);border-color:var(--dsh-update-primary)}',
730
+ '.dsh-upd[data-theme="archive"] .dsh-upd-changelog-count{color:var(--dsh-update-text-muted)}',
731
+ '.dsh-upd[data-theme="archive"] .dsh-upd-changelog-more-note{color:var(--dsh-update-text-muted)}',
641
732
  // —— 大印章(原型 :206 `.seal`:右上 88px、旋转 -7°、双细框;内容与色调来自根属性,不加节点)——
642
733
  '.dsh-upd[data-theme="archive"]{position:relative;padding:16px 20px 14px}',
643
734
  '.dsh-upd[data-theme="archive"]::before{content:attr(data-seal);position:absolute;top:20px;right:24px;width:88px;height:88px;',
644
735
  "display:flex;align-items:center;justify-content:center;text-align:center;letter-spacing:.18em;text-indent:.18em;line-height:1.35;",
645
- "border:3px solid currentColor;border-radius:14px;transform:rotate(-7deg);font-family:var(--d5-serif);font-weight:700;font-size:21px;",
736
+ "border:3px solid currentColor;border-radius:14px;transform:rotate(-7deg);font-family:var(--dsh-update-font-serif);font-weight:700;font-size:21px;",
646
737
  "background:color-mix(in srgb,currentColor 8%,transparent);user-select:none;pointer-events:none;",
647
- "box-shadow:inset 0 0 0 5px var(--d5-card),inset 0 0 0 6px currentColor,0 2px 6px rgba(0,0,0,.12)}",
648
- '.dsh-upd[data-theme="archive"][data-seal-tone="ink"]::before{color:var(--d5-muted)}',
649
- '.dsh-upd[data-theme="archive"][data-seal-tone="green"]::before{color:var(--d5-ok)}',
650
- '.dsh-upd[data-theme="archive"][data-seal-tone="yellow"]::before{color:var(--d5-warn)}',
651
- '.dsh-upd[data-theme="archive"][data-seal-tone="red"]::before{color:var(--d5-bad)}',
738
+ "box-shadow:inset 0 0 0 5px var(--dsh-update-bg),inset 0 0 0 6px currentColor,0 2px 6px rgba(0,0,0,.12)}",
739
+ '.dsh-upd[data-theme="archive"][data-seal-tone="ink"]::before{color:var(--dsh-update-text-muted)}',
740
+ '.dsh-upd[data-theme="archive"][data-seal-tone="green"]::before{color:var(--dsh-update-ok-text)}',
741
+ '.dsh-upd[data-theme="archive"][data-seal-tone="yellow"]::before{color:var(--dsh-update-warn-text)}',
742
+ '.dsh-upd[data-theme="archive"][data-seal-tone="red"]::before{color:var(--dsh-update-bad-text)}',
652
743
  // 印章占位:首行(横幅/状态行)右侧留出 120px,文字不许压到印章上(原型 .filehead padding-right:120px)
653
- // —— 卷宗抬头(原型 :195-203 的刊头,主题切换按钮按用户口径去掉):只有 D5 档案卷才显示 ——
654
- '.dsh-upd[data-theme="archive"] .dsh-upd-masthead{display:block;padding:0 0 8px;margin:0 0 8px;border-bottom:1px solid var(--d5-line)}',
655
- '.dsh-upd[data-theme="archive"] .dsh-upd-masthead-kicker{display:block;font-size:11px;letter-spacing:.35em;color:var(--d5-muted);margin-bottom:3px}',
656
- '.dsh-upd[data-theme="archive"] .dsh-upd-masthead-title{font-family:var(--d5-serif);font-size:26px;font-weight:700;line-height:1.2}',
657
- '.dsh-upd[data-theme="archive"] .dsh-upd-masthead-title i{color:var(--d5-accent);font-style:normal}',
744
+ // —— 卷宗抬头(原型 :195-203 的刊头,主题切换按钮按用户口径去掉):只有 Archive 档案卷才显示 ——
745
+ '.dsh-upd[data-theme="archive"] .dsh-upd-masthead{display:block;padding:0 0 8px;margin:0 0 8px;border-bottom:1px solid var(--dsh-update-border)}',
746
+ '.dsh-upd[data-theme="archive"] .dsh-upd-masthead-kicker{display:block;font-size:11px;letter-spacing:.35em;color:var(--dsh-update-text-muted);margin-bottom:3px}',
747
+ '.dsh-upd[data-theme="archive"] .dsh-upd-masthead-title{font-family:var(--dsh-update-font-serif);font-size:26px;font-weight:700;line-height:1.2}',
748
+ '.dsh-upd[data-theme="archive"] .dsh-upd-masthead-title i{color:var(--dsh-update-primary);font-style:normal}',
658
749
  // 横幅不再给大印章留 124px:实测(headless 量盒子)印章盒底边 y=109,横幅正文顶边 y=108、
659
750
  // 状态行那句在 y=159——印章只压到横幅顶部的留白带,压不到正文。留着反而把 27px 那句话挤成两行
660
751
  // (27px 单行需 428px,留白后只剩 366px)。档案头那 120px 保留:那里是真的重叠。
661
- // —— 更新队列(03 章)D5 皮肤 ——
752
+ // —— 更新队列(03 章)Archive 皮肤 ——
662
753
  '.dsh-upd[data-theme="archive"] .dsh-upd-qrow{padding:6px 0}',
663
754
  '.dsh-upd[data-theme="archive"] .dsh-upd-qk{width:66px;letter-spacing:.18em}',
664
- '.dsh-upd[data-theme="archive"] .dsh-upd-qv{font-family:var(--d5-serif);font-size:16px}',
665
- '.dsh-upd[data-theme="archive"] .dsh-upd-qn{font-family:var(--d5-mono);font-size:11.5px;color:var(--d5-muted)}',
666
- '.dsh-upd[data-theme="archive"] .dsh-upd-qseq{color:var(--d5-muted)}',
667
- '.dsh-upd[data-theme="archive"] .dsh-upd-chap-note{color:var(--d5-muted)}',
668
- '.dsh-upd[data-theme="archive"] .dsh-upd-body .dsh-upd-chap-head{background:var(--d5-card)}',
669
- '.dsh-upd-overlay .dsh-upd[data-theme="archive"] .dsh-upd-body{scrollbar-color:var(--d5-line-strong) transparent}',
670
- '.dsh-upd-overlay .dsh-upd[data-theme="archive"] .dsh-upd-body::-webkit-scrollbar-thumb{background:var(--d5-line-strong)}',
755
+ '.dsh-upd[data-theme="archive"] .dsh-upd-qv{font-family:var(--dsh-update-font-serif);font-size:16px}',
756
+ '.dsh-upd[data-theme="archive"] .dsh-upd-qn{font-family:var(--dsh-update-font-mono);font-size:11.5px;color:var(--dsh-update-text-muted)}',
757
+ '.dsh-upd[data-theme="archive"] .dsh-upd-qseq{color:var(--dsh-update-text-muted)}',
758
+ '.dsh-upd[data-theme="archive"] .dsh-upd-chap-note{color:var(--dsh-update-text-muted)}',
759
+ '.dsh-upd[data-theme="archive"] .dsh-upd-body .dsh-upd-chap-head{background:var(--dsh-update-bg)}',
760
+ '.dsh-upd-overlay .dsh-upd[data-theme="archive"] .dsh-upd-body{scrollbar-color:var(--dsh-update-border-strong) transparent}',
761
+ '.dsh-upd-overlay .dsh-upd[data-theme="archive"] .dsh-upd-body::-webkit-scrollbar-thumb{background:var(--dsh-update-border-strong)}',
671
762
  // —— 小印章(原型 :215 `.sealmini`:30px、旋转 -5°、一字)——
672
763
  // 待重启横幅一律不画印章:那一档的标记是左侧手绘 SVG(原型 :446 的 .mark 只有 SVG)。
673
764
  '.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="loading"]::before,.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="idle"]::before,',
@@ -675,13 +766,13 @@ const UPDATE_PANEL_D5_CSS = [
675
766
  '.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="blocked"]::before,.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="failed"]::before,',
676
767
  '.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="done"]::before{content:attr(data-mini);display:inline-flex;align-items:center;justify-content:center;',
677
768
  "width:30px;height:30px;margin-right:10px;vertical-align:middle;border:2px solid currentColor;border-radius:7px;",
678
- "font-family:var(--d5-serif);font-weight:700;font-size:16px;line-height:26px;transform:rotate(-5deg);flex:none;color:var(--d5-muted)}",
679
- '.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="update"]::before{color:var(--d5-ok)}',
680
- '.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="busy"]::before{color:var(--d5-warn)}',
681
- '.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="blocked"]::before,.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="failed"]::before{color:var(--d5-bad)}',
682
- '.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="done"]::before{color:var(--d5-ok)}',
683
- '.dsh-upd[data-theme="archive"] .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}',
684
- '.dsh-upd[data-theme="archive"] .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}',
769
+ "font-family:var(--dsh-update-font-serif);font-weight:700;font-size:16px;line-height:26px;transform:rotate(-5deg);flex:none;color:var(--dsh-update-text-muted)}",
770
+ '.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="update"]::before{color:var(--dsh-update-ok-text)}',
771
+ '.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="busy"]::before{color:var(--dsh-update-warn-text)}',
772
+ '.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="blocked"]::before,.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="failed"]::before{color:var(--dsh-update-bad-text)}',
773
+ '.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="done"]::before{color:var(--dsh-update-ok-text)}',
774
+ '.dsh-upd[data-theme="archive"] .dsh-upd-log code{font-family:var(--dsh-update-font-mono);font-size:11px;color:var(--dsh-update-text-muted);border:1px solid var(--dsh-update-border-strong);border-radius:3px;padding:0 6px;letter-spacing:.06em}',
775
+ '.dsh-upd[data-theme="archive"] .dsh-upd-manual code{display:block;background:var(--dsh-update-text);color:var(--dsh-update-bg-soft);font-family:var(--dsh-update-font-mono);font-size:12.5px;padding:12px 14px;border-radius:4px;white-space:pre-wrap;word-break:break-all}',
685
776
  // 待重启标记:手绘 SVG 当**独立 flex 标记**放在文字块左侧(原型 :446 `.mark` 是独立节点),
686
777
  // 不能用行内背景——那样换行时三角会落在句子中间把话劈开(现场回归)。
687
778
  '.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="restart"]>div:first-child{display:flex;gap:10px;align-items:flex-start}',
@@ -689,45 +780,45 @@ const UPDATE_PANEL_D5_CSS = [
689
780
  '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}',
690
781
  '.dsh-upd[data-theme="archive"] .dsh-upd-banner>div:first-child{overflow:hidden;text-overflow:ellipsis}',
691
782
  '.dsh-upd[data-theme="archive"] .dsh-upd-actions{flex-wrap:wrap}',
692
- // footer 只换肤(#47 定案 A + D5 约束:不换 DOM 顺序;复制诊断永不隐藏,本串不动它)。
693
- '.dsh-upd[data-theme="archive"] .dsh-upd-footer{margin-top:14px;padding-top:10px;border-top:1px solid var(--d5-line)}',
694
- '.dsh-upd[data-theme="archive"] .dsh-upd-foot-note{color:var(--d5-muted)}',
783
+ // footer 只换肤(#47 定案 A + Archive 约束:不换 DOM 顺序;复制诊断永不隐藏,本串不动它)。
784
+ '.dsh-upd[data-theme="archive"] .dsh-upd-footer{margin-top:14px;padding-top:10px;border-top:1px solid var(--dsh-update-border)}',
785
+ '.dsh-upd[data-theme="archive"] .dsh-upd-foot-note{color:var(--dsh-update-text-muted)}',
695
786
  // —— 档案头(原型 :208-211 `.filehead`:serif 插件名 22px + 使用范围 + profile 牌;右侧留章位)——
696
787
  '.dsh-upd[data-theme="archive"] .dsh-upd-head{display:flex;gap:16px;align-items:baseline;flex-wrap:wrap;padding-right:120px}',
697
- '.dsh-upd[data-theme="archive"] .dsh-upd-name{font-family:var(--d5-serif);font-size:22px;font-weight:700}',
698
- '.dsh-upd[data-theme="archive"] .dsh-upd-meta{width:100%;font-size:12.5px;color:var(--d5-muted)}',
699
- '.dsh-upd[data-theme="archive"] .dsh-upd-meta b{color:var(--d5-ink);font-weight:600}',
700
- '.dsh-upd[data-theme="archive"] .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}',
788
+ '.dsh-upd[data-theme="archive"] .dsh-upd-name{font-family:var(--dsh-update-font-serif);font-size:22px;font-weight:700}',
789
+ '.dsh-upd[data-theme="archive"] .dsh-upd-meta{width:100%;font-size:12.5px;color:var(--dsh-update-text-muted)}',
790
+ '.dsh-upd[data-theme="archive"] .dsh-upd-meta b{color:var(--dsh-update-text);font-weight:600}',
791
+ '.dsh-upd[data-theme="archive"] .dsh-upd-proftag{font-family:var(--dsh-update-font-mono);font-size:11px;color:var(--dsh-update-text-muted);border:1px solid var(--dsh-update-border-strong);border-radius:3px;padding:0 6px;margin-left:8px;letter-spacing:.06em}',
701
792
  // —— 版本条(原型 :216 `.strip`:三格,格间一线,左上小写标签 + 等宽值)——
702
- '.dsh-upd[data-theme="archive"] .dsh-upd-strip{display:flex;flex-wrap:wrap;margin:8px 0 0;border:1px solid var(--d5-line);border-radius:4px;overflow:hidden;font-size:12.5px}',
703
- '.dsh-upd[data-theme="archive"] .dsh-upd-strip>div{flex:1 1 120px;padding:6px 10px;border-left:1px solid var(--d5-line)}',
793
+ '.dsh-upd[data-theme="archive"] .dsh-upd-strip{display:flex;flex-wrap:wrap;margin:8px 0 0;border:1px solid var(--dsh-update-border);border-radius:4px;overflow:hidden;font-size:12.5px}',
794
+ '.dsh-upd[data-theme="archive"] .dsh-upd-strip>div{flex:1 1 120px;padding:6px 10px;border-left:1px solid var(--dsh-update-border)}',
704
795
  '.dsh-upd[data-theme="archive"] .dsh-upd-strip>div:first-child{border-left:0}',
705
- '.dsh-upd[data-theme="archive"] .dsh-upd-strip-k{display:block;font-size:11px;letter-spacing:.2em;color:var(--d5-muted)}',
706
- '.dsh-upd[data-theme="archive"] .dsh-upd-strip-v{font-family:var(--d5-mono);font-size:13px}',
796
+ '.dsh-upd[data-theme="archive"] .dsh-upd-strip-k{display:block;font-size:11px;letter-spacing:.2em;color:var(--dsh-update-text-muted)}',
797
+ '.dsh-upd[data-theme="archive"] .dsh-upd-strip-v{font-family:var(--dsh-update-font-mono);font-size:13px}',
707
798
  // —— 章节(原型 :218-245:01–05 编号 + 衬线标题 + 细线)——
708
- '.dsh-upd[data-theme="archive"] .dsh-upd-chapter{margin-top:16px;padding-top:10px;border-top:1px solid var(--d5-line)}',
799
+ '.dsh-upd[data-theme="archive"] .dsh-upd-chapter{margin-top:16px;padding-top:10px;border-top:1px solid var(--dsh-update-border)}',
709
800
  '.dsh-upd[data-theme="archive"] .dsh-upd-chap-head{display:flex;align-items:baseline;gap:12px;margin-bottom:6px}',
710
- '.dsh-upd[data-theme="archive"] .dsh-upd-chap-no{font-family:var(--d5-serif);font-style:italic;font-size:15px;color:var(--d5-muted)}',
711
- '.dsh-upd[data-theme="archive"] .dsh-upd-chap-title{font-family:var(--d5-serif);font-size:17px;margin:0;letter-spacing:.1em}',
712
- '.dsh-upd[data-theme="archive"] .dsh-upd-chap-rule{flex:1;border-top:1px solid var(--d5-line);transform:translateY(-4px)}',
801
+ '.dsh-upd[data-theme="archive"] .dsh-upd-chap-no{font-family:var(--dsh-update-font-serif);font-style:italic;font-size:15px;color:var(--dsh-update-text-muted)}',
802
+ '.dsh-upd[data-theme="archive"] .dsh-upd-chap-title{font-family:var(--dsh-update-font-serif);font-size:17px;margin:0;letter-spacing:.1em}',
803
+ '.dsh-upd[data-theme="archive"] .dsh-upd-chap-rule{flex:1;border-top:1px solid var(--dsh-update-border);transform:translateY(-4px)}',
713
804
  // —— 横幅即状态行 / 待重启横幅(原型 :81-85 `.restart-banner`:2px 边框、圆角 4、内边距 12/16、衬线;右侧留章位)——
714
- '.dsh-upd[data-theme="archive"] .dsh-upd-banner{border:2px solid var(--d5-line-strong);border-radius:4px;padding:10px 12px;font-size:14.5px;font-family:var(--d5-serif);display:flex;gap:10px;align-items:center;flex-wrap:wrap}',
805
+ '.dsh-upd[data-theme="archive"] .dsh-upd-banner{border:2px solid var(--dsh-update-border-strong);border-radius:4px;padding:10px 12px;font-size:14.5px;font-family:var(--dsh-update-font-serif);display:flex;gap:10px;align-items:center;flex-wrap:wrap}',
715
806
  '.dsh-upd[data-theme="archive"] .dsh-upd-banner>div:first-child{flex:1 1 auto;min-width:0}',
716
807
  // 状态行字号照原型 .status-line=27px(实测去掉横幅右侧占位后可写 486px > 428px,一行放得下)
717
- '.dsh-upd[data-theme="archive"] .dsh-upd-banner>div:first-child strong{font-family:var(--d5-serif);font-size:27px;font-weight:700;line-height:1.25}',
808
+ '.dsh-upd[data-theme="archive"] .dsh-upd-banner>div:first-child strong{font-family:var(--dsh-update-font-serif);font-size:27px;font-weight:700;line-height:1.25}',
718
809
  // —— 进度条 / 跳过行(原型 :132-133 `.prog`、:129-131 `.skipline .tag`)——
719
- '.dsh-upd[data-theme="archive"] .dsh-upd-prog{height:8px;background:var(--d5-line);border-radius:4px;overflow:hidden;margin:10px 0 4px}',
720
- '.dsh-upd[data-theme="archive"] .dsh-upd-prog-bar{display:block;height:100%;background:var(--d5-accent);transition:width .3s}',
721
- '.dsh-upd[data-theme="archive"] .dsh-upd-progtxt{font-size:12.5px;color:var(--d5-muted)}',
810
+ '.dsh-upd[data-theme="archive"] .dsh-upd-prog{height:8px;background:var(--dsh-update-border);border-radius:4px;overflow:hidden;margin:10px 0 4px}',
811
+ '.dsh-upd[data-theme="archive"] .dsh-upd-prog-bar{display:block;height:100%;background:var(--dsh-update-primary);transition:width .3s}',
812
+ '.dsh-upd[data-theme="archive"] .dsh-upd-progtxt{font-size:12.5px;color:var(--dsh-update-text-muted)}',
722
813
  '.dsh-upd[data-theme="archive"] .dsh-upd-skipline{font-size:13px;margin-top:8px}',
723
- '.dsh-upd[data-theme="archive"] .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}',
724
- '.dsh-upd[data-theme="archive"] .dsh-upd-err{font-size:13px;margin:0 0 6px;color:var(--d5-muted)}',
725
- '@media (max-width:640px){.dsh-upd[data-theme="archive"]{padding:10px 12px}.dsh-upd[data-theme="archive"]::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="archive"] .dsh-upd-masthead-title{font-size:19px}.dsh-upd[data-theme="archive"] .dsh-upd-banner{padding-right:16px}.dsh-upd[data-theme="archive"] .dsh-upd-banner::before{width:24px;height:24px;font-size:14px;line-height:20px;flex:none}.dsh-upd[data-theme="archive"] .dsh-upd-banner>div:first-child{white-space:normal}}',
726
- '@media (prefers-color-scheme: dark){.dsh-upd[data-theme="archive"]{--d5-bg:#141210;--d5-card:#1e1a15;--d5-ink:#ece5d3;--d5-muted:#a89c83;',
727
- "--d5-line:#3a3226;--d5-line-strong:#5c4e3b;--d5-accent:#e0684e;--d5-accent-deep:#f0866b;",
728
- "--d5-ok:#8fd6a4;--d5-ok-bg:rgba(80,180,120,.12);--d5-warn:#e8c15a;--d5-warn-bg:rgba(232,193,90,.12);",
729
- "--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)}}",
730
- '@media (prefers-color-scheme: dark){.dsh-upd[data-theme="archive"] button[data-primary="1"]{color:#141210}.dsh-upd[data-theme="archive"] button:focus-visible{outline-color:var(--d5-accent-deep)}}',
814
+ '.dsh-upd[data-theme="archive"] .dsh-upd-tag{display:inline-block;border:1px dashed var(--dsh-update-border-strong);border-radius:3px;padding:1px 8px;margin-right:8px;font-family:var(--dsh-update-font-mono);font-size:12px}',
815
+ '.dsh-upd[data-theme="archive"] .dsh-upd-err{font-size:13px;margin:0 0 6px;color:var(--dsh-update-text-muted)}',
816
+ '@media (max-width:640px){.dsh-upd[data-theme="archive"]{padding:10px 12px}.dsh-upd[data-theme="archive"]::before{top:12px;right:12px;width:56px;height:56px;font-size:15px;box-shadow:inset 0 0 0 4px var(--dsh-update-bg),inset 0 0 0 5px currentColor}.dsh-upd[data-theme="archive"] .dsh-upd-masthead-title{font-size:19px}.dsh-upd[data-theme="archive"] .dsh-upd-banner{padding-right:16px}.dsh-upd[data-theme="archive"] .dsh-upd-banner::before{width:24px;height:24px;font-size:14px;line-height:20px;flex:none}.dsh-upd[data-theme="archive"] .dsh-upd-banner>div:first-child{white-space:normal}}',
817
+ '@media (prefers-color-scheme: dark){.dsh-upd[data-theme="archive"]{--dsh-update-bg-soft:#141210;--dsh-update-bg:#1e1a15;--dsh-update-text:#ece5d3;--dsh-update-text-muted:#a89c83;',
818
+ "--dsh-update-border:#3a3226;--dsh-update-border-strong:#5c4e3b;--dsh-update-primary:#e0684e;--dsh-update-primary-deep:#f0866b;",
819
+ "--dsh-update-ok-text:#8fd6a4;--dsh-update-ok-bg:rgba(80,180,120,.12);--dsh-update-warn-text:#e8c15a;--dsh-update-warn-bg:rgba(232,193,90,.12);",
820
+ "--dsh-update-bad-text:#ef8a7d;--dsh-update-bad-bg:rgba(239,138,125,.12);--dsh-update-shadow:0 1px 2px rgba(0,0,0,.4),0 12px 32px rgba(0,0,0,.45)}}",
821
+ '@media (prefers-color-scheme: dark){.dsh-upd[data-theme="archive"] button[data-primary="1"]{color:#141210}.dsh-upd[data-theme="archive"] button:focus-visible{outline-color:var(--dsh-update-primary-deep)}}',
731
822
  '@media (prefers-color-scheme: dark){.dsh-upd[data-theme="archive"] .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")}}',
732
823
  '@media (forced-colors: active){.dsh-upd[data-theme="archive"]{box-shadow:none}.dsh-upd[data-theme="archive"]::before{background:none;box-shadow:none;border-color:CanvasText;color:CanvasText}.dsh-upd[data-theme="archive"] .dsh-upd-banner{border:1px solid CanvasText}.dsh-upd[data-theme="archive"] .dsh-upd-banner::before{border-color:CanvasText;color:CanvasText;background:Canvas}.dsh-upd[data-theme="archive"] button{border:1px solid ButtonText}.dsh-upd[data-theme="archive"] button[data-primary="1"]{background:ButtonFace;color:ButtonText;border-color:ButtonText}.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="restart"]>div:first-child::before{background-image:none;content:"\u26A0"}}',
733
824
  '@media (prefers-reduced-motion: reduce){.dsh-upd[data-theme="archive"] *{transition:none !important;animation:none !important}}'
@@ -735,58 +826,83 @@ const UPDATE_PANEL_D5_CSS = [
735
826
  function escapeHtml(text) {
736
827
  return String(text).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
737
828
  }
738
- const CHAPTER_TITLES = ["\u68C0\u67E5\u4E0E\u5B89\u88C5", "\u66F4\u65B0\u65E5\u5FD7", "\u66F4\u65B0\u961F\u5217", "\u9519\u8BEF\u4FE1\u606F", "\u624B\u5DE5\u547D\u4EE4"];
739
- function formatLatchTime(atMs) {
829
+ const CHAPTER_KEYS = [
830
+ "panel.chapter.check",
831
+ "panel.chapter.changelog",
832
+ "panel.chapter.queue",
833
+ "panel.chapter.error",
834
+ "panel.chapter.manual"
835
+ ];
836
+ function chapterTitle(index, lang) {
837
+ const l = normalizeLangTag(lang ?? "zh");
838
+ return copyText(CHAPTER_KEYS[index - 1], l);
839
+ }
840
+ function formatLatchTime(atMs, lang) {
740
841
  if (typeof atMs !== "number" || !Number.isFinite(atMs) || atMs <= 0) return null;
741
- try {
742
- const s = new Date(atMs).toLocaleTimeString("zh-CN", { hour12: false });
743
- return s ? s : null;
744
- } catch {
745
- return null;
842
+ const raw = typeof lang === "string" ? lang.trim().replace(/_/g, "-") : "";
843
+ const l = normalizeLangTag(lang ?? "zh");
844
+ const primary = l === "en" ? "en-US" : "zh-CN";
845
+ const fallback = l === "en" ? "zh-CN" : "en-US";
846
+ const candidates = [];
847
+ if (raw && raw !== primary && /^[A-Za-z]{2,3}(-[A-Za-z0-9]+)*$/.test(raw)) candidates.push(raw);
848
+ candidates.push(primary, fallback);
849
+ for (const tag of candidates) {
850
+ try {
851
+ const s = new Date(atMs).toLocaleTimeString(tag, { hour12: false });
852
+ if (s) return s;
853
+ } catch {
854
+ }
746
855
  }
856
+ return null;
747
857
  }
748
- function chapterOf(index, inner, note = "") {
858
+ function chapterOf(index, inner, note = "", lang) {
749
859
  const no = String(index).padStart(2, "0");
750
- const title = CHAPTER_TITLES[index - 1];
860
+ const l = normalizeLangTag(lang ?? "zh");
861
+ const title = chapterTitle(index, l);
751
862
  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>`;
752
863
  }
753
- function skeletonStrip(loading) {
864
+ function skeletonStrip(loading, lang) {
754
865
  if (!loading) return "";
866
+ const l = normalizeLangTag(lang ?? "zh");
755
867
  const cell = (k) => `<div><span class="dsh-upd-strip-k">${escapeHtml(k)}</span><span class="dsh-upd-strip-v dsh-upd-skv" aria-hidden="true">\u2026</span></div>`;
756
- return `<div class="dsh-upd-strip" aria-hidden="true">` + cell("\u8FD0\u884C") + cell("\u78C1\u76D8") + cell("\u8FDC\u7AEF") + `</div>`;
868
+ return `<div class="dsh-upd-strip" aria-hidden="true">` + cell(copyText("panel.strip.running", l)) + cell(copyText("panel.strip.installed", l)) + cell(copyText("panel.strip.latest", l)) + `</div>`;
757
869
  }
758
- function versionStrip(snapshot) {
759
- 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>`;
870
+ function versionStrip(snapshot, lang) {
871
+ const l = normalizeLangTag(lang ?? "zh");
872
+ const unknown = copyText("panel.meta.unknown", l);
873
+ const cell = (k, v) => `<div><span class="dsh-upd-strip-k">${escapeHtml(k)}</span><span class="dsh-upd-strip-v">${escapeHtml(v ?? unknown)}</span></div>`;
760
874
  if (!snapshot) return "";
761
- return `<div class="dsh-upd-strip">` + cell("\u8FD0\u884C", snapshot.runningVersion) + cell("\u78C1\u76D8", snapshot.installedVersion) + cell("\u8FDC\u7AEF", snapshot.latestVersion) + `</div>`;
875
+ return `<div class="dsh-upd-strip">` + cell(copyText("panel.strip.running", l), snapshot.runningVersion) + cell(copyText("panel.strip.installed", l), snapshot.installedVersion) + cell(copyText("panel.strip.latest", l), snapshot.latestVersion) + `</div>`;
762
876
  }
763
- function progressBar(snapshot) {
877
+ function progressBar(snapshot, lang) {
764
878
  const job = snapshot?.job;
765
879
  if (!job) return "";
766
880
  const state = String(job.state);
767
881
  if (state !== "installing" && state !== "verifying") return "";
882
+ const l = normalizeLangTag(lang ?? "zh");
768
883
  const width = state === "installing" ? 60 : 90;
769
- const text = state === "installing" ? "\u6B63\u5728\u5B89\u88C5\u65B0\u7248\u2026" : "\u6B63\u5728\u6821\u9A8C\u5B89\u88C5\u7ED3\u679C\u2026";
884
+ const text = state === "installing" ? copyText("panel.progress.installing", l) : copyText("panel.progress.verifying", l);
770
885
  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>`;
771
886
  }
772
- function renderUpdatePanelKernel(input, view) {
887
+ function renderUpdatePanelKernel(input, view, lang) {
773
888
  const { snapshot, manual, queue, mode, showOthers, pluginId, copyNotice } = input;
889
+ const l = normalizeLangTag(lang ?? input.lang ?? "zh");
774
890
  const changelogMarkdown = input.changelogMarkdown ?? null;
775
891
  const profileName = typeof input.profileName === "string" && input.profileName ? input.profileName : null;
776
892
  const b = view.banner;
777
893
  const seal = view.seal;
778
894
  const parts = [];
779
895
  parts.push(
780
- '<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>'
896
+ '<div class="dsh-upd-masthead"><span class="dsh-upd-masthead-kicker">' + escapeHtml(copyText("panel.masthead.kicker", l)) + '</span><span class="dsh-upd-masthead-title">' + escapeHtml(copyText("panel.masthead.title", l)) + " <i>" + escapeHtml(copyText("panel.masthead.volume", l)) + "</i></span></div>"
781
897
  );
782
898
  parts.push(
783
- `<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>`
899
+ `<div class="dsh-upd-head"><span class="dsh-upd-name">${escapeHtml(pluginId)}</span><span class="dsh-upd-meta">${escapeHtml(copyText("panel.meta.label", l))} <b>${escapeHtml(profileName ?? copyText("panel.meta.unknown", l))}</b><span class="dsh-upd-proftag">profile</span></span></div>`
784
900
  );
785
901
  parts.push(`<div class="dsh-upd-banner" data-kind="${b.kind}" data-mini="${escapeHtml(seal.mini)}" role="status" aria-live="polite">`);
786
902
  parts.push(`<div><strong>${escapeHtml(b.title)}</strong></div>`);
787
903
  if (b.action) parts.push(`<div>${escapeHtml(b.action)}</div>`);
788
904
  parts.push("</div>");
789
- parts.push(snapshot ? versionStrip(snapshot) : skeletonStrip(view.banner.kind === "loading"));
905
+ parts.push(snapshot ? versionStrip(snapshot, l) : skeletonStrip(view.banner.kind === "loading", l));
790
906
  const showActions = input.actions !== "none";
791
907
  const actions = [];
792
908
  if (showActions) {
@@ -795,36 +911,36 @@ function renderUpdatePanelKernel(input, view) {
795
911
  const installBusy = busyAct === "install";
796
912
  actions.push('<div class="dsh-upd-actions">');
797
913
  actions.push(
798
- (checkBusy ? `<button type="button" data-action="check" disabled aria-busy="true" title="\u6B63\u5728\u5411\u5B98\u65B9\u6E90\u67E5\u8BE2\uFF0C\u8BF7\u7A0D\u5019">\u6B63\u5728\u67E5\u65B0\u7248\u2026</button>` : `<button type="button" data-action="check" title="\u91CD\u65B0\u5411\u5B98\u65B9\u6E90\u67E5\u4E00\u6B21\u65B0\u7248\uFF08\u53EA\u8BFB\uFF0C\u4E0D\u5B89\u88C5\uFF09">\u67E5\u65B0\u7248</button>`) + (installBusy ? `<button type="button" data-action="install" data-primary="1" disabled aria-busy="true" title="\u6B63\u5728\u5B89\u88C5\uFF0C\u8BF7\u7A0D\u5019">\u6B63\u5728\u5B89\u88C5\u2026</button>` : `<button type="button" data-action="install" data-primary="1" title="\u7528\u7CBE\u786E\u7248\u672C\u5B89\u88C5\uFF1B\u540C\u4E00\u4F7F\u7528\u8303\u56F4\u540C\u65F6\u53EA\u88C5\u4E00\u4E2A"${view.installEnabled ? "" : " disabled"}>${escapeHtml(view.installLabel)}</button>`)
914
+ (checkBusy ? `<button type="button" data-action="check" disabled aria-busy="true" title="${escapeHtml(copyText("panel.action.checking-busy-title", l))}">${escapeHtml(copyText("panel.action.checking-busy", l))}</button>` : `<button type="button" data-action="check" title="${escapeHtml(copyText("panel.action.check-title", l))}">${escapeHtml(copyText("panel.action.check", l))}</button>`) + (installBusy ? `<button type="button" data-action="install" data-primary="1" disabled aria-busy="true" title="${escapeHtml(copyText("panel.action.installing-busy-title", l))}">${escapeHtml(copyText("panel.action.installing-busy", l))}</button>` : `<button type="button" data-action="install" data-primary="1" title="${escapeHtml(copyText("panel.action.install-title", l))}"${view.installEnabled ? "" : " disabled"}>${escapeHtml(view.installLabel)}</button>`)
799
915
  );
800
916
  if (snapshot?.latestVersion && !view.skippedLatest && view.banner.kind === "update") {
801
- actions.push(`<button type="button" data-action="skip" title="\u8BE5\u7248\u672C\u4E0D\u518D\u63D0\u9192\uFF1B\u6709\u66F4\u65B0\u7684\u65B0\u7248\u672C\u7167\u5E38\u63D0\u9192">\u8DF3\u8FC7\u8BE5\u7248\u672C</button>`);
917
+ actions.push(`<button type="button" data-action="skip" title="${escapeHtml(copyText("panel.action.skip-title", l))}">${escapeHtml(copyText("panel.action.skip", l))}</button>`);
802
918
  }
803
919
  if (view.showReset && snapshot?.latestVersion) {
804
- actions.push(`<button type="button" data-action="reset-skip" title="\u64A4\u9500\u8DF3\u8FC7\uFF0C\u8BE5\u7248\u672C\u91CD\u65B0\u63D0\u9192">\u6062\u590D\uFF08${escapeHtml(snapshot.latestVersion)}\uFF09</button>`);
920
+ actions.push(`<button type="button" data-action="reset-skip" title="${escapeHtml(copyText("panel.action.unskip-title", l))}">${escapeHtml(copyText("panel.action.unskip", l, { version: snapshot.latestVersion }))}</button>`);
805
921
  }
806
922
  if (view.showManual && manual) {
807
- actions.push(`<button type="button" data-action="copy-manual" title="\u590D\u5236\u624B\u5DE5\u547D\u4EE4\uFF0C\u7C98\u5230\u7EC8\u7AEF\u6574\u884C\u6267\u884C">\u590D\u5236\u624B\u5DE5\u547D\u4EE4</button>`);
923
+ actions.push(`<button type="button" data-action="copy-manual" title="${escapeHtml(copyText("panel.action.copy-manual-title", l))}">${escapeHtml(copyText("panel.action.copy-manual", l))}</button>`);
808
924
  }
809
925
  if (b.kind === "restart") {
810
- actions.push(`<button type="button" data-action="restart-hint" data-primary="1" title="\u5BBF\u4E3B\u6CA1\u6709\u81EA\u91CD\u542F\u7535\u8BDD\uFF1A\u8BF7\u624B\u52A8\u91CD\u542F\u5BBF\u4E3B">\u91CD\u542F\u5BBF\u4E3B</button>`);
926
+ actions.push(`<button type="button" data-action="restart-hint" data-primary="1" title="${escapeHtml(copyText("panel.action.restart-host-title", l))}">${escapeHtml(copyText("panel.action.restart-host", l))}</button>`);
811
927
  }
812
928
  if (b.kind === "failed") {
813
- actions.push(`<button type="button" data-action="dismiss-failure" title="\u786E\u8BA4\u5DF2\u77E5\u6653\u8BE5\u5931\u8D25\uFF1A\u56DE\u5230\u53EF\u88C5\u9875\uFF0C\u4E0B\u6B21\u67E5/\u88C5\u5C06\u91CD\u65B0\u8BC4\u4F30">\u77E5\u9053\u4E86</button>`);
929
+ actions.push(`<button type="button" data-action="dismiss-failure" title="${escapeHtml(copyText("panel.action.dismiss-title", l))}">${escapeHtml(copyText("panel.action.dismiss", l))}</button>`);
814
930
  }
815
931
  if (snapshot) {
816
- actions.push(`<button type="button" data-action="copy-diag" title="\u590D\u5236\u5DF2\u8131\u654F\u8BCA\u65AD\uFF0C\u76F4\u63A5\u7C98\u7ED9\u63D2\u4EF6\u4F5C\u8005">\u590D\u5236\u8BCA\u65AD</button>`);
932
+ actions.push(`<button type="button" data-action="copy-diag" title="${escapeHtml(copyText("panel.action.copy-diag-title", l))}">${escapeHtml(copyText("panel.action.copy-diag", l))}</button>`);
817
933
  }
818
934
  actions.push("</div>");
819
935
  }
820
- actions.push(progressBar(snapshot));
936
+ actions.push(progressBar(snapshot, l));
821
937
  if (view.skippedLatest && snapshot?.latestVersion) {
822
938
  actions.push(
823
- `<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>`
939
+ `<div class="dsh-upd-skipline"><span class="dsh-upd-tag">${escapeHtml(copyText("panel.skip.line-tag", l, { version: snapshot.latestVersion }))}</span>${escapeHtml(copyText("panel.skip.line-note", l))}</div>`
824
940
  );
825
941
  }
826
942
  const chapters = [];
827
- chapters.push(chapterOf(1, actions.join("")));
943
+ chapters.push(chapterOf(1, actions.join(""), "", l));
828
944
  {
829
945
  let inner = "";
830
946
  let hasLog = false;
@@ -838,10 +954,10 @@ function renderUpdatePanelKernel(input, view) {
838
954
  snapshot.latestVersion,
839
955
  snapshot.installedVersion
840
956
  );
841
- const changelogHTML = renderChangelogHTML(ranged);
957
+ const changelogHTML = renderChangelogHTML(ranged, { lang: l });
842
958
  const fromText = String(snapshot.runningVersion ?? "");
843
959
  const toText = String(snapshot.latestVersion ?? "");
844
- const rangeTitle = fromText && toText ? `\u66F4\u65B0\u8BF4\u660E\uFF08${fromText} \u2192 ${toText}\uFF09\uFF1A` : "\u66F4\u65B0\u8BF4\u660E\uFF1A";
960
+ const rangeTitle = fromText && toText ? copyText("panel.changelog.heading-range", l, { from: fromText, to: toText }) : copyText("panel.changelog.heading", l);
845
961
  let yankedBanner = "";
846
962
  try {
847
963
  const toEntry = Array.isArray(ranged) ? ranged.find(function(e) {
@@ -852,7 +968,7 @@ function renderUpdatePanelKernel(input, view) {
852
968
  }
853
969
  }) : null;
854
970
  if (toEntry && toEntry.yanked === true && toText) {
855
- yankedBanner = yankedBannerHTML(toText);
971
+ yankedBanner = yankedBannerHTML(toText, l);
856
972
  }
857
973
  } catch {
858
974
  yankedBanner = "";
@@ -868,12 +984,12 @@ ${changelogHTML}
868
984
  }
869
985
  if (!inner) {
870
986
  inner = `<div class="dsh-upd-changelog-wrap"><div class="dsh-upd-changelog-neutral">${escapeHtml(
871
- 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"
987
+ snapshot && snapshot.latestVersion ? copyText("panel.changelog.unavailable", l) : copyText("panel.changelog.unavailable-empty", l)
872
988
  )}</div></div>`;
873
989
  }
874
990
  const logCollapsed = input.changelogCollapsed === true;
875
- const logNote = showActions && hasLog ? `<span class="dsh-upd-chap-note"><button type="button" data-action="toggle-changelog" title="\u5C55\u5F00\u6216\u6536\u8D77\u66F4\u65B0\u65E5\u5FD7">${logCollapsed ? "\u5C55\u5F00\u66F4\u65B0\u65E5\u5FD7" : "\u6536\u8D77\u66F4\u65B0\u65E5\u5FD7"}</button></span>` : "";
876
- chapters.push(chapterOf(2, inner, logNote));
991
+ const logNote = showActions && hasLog ? '<span class="dsh-upd-chap-note"><button type="button" data-action="toggle-changelog" title="' + escapeHtml(copyText("panel.changelog.toggle-title", l)) + `">${escapeHtml(logCollapsed ? copyText("panel.changelog.expand", l) : copyText("panel.changelog.collapse", l))}</button></span>` : "";
992
+ chapters.push(chapterOf(2, inner, logNote, l));
877
993
  }
878
994
  {
879
995
  const queued = !!queue && (queue.busy || queue.waiting.length > 0);
@@ -884,30 +1000,32 @@ ${changelogHTML}
884
1000
  const version = ownerRaw && "targetVersion" in ownerRaw && ownerRaw.targetVersion ? `@${String(ownerRaw.targetVersion)}` : "";
885
1001
  const aboutSelf = named !== null && named === pluginId;
886
1002
  const reveal = showOthers === true;
887
- const ownerShown = !busy ? "\u7A7A\u95F2" : named === null ? "\u5176\u4ED6\u63D2\u4EF6" : aboutSelf ? "\u672C\u63D2\u4EF6" : reveal ? named : "\u5176\u4ED6\u63D2\u4EF6";
1003
+ const ownerShown = !busy ? copyText("panel.queue.state-idle", l) : named === null ? copyText("panel.queue.other", l) : aboutSelf ? copyText("panel.queue.self", l) : reveal ? named : copyText("panel.queue.other", l);
888
1004
  const ownerVer = busy && named !== null && (aboutSelf || reveal) ? version : "";
889
1005
  const pos = typeof queue.position === "number" ? queue.position : null;
890
- const posText = pos === null ? "\u672A\u6392\u961F" : `\u7B2C ${pos} \u4F4D`;
891
- const posNote = pos === null ? "" : pos === 1 ? "\u4E0B\u4E00\u4E2A\u5C31\u662F\u4F60" : `\u524D\u65B9 ${pos - 1} \u4E2A`;
1006
+ const posText = pos === null ? copyText("panel.queue.pos-absent", l) : copyText("panel.queue.pos-n", l, { n: pos });
1007
+ const posNote = pos === null ? "" : pos === 1 ? copyText("panel.queue.pos-next", l) : copyText("panel.queue.pos-ahead", l, { n: pos - 1 });
892
1008
  const rows = [
893
- `<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>`,
894
- `<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>`
1009
+ `<div class="dsh-upd-qrow"><span class="dsh-upd-qdot" data-tone="${busy ? "busy" : "idle"}"></span><span class="dsh-upd-qk">${escapeHtml(copyText("panel.queue.row-installing", l))}</span><span class="dsh-upd-qv">${escapeHtml(ownerShown + ownerVer)}</span><span class="dsh-upd-qn">${escapeHtml(busy ? copyText("panel.queue.row-installing-note", l) : copyText("panel.queue.row-idle-note", l))}</span></div>`,
1010
+ `<div class="dsh-upd-qrow"><span class="dsh-upd-qdot" data-tone="you"></span><span class="dsh-upd-qk">${escapeHtml(copyText("panel.queue.row-position", l))}</span><span class="dsh-upd-qv">${escapeHtml(posText)}</span><span class="dsh-upd-qn">${escapeHtml(posNote)}</span></div>`
895
1011
  ];
896
1012
  if (reveal && queue.waiting.length > 0) {
897
1013
  const seq = queue.waiting.map((e) => `${e.pluginId}${e.targetVersion ? `@${e.targetVersion}` : ""}`).join(" \u2192 ");
898
1014
  rows.push(
899
- `<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>`
1015
+ `<div class="dsh-upd-qrow"><span class="dsh-upd-qdot"></span><span class="dsh-upd-qk">${escapeHtml(copyText("panel.queue.row-order", l))}</span><span class="dsh-upd-qseq">${escapeHtml(seq)}</span></div>`
900
1016
  );
901
1017
  }
902
- 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>` : "";
903
- chapters.push(chapterOf(3, `<div class="dsh-upd-queue">${rows.join("")}</div>`, note));
1018
+ const note = showActions ? `<span class="dsh-upd-chap-note"><button type="button" data-action="toggle-queue">${escapeHtml(reveal ? copyText("panel.queue.toggle-hide", l) : copyText("panel.queue.toggle-show", l))}</button></span>` : "";
1019
+ chapters.push(chapterOf(3, `<div class="dsh-upd-queue">${rows.join("")}</div>`, note, l));
904
1020
  } else {
905
1021
  chapters.push(
906
1022
  chapterOf(
907
1023
  3,
908
1024
  `<div class="dsh-upd-queue"><div class="dsh-upd-changelog-neutral">${escapeHtml(
909
- view.queueNote ?? "\u5F53\u524D\u6CA1\u6709\u6392\u961F\u4EFB\u52A1\uFF0C\u540C\u4E00\u4F7F\u7528\u8303\u56F4\u4E00\u6B21\u53EA\u88C5\u4E00\u4E2A\u3002"
910
- )}</div></div>`
1025
+ view.queueNote ?? copyText("panel.queue.empty", l)
1026
+ )}</div></div>`,
1027
+ "",
1028
+ l
911
1029
  )
912
1030
  );
913
1031
  }
@@ -919,70 +1037,84 @@ ${changelogHTML}
919
1037
  if (failedNow) {
920
1038
  const shownCode = b.kind === "blocked" ? snapshot?.blockedReason ?? b.kind : typeof input.lastError === "string" && input.lastError.trim() ? input.lastError : "install-failed";
921
1039
  errLines.push(
922
- `<div class="dsh-upd-err">\u7A33\u5B9A\u7801 <code>${escapeHtml(String(shownCode))}</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>`
1040
+ `<div class="dsh-upd-err">${escapeHtml(copyText("panel.error.code-label", l))} <code>${escapeHtml(String(shownCode))}</code>${l === "en" ? ":" : "\uFF1A"}${escapeHtml(copyText("panel.error.code-note", l))}</div>`
923
1041
  );
924
1042
  if (b.kind === "failed" && failRef) {
925
1043
  const keys = [];
926
- if (typeof failRef.requestId === "string" && failRef.requestId) keys.push(`\u8BF7\u6C42 <code>${escapeHtml(failRef.requestId)}</code>`);
927
- if (typeof failRef.checkId === "string" && failRef.checkId) keys.push(`\u68C0\u67E5 <code>${escapeHtml(failRef.checkId)}</code>`);
928
- const at = formatLatchTime(failRef.atMs);
929
- if (at) keys.push(`\u5931\u8D25\u4E8E ${escapeHtml(at)}`);
1044
+ if (typeof failRef.requestId === "string" && failRef.requestId) keys.push(`${escapeHtml(copyText("panel.error.query-request", l))} <code>${escapeHtml(failRef.requestId)}</code>`);
1045
+ if (typeof failRef.checkId === "string" && failRef.checkId) keys.push(`${escapeHtml(copyText("panel.error.query-check", l))} <code>${escapeHtml(failRef.checkId)}</code>`);
1046
+ const at = formatLatchTime(failRef.atMs, input.lang ?? l);
1047
+ if (at) keys.push(`${escapeHtml(copyText("panel.error.failed-at", l, { time: at }))}`);
930
1048
  if (keys.length > 0) {
931
- errLines.push(`<div class="dsh-upd-err">\u672C\u6B21\u67E5\u8BE2\u952E\uFF1A${keys.join(" \xB7 ")}\uFF08\u62FF\u7740\u5B83\u4EEC\u53BB\u65E5\u5FD7\u91CC\u5BF9\uFF09\u3002</div>`);
1049
+ errLines.push(`<div class="dsh-upd-err">${copyText("panel.error.query-keys", l, { keys: keys.join(" \xB7 ") })}</div>`);
932
1050
  }
933
1051
  if (!failRef.volatile) {
934
1052
  errLines.push(
935
- `<div class="dsh-upd-err">\u8BC1\u636E\u5DF2\u51BB\u7ED3\uFF1A\u590D\u5236\u8BCA\u65AD\u91CC\u7684\u7801\u3001\u7248\u672C\u3001\u7F16\u53F7\u90FD\u53D6\u81EA\u5931\u8D25\u65F6\u523B\uFF0C\u4E0D\u968F\u8F6E\u8BE2\u5237\u65B0\uFF1B\u4E0B\u4E00\u6B21\u67E5\u65B0\u7248\u6216\u5B89\u88C5\u4F1A\u66F4\u65B0\u5B83\u3002</div>`
1053
+ `<div class="dsh-upd-err">${escapeHtml(copyText("panel.error.evidence-frozen", l))}</div>`
936
1054
  );
937
1055
  } else {
938
1056
  errLines.push(
939
- `<div class="dsh-upd-err">\u8BFB\u6570\u77AC\u6001\u5931\u8D25\uFF1A\u4E0B\u4E00\u6B21\u6210\u529F\u8BFB\u6570\u4F1A\u81EA\u52A8\u89E3\u9664\uFF1B\u4E00\u76F4\u51FA\u73B0\u518D\u6309\u7A33\u5B9A\u7801\u6392\u67E5\u3002</div>`
1057
+ `<div class="dsh-upd-err">${escapeHtml(copyText("panel.error.evidence-transient", l))}</div>`
940
1058
  );
941
1059
  }
942
1060
  }
943
1061
  } else {
944
- errLines.push(`<div class="dsh-upd-changelog-neutral">\u6682\u65E0\u5931\u8D25\uFF1A\u6B64\u65F6\u590D\u5236\u8BCA\u65AD\u7ED9\u51FA\u7684\u662F\u5F53\u524D\u72B6\u6001\u5FEB\u7167\u3002</div>`);
1062
+ errLines.push(`<div class="dsh-upd-changelog-neutral">${escapeHtml(copyText("panel.error.no-failure", l))}</div>`);
945
1063
  }
946
1064
  if (input.showLogHint !== false) {
947
1065
  errLines.push(
948
- `<div class="dsh-upd-log">\u6DF1\u6316\u770B\u65E5\u5FD7\uFF1A\u6309\u63D2\u4EF6\u6807\u8BC6 <code>${escapeHtml(pluginId)}</code> \u8FC7\u6EE4 <code>${LOG_EVENT_CALL}</code>\u3001<code>${LOG_EVENT_CALL_FAIL}</code>\u3001<code>${LOG_EVENT_INSTALL_EXEC}</code> \u4E09\u4E2A\u4E8B\u4EF6\u3002</div>`
1066
+ `<div class="dsh-upd-log">${copyText("panel.error.log-hint", l, {
1067
+ pluginId: `<code>${escapeHtml(pluginId)}</code>`,
1068
+ e1: `<code>${LOG_EVENT_CALL}</code>`,
1069
+ e2: `<code>${LOG_EVENT_CALL_FAIL}</code>`,
1070
+ e3: `<code>${LOG_EVENT_INSTALL_EXEC}</code>`
1071
+ })}</div>`
949
1072
  );
950
1073
  if (b.kind === "failed" && failRef && (failRef.requestId || failRef.checkId)) {
951
1074
  errLines.push(
952
- `<div class="dsh-upd-log">\u51ED\u4E0A\u9762\u7684\u8BF7\u6C42\uFF0F\u68C0\u67E5\u7F16\u53F7\u5728 <code>${LOG_EVENT_CALL_FAIL}</code> \u91CC\u5BF9\u4E0A\uFF1B\u57FA\u7EBF\u8017\u65F6\u770B <code>${LOG_EVENT_CALL}</code>\uFF0C\u6267\u884C\u7ED3\u679C\u770B <code>${LOG_EVENT_INSTALL_EXEC}</code>\u3002</div>`
1075
+ `<div class="dsh-upd-log">${copyText("panel.error.log-follow", l, {
1076
+ eFail: `<code>${LOG_EVENT_CALL_FAIL}</code>`,
1077
+ eCall: `<code>${LOG_EVENT_CALL}</code>`,
1078
+ eExec: `<code>${LOG_EVENT_INSTALL_EXEC}</code>`
1079
+ })}</div>`
953
1080
  );
954
1081
  }
955
1082
  }
956
1083
  if (copyNotice) errLines.push(`<div class="dsh-upd-copy" role="status">${escapeHtml(copyNotice)}</div>`);
957
- chapters.push(chapterOf(4, errLines.join("")));
1084
+ chapters.push(chapterOf(4, errLines.join(""), "", l));
958
1085
  }
959
1086
  chapters.push(
960
1087
  chapterOf(
961
1088
  5,
962
- 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>`
1089
+ view.showManual && manual ? `<div class="dsh-upd-manual"><div>${escapeHtml(copyText("panel.manual.heading", l))}</div><code>${escapeHtml(manual)}</code></div>` : `<div class="dsh-upd-manual"><div class="dsh-upd-changelog-neutral">${escapeHtml(copyText("panel.manual.absent", l))}</div></div>`,
1090
+ "",
1091
+ l
963
1092
  )
964
1093
  );
965
1094
  parts.push('<div class="dsh-upd-body">' + chapters.join("\n") + "</div>");
966
1095
  if (showActions && mode === "dialog") {
967
1096
  parts.push(
968
- `<div class="dsh-upd-footer"><span class="dsh-upd-foot-note">\u5173\u95ED\u4E0D\u5F71\u54CD\u66F4\u65B0\uFF0C\u53EF\u968F\u65F6\u56DE\u6765\u67E5\u770B</span><button type="button" data-action="close-view" title="\u5173\u95ED\u7A97\u53E3\uFF0C\u66F4\u65B0\u4E0D\u53D7\u5F71\u54CD">\u5173\u95ED</button></div>`
1097
+ '<div class="dsh-upd-footer"><span class="dsh-upd-foot-note">' + escapeHtml(copyText("panel.footer.note", l)) + `</span><button type="button" data-action="close-view" title="${escapeHtml(copyText("panel.footer.close-title", l))}">${escapeHtml(copyText("panel.footer.close", l))}</button></div>`
969
1098
  );
970
1099
  }
971
1100
  return parts.join("\n");
972
1101
  }
973
- function renderUpdatePanelHTML(input) {
974
- const view = panelViewModel(input);
975
- const kernel = renderUpdatePanelKernel(input, view);
976
- const d5 = normalizePanelTheme(input.theme) === "archive";
977
- const attr = d5 ? ' data-theme="archive"' : "";
1102
+ function renderUpdatePanelHTML(input, lang) {
1103
+ const l = normalizeLangTag(lang ?? input.lang ?? "zh");
1104
+ const view = panelViewModel(input, l);
1105
+ const kernel = renderUpdatePanelKernel(input, view, l);
1106
+ const archive = normalizePanelTheme(input.theme) === "archive";
1107
+ const attr = archive ? ' data-theme="archive"' : "";
1108
+ const tokensStyle = themeTokensStyleFor(input.themeTokens ?? void 0);
1109
+ const tokensAttr = tokensStyle ? ` style="${tokensStyle}"` : "";
978
1110
  const sealAttr = ` data-seal="${escapeHtml(view.seal.text)}" data-seal-tone="${view.seal.tone}"`;
979
- 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}>
1111
+ 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}${tokensAttr}>
980
1112
  ${kernel}
981
- </div></div>` : `<div class="dsh-upd" data-mode="embedded" data-plugin="${escapeHtml(input.pluginId)}"${sealAttr}${attr}>
1113
+ </div></div>` : `<div class="dsh-upd" data-mode="embedded" data-plugin="${escapeHtml(input.pluginId)}"${sealAttr}${attr}${tokensAttr}>
982
1114
  ${kernel}
983
1115
  </div>`;
984
- const css = d5 ? `${UPDATE_PANEL_CSS}
985
- ${UPDATE_PANEL_D5_CSS}` : UPDATE_PANEL_CSS;
1116
+ const css = archive ? `${UPDATE_PANEL_CSS}
1117
+ ${UPDATE_PANEL_ARCHIVE_CSS}` : UPDATE_PANEL_CSS;
986
1118
  return `<style>${css}</style>
987
1119
  ${body}`;
988
1120
  }
@@ -1076,17 +1208,33 @@ function mountUpdatePanel(container, options) {
1076
1208
  if (mode !== "embedded" && mode !== "dialog") {
1077
1209
  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`);
1078
1210
  }
1079
- if (options.theme !== void 0 && options.theme !== "default" && options.theme !== "archive" && options.theme !== "d5-paper") {
1080
- throw new Error(`[dsh-plugin-update] \u4E3B\u9898\u975E\u6CD5\uFF1A\u53EA\u6536 default \u6216 archive\uFF08d5-paper \u4E3A\u65E7\u522B\u540D\u4ECD\u53EF\u7528\uFF09\uFF08\u6536\u5230 ${JSON.stringify(options.theme)}\uFF09`);
1211
+ if (options.theme !== void 0 && options.theme !== "default" && options.theme !== "archive") {
1212
+ throw new Error(`[dsh-plugin-update] \u4E3B\u9898\u975E\u6CD5\uFF1A\u53EA\u6536 default \u6216 archive\uFF08\u6536\u5230 ${JSON.stringify(options.theme)}\uFF09`);
1081
1213
  }
1082
1214
  let theme = normalizePanelTheme(options.theme ?? "default");
1215
+ themeTokensStyleFor(options.themeTokens ?? void 0);
1216
+ let themeTokens = options.themeTokens;
1217
+ const localeOpt = options.locale ?? void 0;
1218
+ if (localeOpt !== void 0 && localeOpt !== null) {
1219
+ const isStr = typeof localeOpt === "string";
1220
+ const isObj = typeof localeOpt === "object" && typeof localeOpt.getActive === "function";
1221
+ if (!isStr && !isObj) {
1222
+ throw new Error("[dsh-plugin-update] invalid locale: expected zh / en / BCP47 or { getActive(), subscribe? }");
1223
+ }
1224
+ if (isStr && !localeOpt.trim()) {
1225
+ throw new Error("[dsh-plugin-update] invalid locale: empty string");
1226
+ }
1227
+ }
1228
+ function currentLang() {
1229
+ return resolveLang(localeOpt);
1230
+ }
1083
1231
  let showOthers = options.showOthers === true;
1084
1232
  let changelogCollapsed = false;
1085
1233
  const call = options.call;
1086
1234
  const onRestartRequested = options.onRestartRequested;
1087
1235
  const onCloseRequested = typeof options.onCloseRequested === "function" ? options.onCloseRequested : null;
1088
1236
  let lastHTML = "";
1089
- const copyText = options.copyText ?? defaultCopyText;
1237
+ const copyTextOut = options.copyText ?? defaultCopyText;
1090
1238
  const skipStore = options.skipStore ?? createBrowserSkipStore(pluginId);
1091
1239
  const hostKind = typeof options.hostKind === "string" && options.hostKind ? options.hostKind : null;
1092
1240
  const profileNameOption = typeof options.profileName === "string" && options.profileName ? options.profileName : null;
@@ -1271,12 +1419,86 @@ function mountUpdatePanel(container, options) {
1271
1419
  let envHostKind = null;
1272
1420
  function sayCopy(text) {
1273
1421
  copyNotice = text;
1422
+ }
1423
+ function sayCopyKey(key, values) {
1424
+ try {
1425
+ copyNotice = copyText(key, currentLang(), values);
1426
+ } catch {
1427
+ copyNotice = copyText(key, "zh", values);
1428
+ }
1274
1429
  try {
1275
1430
  noticeExpiresAt = Date.now() + 5e3;
1276
1431
  } catch {
1277
1432
  noticeExpiresAt = 0;
1278
1433
  }
1279
1434
  }
1435
+ function setStableHTML(target, html) {
1436
+ try {
1437
+ const g = globalThis;
1438
+ const doc = g["document"];
1439
+ const el = target;
1440
+ if (!doc || typeof el.querySelectorAll !== "function") {
1441
+ target.innerHTML = html;
1442
+ return;
1443
+ }
1444
+ let focusAction = null;
1445
+ try {
1446
+ const active = doc.activeElement;
1447
+ if (active && typeof active.getAttribute === "function") {
1448
+ focusAction = active.getAttribute("data-action");
1449
+ }
1450
+ } catch {
1451
+ focusAction = null;
1452
+ }
1453
+ let scrolls = [];
1454
+ try {
1455
+ const nodes = el.querySelectorAll(".dsh-upd-body");
1456
+ if (nodes) {
1457
+ for (let i = 0; i < nodes.length; i++) {
1458
+ const n = nodes[i];
1459
+ scrolls.push(typeof n.scrollTop === "number" ? n.scrollTop : 0);
1460
+ }
1461
+ }
1462
+ } catch {
1463
+ scrolls = [];
1464
+ }
1465
+ target.innerHTML = html;
1466
+ try {
1467
+ const bodies = el.querySelectorAll(".dsh-upd-body");
1468
+ if (bodies) {
1469
+ for (let i = 0; i < bodies.length && i < scrolls.length; i++) {
1470
+ const n = bodies[i];
1471
+ try {
1472
+ n.scrollTop = scrolls[i];
1473
+ } catch {
1474
+ }
1475
+ }
1476
+ }
1477
+ } catch {
1478
+ }
1479
+ try {
1480
+ if (focusAction && typeof el.querySelector === "function") {
1481
+ const next = el.querySelector('[data-action="' + focusAction + '"]');
1482
+ if (next && typeof next.focus === "function") {
1483
+ try {
1484
+ next.focus.call(next, { preventScroll: true });
1485
+ } catch {
1486
+ try {
1487
+ next.focus.call(next);
1488
+ } catch {
1489
+ }
1490
+ }
1491
+ }
1492
+ }
1493
+ } catch {
1494
+ }
1495
+ } catch {
1496
+ try {
1497
+ target.innerHTML = html;
1498
+ } catch {
1499
+ }
1500
+ }
1501
+ }
1280
1502
  function queueArgs() {
1281
1503
  return { includeQueue: true, includeEnv: true, showOthers, ...requestId ? { requestId } : {} };
1282
1504
  }
@@ -1289,11 +1511,13 @@ function mountUpdatePanel(container, options) {
1289
1511
  }
1290
1512
  const latest = snapshot?.latestVersion ?? null;
1291
1513
  const skippedLatest = !!latest && validReleaseVersion(latest) && skipStore.has(latest);
1514
+ const langNow = currentLang();
1292
1515
  const nextHTML = renderUpdatePanelHTML({
1293
1516
  snapshot,
1294
1517
  manual,
1295
1518
  queue,
1296
1519
  busyAct,
1520
+ lang: langNow,
1297
1521
  skippedLatest,
1298
1522
  lastError: latch?.code ?? null,
1299
1523
  errorKind: latch?.kind ?? null,
@@ -1306,13 +1530,14 @@ function mountUpdatePanel(container, options) {
1306
1530
  pluginId,
1307
1531
  copyNotice,
1308
1532
  theme,
1533
+ themeTokens,
1309
1534
  // 使用范围与宿主种类:调用方显式传的优先,否则用宿主回的真值。
1310
1535
  profileName: profileNameOption ?? envProfileName,
1311
1536
  hostKind: hostKind ?? envHostKind
1312
1537
  });
1313
1538
  if (nextHTML !== lastHTML) {
1314
1539
  lastHTML = nextHTML;
1315
- container.innerHTML = nextHTML;
1540
+ setStableHTML(container, nextHTML);
1316
1541
  }
1317
1542
  }
1318
1543
  function applyStatusReply(reply, via) {
@@ -1456,7 +1681,7 @@ function mountUpdatePanel(container, options) {
1456
1681
  case "check": {
1457
1682
  if (busyAct) return;
1458
1683
  busyAct = "check";
1459
- copyNotice = "\u6B63\u5728\u67E5\u65B0\u7248\u2026";
1684
+ copyNotice = copyText("panel.toast.checking", currentLang());
1460
1685
  render();
1461
1686
  try {
1462
1687
  const reply = await call(phoneNames.updateCheck, queueArgs());
@@ -1468,7 +1693,13 @@ function mountUpdatePanel(container, options) {
1468
1693
  setLatch(userThrowLatch("check", err));
1469
1694
  } finally {
1470
1695
  busyAct = null;
1471
- if (copyNotice === "\u6B63\u5728\u67E5\u65B0\u7248\u2026") copyNotice = null;
1696
+ try {
1697
+ const zh = copyText("panel.toast.checking", "zh");
1698
+ const en = copyText("panel.toast.checking", "en");
1699
+ if (copyNotice === zh || copyNotice === en) copyNotice = null;
1700
+ } catch {
1701
+ copyNotice = null;
1702
+ }
1472
1703
  }
1473
1704
  render();
1474
1705
  maybeAutoChangelog();
@@ -1477,7 +1708,7 @@ function mountUpdatePanel(container, options) {
1477
1708
  case "install": {
1478
1709
  if (busyAct) return;
1479
1710
  busyAct = "install";
1480
- copyNotice = "\u6B63\u5728\u5B89\u88C5\u2026";
1711
+ copyNotice = copyText("panel.toast.installing", currentLang());
1481
1712
  render();
1482
1713
  try {
1483
1714
  if (!receipt) {
@@ -1528,7 +1759,13 @@ function mountUpdatePanel(container, options) {
1528
1759
  setLatch(userThrowLatch("install", err));
1529
1760
  } finally {
1530
1761
  busyAct = null;
1531
- if (copyNotice === "\u6B63\u5728\u5B89\u88C5\u2026") copyNotice = null;
1762
+ try {
1763
+ const zh = copyText("panel.toast.installing", "zh");
1764
+ const en = copyText("panel.toast.installing", "en");
1765
+ if (copyNotice === zh || copyNotice === en) copyNotice = null;
1766
+ } catch {
1767
+ copyNotice = null;
1768
+ }
1532
1769
  }
1533
1770
  render();
1534
1771
  maybeAutoChangelog();
@@ -1556,10 +1793,10 @@ function mountUpdatePanel(container, options) {
1556
1793
  case "copy-manual": {
1557
1794
  if (manual) {
1558
1795
  try {
1559
- await copyText(manual);
1560
- sayCopy("\u624B\u5DE5\u547D\u4EE4\u5DF2\u590D\u5236\uFF0C\u7C98\u5230\u7EC8\u7AEF\u6574\u884C\u6267\u884C\u5373\u53EF\u3002");
1796
+ await copyTextOut(manual);
1797
+ sayCopyKey("panel.toast.copy-manual-ok");
1561
1798
  } catch {
1562
- sayCopy("\u590D\u5236\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u9009\u4E2D\u4E0A\u9762\u7684\u547D\u4EE4\u3002");
1799
+ sayCopyKey("panel.toast.copy-manual-fail");
1563
1800
  }
1564
1801
  }
1565
1802
  render();
@@ -1585,10 +1822,10 @@ function mountUpdatePanel(container, options) {
1585
1822
  ].map((s) => redactForCopy(s));
1586
1823
  const text2 = diagCopyFormat === "line" ? segs.join(" \xB7 ") : segs.join("\n");
1587
1824
  try {
1588
- await copyText(text2);
1589
- sayCopy("\u5DF2\u590D\u5236\u5F53\u524D\u72B6\u6001\uFF08\u65E0\u5931\u8D25\uFF09\uFF0C\u76F4\u63A5\u7C98\u7ED9\u63D2\u4EF6\u4F5C\u8005\u5373\u53EF\uFF08\u5DF2\u8131\u654F\uFF09\u3002");
1825
+ await copyTextOut(text2);
1826
+ sayCopyKey("panel.toast.copy-state-ok");
1590
1827
  } catch {
1591
- sayCopy("\u590D\u5236\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u9009\u4E2D\u4E0A\u9762\u7684\u4FE1\u606F\u3002");
1828
+ sayCopyKey("panel.toast.copy-diag-fail");
1592
1829
  }
1593
1830
  render();
1594
1831
  return;
@@ -1611,20 +1848,21 @@ function mountUpdatePanel(container, options) {
1611
1848
  route: null,
1612
1849
  diag: frozen?.diag ?? null,
1613
1850
  manual,
1614
- format: diagCopyFormat
1851
+ format: diagCopyFormat,
1852
+ lang: currentLang()
1615
1853
  });
1616
1854
  try {
1617
- await copyText(text);
1618
- sayCopy("\u8BCA\u65AD\u5DF2\u590D\u5236\uFF0C\u76F4\u63A5\u7C98\u7ED9\u63D2\u4EF6\u4F5C\u8005\u5373\u53EF\uFF08\u5DF2\u8131\u654F\uFF09\u3002");
1855
+ await copyTextOut(text);
1856
+ sayCopyKey("panel.toast.copy-diag-ok");
1619
1857
  } catch {
1620
- sayCopy("\u590D\u5236\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u9009\u4E2D\u4E0A\u9762\u7684\u4FE1\u606F\u3002");
1858
+ sayCopyKey("panel.toast.copy-diag-fail");
1621
1859
  }
1622
1860
  render();
1623
1861
  return;
1624
1862
  }
1625
1863
  case "dismiss-failure": {
1626
1864
  clearLatch();
1627
- sayCopy("\u5DF2\u786E\u8BA4\u8BE5\u5931\u8D25\u63D0\u793A\uFF1B\u4E0B\u6B21\u67E5\u65B0\u7248\u6216\u5B89\u88C5\u5C06\u91CD\u65B0\u8BC4\u4F30\u3002");
1865
+ sayCopyKey("panel.toast.failure-dismissed");
1628
1866
  render();
1629
1867
  return;
1630
1868
  }
@@ -1644,12 +1882,12 @@ function mountUpdatePanel(container, options) {
1644
1882
  try {
1645
1883
  if (typeof onRestartRequested === "function") {
1646
1884
  await onRestartRequested();
1647
- sayCopy("\u5DF2\u6309\u8C03\u7528\u65B9\u7684\u91CD\u542F\u6D41\u7A0B\u5904\u7406\uFF1B\u91CD\u542F\u540E\u65B0\u7248\u751F\u6548\u3002");
1885
+ sayCopyKey("panel.toast.restart-delegated");
1648
1886
  } else {
1649
- sayCopy("\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");
1887
+ sayCopyKey("panel.toast.restart-manual");
1650
1888
  }
1651
1889
  } catch {
1652
- sayCopy("\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");
1890
+ sayCopyKey("panel.toast.restart-failed");
1653
1891
  }
1654
1892
  render();
1655
1893
  return;
@@ -1668,12 +1906,17 @@ function mountUpdatePanel(container, options) {
1668
1906
  render();
1669
1907
  }
1670
1908
  async function setTheme(next) {
1671
- if (next !== "default" && next !== "archive" && next !== "d5-paper") {
1672
- throw new Error(`[dsh-plugin-update] \u4E3B\u9898\u975E\u6CD5\uFF1A\u53EA\u6536 default \u6216 archive\uFF08d5-paper \u4E3A\u65E7\u522B\u540D\u4ECD\u53EF\u7528\uFF09\uFF08\u6536\u5230 ${JSON.stringify(next)}\uFF09`);
1909
+ if (next !== "default" && next !== "archive") {
1910
+ throw new Error(`[dsh-plugin-update] \u4E3B\u9898\u975E\u6CD5\uFF1A\u53EA\u6536 default \u6216 archive\uFF08\u6536\u5230 ${JSON.stringify(next)}\uFF09`);
1673
1911
  }
1674
1912
  theme = normalizePanelTheme(next);
1675
1913
  render();
1676
1914
  }
1915
+ async function setThemeTokens(next) {
1916
+ themeTokensStyleFor(next ?? void 0);
1917
+ themeTokens = next ?? void 0;
1918
+ render();
1919
+ }
1677
1920
  async function setShowOthers(show) {
1678
1921
  showOthers = show === true;
1679
1922
  await refresh();
@@ -1718,6 +1961,10 @@ function mountUpdatePanel(container, options) {
1718
1961
  function unmount() {
1719
1962
  if (!mounted) return;
1720
1963
  mounted = false;
1964
+ try {
1965
+ unsubLang();
1966
+ } catch {
1967
+ }
1721
1968
  autoSeq++;
1722
1969
  changelogInflight.clear();
1723
1970
  try {
@@ -1733,6 +1980,9 @@ function mountUpdatePanel(container, options) {
1733
1980
  } catch {
1734
1981
  }
1735
1982
  }
1983
+ const unsubLang = subscribeLang(() => {
1984
+ render();
1985
+ }, localeOpt);
1736
1986
  render();
1737
1987
  try {
1738
1988
  container.addEventListener?.("click", onClick);
@@ -1744,6 +1994,7 @@ function mountUpdatePanel(container, options) {
1744
1994
  }
1745
1995
  const timer = getTimer();
1746
1996
  const handle = timer.set(() => {
1997
+ if (busyAct) return;
1747
1998
  void refresh();
1748
1999
  }, pollMs);
1749
2000
  try {
@@ -1758,7 +2009,7 @@ function mountUpdatePanel(container, options) {
1758
2009
  } catch {
1759
2010
  }
1760
2011
  })();
1761
- return { refresh, act, setMode, setTheme, setShowOthers, setChangelogCollapsed, setChangelogMarkdown, unmount };
2012
+ return { refresh, act, setMode, setTheme, setThemeTokens, setShowOthers, setChangelogCollapsed, setChangelogMarkdown, unmount };
1762
2013
  }
1763
2014
  import { buildPhoneNames as buildPhoneNames2 } from "./config.js";
1764
2015
  import { buildChangelogPhoneName as buildChangelogPhoneName2 } from "./config.js";
@@ -1809,8 +2060,8 @@ export {
1809
2060
  PANEL_DIAG_MAX_CHARS,
1810
2061
  PANEL_POLL,
1811
2062
  QUEUE_INTENT_TTL_MS,
2063
+ UPDATE_PANEL_ARCHIVE_CSS,
1812
2064
  UPDATE_PANEL_CSS,
1813
- UPDATE_PANEL_D5_CSS,
1814
2065
  blockedCopy,
1815
2066
  buildDiagnosticText,
1816
2067
  buildChangelogPhoneName2 as buildPanelChangelogPhoneName,
@@ -1827,10 +2078,12 @@ export {
1827
2078
  isHeadOfQueue,
1828
2079
  isKnownFailureCode,
1829
2080
  isQueueBusy,
2081
+ isSafeThemeCssValue,
1830
2082
  isUnreleasedVersion,
1831
2083
  mountUpdatePanel,
1832
2084
  normalizePanelTheme,
1833
2085
  normalizeQueueState,
2086
+ panelSealFor,
1834
2087
  panelViewModel,
1835
2088
  parseChangelog2 as parseChangelog,
1836
2089
  pendingAutoCheck,
@@ -1843,5 +2096,6 @@ export {
1843
2096
  renderUpdatePanelHTML,
1844
2097
  renderUpdatePanelKernel,
1845
2098
  selectChangelogEntries,
2099
+ themeTokensStyleFor,
1846
2100
  visibleQueueFor2 as visibleQueueFor
1847
2101
  };