dsh-life-pack 0.3.40 → 0.3.41

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/client.js CHANGED
@@ -153,27 +153,78 @@ window.__ModuleLoader__.load({
153
153
  path: "/api/ilife-manager"
154
154
  };
155
155
  //#endregion
156
- //#region ../../node_modules/.pnpm/dsh-plugin-update@0.5.1/node_modules/dsh-plugin-update/dist/config.js
157
- const DEFAULT_PANEL_POLL_MS = 1e3;
156
+ //#region ../../node_modules/.pnpm/dsh-plugin-update@0.5.4/node_modules/dsh-plugin-update/dist/config.js
158
157
  function assertPrefix(value, role) {
159
158
  if (typeof value !== "string" || value.length === 0) throw new Error("[dsh-plugin-update] " + role + " 非法:须为非空字符串(收到 " + JSON.stringify(value) + ")");
160
159
  if (value.includes(".") || value.includes("/") || value.includes("\\") || /\s/.test(value)) throw new Error("[dsh-plugin-update] " + role + " 非法:不得含有点、路径分隔符或空白(收到 " + JSON.stringify(value) + ")");
161
160
  return value;
162
161
  }
163
- function buildPhoneNames(prefix) {
164
- const checked = assertPrefix(prefix, "电话名前缀 prefix");
165
- return {
166
- updateStatus: checked + ".updateStatus",
167
- updateCheck: checked + ".updateCheck",
168
- updateInstall: checked + ".updateInstall",
169
- updateChangelog: checked + ".updateChangelog"
170
- };
162
+ //#endregion
163
+ //#region ../../node_modules/.pnpm/dsh-plugin-update@0.5.4/node_modules/dsh-plugin-update/dist/redaction.js
164
+ const COPY_BUDGET_CHARS = 1500;
165
+ const REDACTED_PATH = "<路径>";
166
+ const REDACTED_SECRET = "<脱敏>";
167
+ const ABSOLUTE_PATH_RE = /[A-Za-z]:\\[^\s"']*|\\\\[^\s"'()\[\];]+|(^|[\s"'(\[=,])\/\/[^\s"'()\[\];]+|(^|[\s"'(\[=:,])\/(?!\/)[^\s"'()\[\];]+/g;
168
+ const URL_USERINFO_RE = /[A-Za-z][A-Za-z0-9+.-]*:\/\/[^\s/]*@/;
169
+ const NPM_TOKEN_RE = /\bnpm_[A-Za-z0-9_-]{8,}/g;
170
+ const GITHUB_TOKEN_RE = /\b(?:gh[pousr]_[A-Za-z0-9]{8,}|github_pat_[A-Za-z0-9_]{10,})/g;
171
+ const SK_TOKEN_RE = /\bsk-[A-Za-z0-9_-]{6,}/g;
172
+ const BEARER_TOKEN_RE = /(\bbearer\s+)[A-Za-z0-9._~+/-=]{6,}/gi;
173
+ const CRED_PAIR_RE = /(\b(?:token|password|passwd|pwd|api[_-]?key|access[_-]?key|auth[_-]?token|secret|cookie)\s*[:=]\s*)("[^"]+"|'[^']+'|[^\s'";,)\]]+)/gi;
174
+ const EMAIL_RE = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g;
175
+ function flattenWhitespace(text) {
176
+ return String(text ?? "").replace(/\s+/g, " ").trim();
177
+ }
178
+ function applyPathRule(text) {
179
+ ABSOLUTE_PATH_RE.lastIndex = 0;
180
+ return text.replace(ABSOLUTE_PATH_RE, (_whole, g1, g2) => {
181
+ return `${g1 ?? g2 ?? ""}${REDACTED_PATH}`;
182
+ });
183
+ }
184
+ function applyTokenRule(text) {
185
+ NPM_TOKEN_RE.lastIndex = 0;
186
+ GITHUB_TOKEN_RE.lastIndex = 0;
187
+ SK_TOKEN_RE.lastIndex = 0;
188
+ BEARER_TOKEN_RE.lastIndex = 0;
189
+ return text.replace(NPM_TOKEN_RE, REDACTED_SECRET).replace(GITHUB_TOKEN_RE, REDACTED_SECRET).replace(SK_TOKEN_RE, REDACTED_SECRET).replace(BEARER_TOKEN_RE, (_whole, prefix) => `${prefix}${REDACTED_SECRET}`);
190
+ }
191
+ function applyCredPairRule(text) {
192
+ CRED_PAIR_RE.lastIndex = 0;
193
+ return text.replace(CRED_PAIR_RE, (_whole, head) => `${head}${REDACTED_SECRET}`);
194
+ }
195
+ function applyEmailRule(text) {
196
+ EMAIL_RE.lastIndex = 0;
197
+ return text.replace(EMAIL_RE, REDACTED_SECRET);
198
+ }
199
+ function hasUserinfo(text) {
200
+ URL_USERINFO_RE.lastIndex = 0;
201
+ return URL_USERINFO_RE.test(text);
171
202
  }
172
- function buildChangelogPhoneName(prefix) {
173
- return buildPhoneNames(prefix).updateChangelog;
203
+ function truncateToWordBoundary(text, max) {
204
+ const input = String(text ?? "");
205
+ if (input.length <= max) return input;
206
+ const slice = input.slice(0, max);
207
+ const lastSpace = slice.lastIndexOf(" ");
208
+ let cut = lastSpace > 0 ? slice.slice(0, lastSpace) : slice;
209
+ const lastOpen = cut.lastIndexOf("<");
210
+ if (lastOpen > cut.lastIndexOf(">")) cut = cut.slice(0, lastOpen).trimEnd();
211
+ if (!cut) cut = slice;
212
+ return `${cut}\u2026`;
213
+ }
214
+ function truncateForCopy(text) {
215
+ return truncateToWordBoundary(text, COPY_BUDGET_CHARS);
216
+ }
217
+ function scrubWithoutBudget(text) {
218
+ return applyEmailRule(applyCredPairRule(applyTokenRule(applyPathRule(text))));
219
+ }
220
+ function sanitizeForCopy(text) {
221
+ const flat = flattenWhitespace(String(text ?? ""));
222
+ if (!flat) return "";
223
+ if (hasUserinfo(flat)) return "";
224
+ return truncateForCopy(scrubWithoutBudget(flat));
174
225
  }
175
226
  //#endregion
176
- //#region ../../node_modules/.pnpm/dsh-plugin-update@0.5.1/node_modules/dsh-plugin-update/dist/service.js
227
+ //#region ../../node_modules/.pnpm/dsh-plugin-update@0.5.4/node_modules/dsh-plugin-update/dist/service.js
177
228
  function updateError(code) {
178
229
  return Object.assign(new Error(code), { code });
179
230
  }
@@ -260,7 +311,7 @@ window.__ModuleLoader__.load({
260
311
  return 0;
261
312
  }
262
313
  //#endregion
263
- //#region ../../node_modules/.pnpm/dsh-plugin-update@0.5.1/node_modules/dsh-plugin-update/dist/changelog.js
314
+ //#region ../../node_modules/.pnpm/dsh-plugin-update@0.5.4/node_modules/dsh-plugin-update/dist/changelog.js
264
315
  const CHANGELOG_NEUTRAL_LINE = "作者未提供更新说明,安装不受影响。";
265
316
  const CHANGELOG_MAX_CHARS = 65536;
266
317
  const CHANGELOG_ALL_CATEGORIES = [
@@ -708,151 +759,8 @@ ${blocks.join("\n")}
708
759
  return renderChangelogNeutral();
709
760
  }
710
761
  }
711
- function isTerminalPhase(phase) {
712
- return phase === "done" || phase === "failed" || phase === "skipped" || phase === "current";
713
- }
714
- function asMillis(value, fallback) {
715
- return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : fallback;
716
- }
717
- function isNonEmptyString(value) {
718
- return typeof value === "string" && value.length > 0;
719
- }
720
- const PHASES = [
721
- "pending",
722
- "checking",
723
- "ready",
724
- "installing",
725
- "current",
726
- "done",
727
- "failed",
728
- "skipped"
729
- ];
730
- function asPhase(value) {
731
- return typeof value === "string" && PHASES.includes(value) ? value : "pending";
732
- }
733
- function batchRequestId(sessionId, key) {
734
- return "batch:" + sessionId + ":" + key;
735
- }
736
- function emptyBatchSession() {
737
- return {
738
- version: 1,
739
- id: "",
740
- selfKey: null,
741
- stopOnFailure: false,
742
- order: [],
743
- entries: [],
744
- createdAt: 0,
745
- updatedAt: 0
746
- };
747
- }
748
- function normalizeBatchSession(raw) {
749
- try {
750
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) return emptyBatchSession();
751
- const input = raw;
752
- if (input["version"] !== 1) return emptyBatchSession();
753
- const id = isNonEmptyString(input["id"]) ? input["id"] : "";
754
- const selfKey = isNonEmptyString(input["selfKey"]) ? input["selfKey"] : null;
755
- const entries = [];
756
- const seen = /* @__PURE__ */ new Set();
757
- const rawEntries = input["entries"];
758
- if (Array.isArray(rawEntries)) for (const item of rawEntries) {
759
- if (!item || typeof item !== "object" || Array.isArray(item)) continue;
760
- const e = item;
761
- const key = e["key"];
762
- if (!isNonEmptyString(key) || seen.has(key)) continue;
763
- seen.add(key);
764
- const requestId = isNonEmptyString(e["requestId"]) ? e["requestId"] : batchRequestId(id, key);
765
- entries.push({
766
- key,
767
- phase: asPhase(e["phase"]),
768
- requestId,
769
- targetVersion: isNonEmptyString(e["targetVersion"]) ? e["targetVersion"] : null,
770
- restartRequired: e["restartRequired"] === true,
771
- error: isNonEmptyString(e["error"]) ? e["error"] : null,
772
- updatedAt: asMillis(e["updatedAt"], 0)
773
- });
774
- }
775
- const order = entries.map((e) => e.key);
776
- return {
777
- version: 1,
778
- id,
779
- selfKey: selfKey && seen.has(selfKey) ? selfKey : null,
780
- stopOnFailure: input["stopOnFailure"] === true,
781
- order,
782
- entries,
783
- createdAt: asMillis(input["createdAt"], 0),
784
- updatedAt: asMillis(input["updatedAt"], 0)
785
- };
786
- } catch {
787
- return emptyBatchSession();
788
- }
789
- }
790
762
  //#endregion
791
- //#region ../../node_modules/.pnpm/dsh-plugin-update@0.5.1/node_modules/dsh-plugin-update/dist/redaction.js
792
- const COPY_BUDGET_CHARS = 1500;
793
- const REDACTED_PATH = "<路径>";
794
- const REDACTED_SECRET = "<脱敏>";
795
- const ABSOLUTE_PATH_RE = /[A-Za-z]:\\[^\s"']*|\\\\[^\s"'()\[\];]+|(^|[\s"'(\[=,])\/\/[^\s"'()\[\];]+|(^|[\s"'(\[=:,])\/(?!\/)[^\s"'()\[\];]+/g;
796
- const URL_USERINFO_RE = /[A-Za-z][A-Za-z0-9+.-]*:\/\/[^\s/]*@/;
797
- const NPM_TOKEN_RE = /\bnpm_[A-Za-z0-9_-]{8,}/g;
798
- const GITHUB_TOKEN_RE = /\b(?:gh[pousr]_[A-Za-z0-9]{8,}|github_pat_[A-Za-z0-9_]{10,})/g;
799
- const SK_TOKEN_RE = /\bsk-[A-Za-z0-9_-]{6,}/g;
800
- const BEARER_TOKEN_RE = /(\bbearer\s+)[A-Za-z0-9._~+/-=]{6,}/gi;
801
- const CRED_PAIR_RE = /(\b(?:token|password|passwd|pwd|api[_-]?key|access[_-]?key|auth[_-]?token|secret|cookie)\s*[:=]\s*)("[^"]+"|'[^']+'|[^\s'";,)\]]+)/gi;
802
- const EMAIL_RE = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g;
803
- function flattenWhitespace(text) {
804
- return String(text ?? "").replace(/\s+/g, " ").trim();
805
- }
806
- function applyPathRule(text) {
807
- ABSOLUTE_PATH_RE.lastIndex = 0;
808
- return text.replace(ABSOLUTE_PATH_RE, (_whole, g1, g2) => {
809
- return `${g1 ?? g2 ?? ""}${REDACTED_PATH}`;
810
- });
811
- }
812
- function applyTokenRule(text) {
813
- NPM_TOKEN_RE.lastIndex = 0;
814
- GITHUB_TOKEN_RE.lastIndex = 0;
815
- SK_TOKEN_RE.lastIndex = 0;
816
- BEARER_TOKEN_RE.lastIndex = 0;
817
- return text.replace(NPM_TOKEN_RE, REDACTED_SECRET).replace(GITHUB_TOKEN_RE, REDACTED_SECRET).replace(SK_TOKEN_RE, REDACTED_SECRET).replace(BEARER_TOKEN_RE, (_whole, prefix) => `${prefix}${REDACTED_SECRET}`);
818
- }
819
- function applyCredPairRule(text) {
820
- CRED_PAIR_RE.lastIndex = 0;
821
- return text.replace(CRED_PAIR_RE, (_whole, head) => `${head}${REDACTED_SECRET}`);
822
- }
823
- function applyEmailRule(text) {
824
- EMAIL_RE.lastIndex = 0;
825
- return text.replace(EMAIL_RE, REDACTED_SECRET);
826
- }
827
- function hasUserinfo(text) {
828
- URL_USERINFO_RE.lastIndex = 0;
829
- return URL_USERINFO_RE.test(text);
830
- }
831
- function truncateToWordBoundary(text, max) {
832
- const input = String(text ?? "");
833
- if (input.length <= max) return input;
834
- const slice = input.slice(0, max);
835
- const lastSpace = slice.lastIndexOf(" ");
836
- let cut = lastSpace > 0 ? slice.slice(0, lastSpace) : slice;
837
- const lastOpen = cut.lastIndexOf("<");
838
- if (lastOpen > cut.lastIndexOf(">")) cut = cut.slice(0, lastOpen).trimEnd();
839
- if (!cut) cut = slice;
840
- return `${cut}\u2026`;
841
- }
842
- function truncateForCopy(text) {
843
- return truncateToWordBoundary(text, COPY_BUDGET_CHARS);
844
- }
845
- function scrubWithoutBudget(text) {
846
- return applyEmailRule(applyCredPairRule(applyTokenRule(applyPathRule(text))));
847
- }
848
- function sanitizeForCopy(text) {
849
- const flat = flattenWhitespace(String(text ?? ""));
850
- if (!flat) return "";
851
- if (hasUserinfo(flat)) return "";
852
- return truncateForCopy(scrubWithoutBudget(flat));
853
- }
854
- //#endregion
855
- //#region ../../node_modules/.pnpm/dsh-plugin-update@0.5.1/node_modules/dsh-plugin-update/dist/panel.js
763
+ //#region ../../node_modules/.pnpm/dsh-plugin-update@0.5.4/node_modules/dsh-plugin-update/dist/panel.js
856
764
  function normalizePanelTheme(value) {
857
765
  return value === "d5-paper" || value === "archive" ? "d5-paper" : "default";
858
766
  }
@@ -974,109 +882,6 @@ ${blocks.join("\n")}
974
882
  if (manual) lines.push(`\u624B\u5DE5\u547D\u4EE4\uFF1A${manual}`);
975
883
  return redactForCopy(lines.join("\n"));
976
884
  }
977
- const DIAG_KNOWN_TYPES = {
978
- v: "number",
979
- stage: "string",
980
- route: "string",
981
- method: "string",
982
- httpStatus: "number",
983
- exitCode: "number",
984
- latencyMs: "number",
985
- detail: "string",
986
- targetPackageName: "string",
987
- runningVersion: "string",
988
- latestVersion: "string",
989
- environmentKind: "string",
990
- requestId: "string",
991
- checkId: "string",
992
- registryHost: "string",
993
- action: "string"
994
- };
995
- function readDiagTolerant(diag) {
996
- const empty = {
997
- stage: null,
998
- route: null,
999
- method: null,
1000
- httpStatus: null,
1001
- exitCode: null,
1002
- latencyMs: null,
1003
- detail: null,
1004
- targetPackageName: null,
1005
- runningVersion: null,
1006
- latestVersion: null,
1007
- environmentKind: null,
1008
- requestId: null,
1009
- checkId: null,
1010
- registryHost: null,
1011
- action: null,
1012
- unknownKeys: []
1013
- };
1014
- if (!diag || typeof diag !== "object" || Array.isArray(diag)) return empty;
1015
- const raw = diag;
1016
- const out = {
1017
- ...empty,
1018
- unknownKeys: []
1019
- };
1020
- for (const key of Object.keys(DIAG_KNOWN_TYPES)) {
1021
- if (!Object.prototype.hasOwnProperty.call(raw, key)) continue;
1022
- const v = raw[key];
1023
- if (v === void 0 || v === null) continue;
1024
- if (typeof v !== DIAG_KNOWN_TYPES[key]) continue;
1025
- out[key] = v;
1026
- }
1027
- for (const key of Object.keys(raw)) if (!Object.prototype.hasOwnProperty.call(DIAG_KNOWN_TYPES, key)) out.unknownKeys.push(key);
1028
- return out;
1029
- }
1030
- function pickText(value, fallback) {
1031
- if (typeof value === "string" && value.trim()) return value.trim();
1032
- return fallback;
1033
- }
1034
- function queueTextOf(queuePosition, _diagQueuePos) {
1035
- const pos = typeof queuePosition === "number" ? queuePosition : null;
1036
- if (pos === 0) return "正在安装";
1037
- if (typeof pos === "number") return `\u6392\u961F\u7B2C ${pos} \u4F4D`;
1038
- return "不在队列里";
1039
- }
1040
- function buildUpdateDiagCopy(input) {
1041
- const rawCode = String(input.code ?? "").trim() || "internal";
1042
- const copy = failureCopy(rawCode) ?? UNKNOWN_FAILURE_COPY;
1043
- const diag = readDiagTolerant(input.diag);
1044
- const detail = redactForCopy(diag.detail ?? input.detail ?? "") || "(本回包没有带诊断摘要,等电话侧 diag 落定后补齐)";
1045
- const pluginName = pickText(diag.targetPackageName ?? input.pluginId, "(未知包)");
1046
- const runVer = pickText(diag.runningVersion ?? input.runningVersion, "?");
1047
- const instVer = pickText(input.installedVersion ?? diag.latestVersion, "?");
1048
- const host = pickText(diag.environmentKind ?? input.hostKind, "未知");
1049
- const routeRaw = diag.route ?? input.route;
1050
- const requestRaw = diag.requestId ?? input.requestId;
1051
- const checkRaw = diag.checkId ?? input.checkId;
1052
- const queue = queueTextOf(input.queuePosition ?? null);
1053
- const prov = [
1054
- `\u63D2\u4EF6=${pluginName}`,
1055
- `\u7248\u672C=${runVer}\u2192${instVer}`,
1056
- `\u5BBF\u4E3B=${host}`
1057
- ];
1058
- if (typeof routeRaw === "string" && routeRaw.trim()) prov.push(`\u8DEF\u7531=${routeRaw.trim()}`);
1059
- if (diag.stage) prov.push(`\u9636\u6BB5=${diag.stage}`);
1060
- if (diag.method) prov.push(`\u65B9\u6CD5=${diag.method}`);
1061
- if (typeof diag.httpStatus === "number") prov.push(`HTTP=${diag.httpStatus}`);
1062
- if (typeof diag.exitCode === "number") prov.push(`exit=${diag.exitCode}`);
1063
- if (typeof diag.latencyMs === "number") prov.push(`\u8017\u65F6=${diag.latencyMs}ms`);
1064
- if (diag.registryHost) prov.push(`\u6E90=${diag.registryHost}`);
1065
- else prov.push("源=未知(非官方源时省略本身即信息)");
1066
- if (diag.action) prov.push(`\u5EFA\u8BAE=${diag.action}`);
1067
- if (typeof requestRaw === "string" && requestRaw.trim()) prov.push(`\u8BF7\u6C42=${requestRaw.trim()}`);
1068
- if (typeof checkRaw === "string" && checkRaw.trim()) prov.push(`\u68C0\u67E5=${checkRaw.trim()}`);
1069
- prov.push(`\u961F\u5217=${queue}`);
1070
- const format = input.format === "line" ? "line" : "block";
1071
- const provLine = redactForCopy(prov.join(" · "));
1072
- if (format === "line") return redactForCopy(`[update-diag] code=${rawCode} \xB7 ${copy.zh} \xB7 \u6458\u8981=${detail} \xB7 ${provLine} \xB7 \u600E\u4E48\u529E=${copy.act}`);
1073
- return [
1074
- `[update-diag] ${rawCode} \u2014 ${copy.zh}`,
1075
- ` \u6458\u8981\uFF1A${detail}`,
1076
- ` \u6765\u6E90\uFF1A${provLine}`,
1077
- ` \u600E\u4E48\u529E\uFF1A${copy.act}`
1078
- ].join("\n");
1079
- }
1080
885
  const PANEL_SKIPPED_MAX = 50;
1081
886
  function panelSkipKey(pluginId) {
1082
887
  return `dsh-upd-skipped/${String(pluginId)}`;
@@ -1417,6 +1222,9 @@ ${blocks.join("\n")}
1417
1222
  ".dsh-upd-strip-k{display:block;font-size:11px;letter-spacing:.14em;opacity:.7}",
1418
1223
  ".dsh-upd-strip-v{font-family:Consolas,Menlo,monospace;font-size:12.5px}",
1419
1224
  ".dsh-upd-chapter{margin-top:14px;padding-top:10px;border-top:1px solid var(--dsh-upd-line,#e5e7eb)}",
1225
+ ".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)}",
1226
+ ".dsh-upd-foot-note{margin-right:auto;font-size:12px;opacity:.7}",
1227
+ ".dsh-upd-footer button{margin:0}",
1420
1228
  ".dsh-upd-chap-head{display:flex;align-items:baseline;gap:10px;margin-bottom:6px}",
1421
1229
  ".dsh-upd-chap-no{font-size:13px;font-style:italic;opacity:.6}",
1422
1230
  ".dsh-upd-chap-title{font-size:14px;margin:0}",
@@ -1523,6 +1331,8 @@ ${blocks.join("\n")}
1523
1331
  "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}",
1524
1332
  ".dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-banner>div:first-child{overflow:hidden;text-overflow:ellipsis}",
1525
1333
  ".dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-actions{flex-wrap:wrap}",
1334
+ ".dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-footer{margin-top:26px;padding-top:16px;border-top:1px solid var(--d5-line)}",
1335
+ ".dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-foot-note{color:var(--d5-muted)}",
1526
1336
  ".dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-head{display:flex;gap:16px;align-items:baseline;flex-wrap:wrap;padding-right:120px}",
1527
1337
  ".dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-name{font-family:var(--d5-serif);font-size:22px;font-weight:700}",
1528
1338
  ".dsh-upd[data-theme=\"d5-paper\"] .dsh-upd-meta{width:100%;font-size:12.5px;color:var(--d5-muted)}",
@@ -1617,7 +1427,6 @@ ${blocks.join("\n")}
1617
1427
  if (view.showManual && manual) 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>`);
1618
1428
  if (b.kind === "restart") 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>`);
1619
1429
  if (snapshot) 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>`);
1620
- if (mode === "dialog") actions.push(`<button type="button" data-action="close-view" title="\u5173\u95ED\u9762\u677F\uFF08\u5B89\u88C5\u5728\u5BBF\u4E3B\u4FA7\u7EE7\u7EED\u8DD1\uFF0C\u53EF\u91CD\u5F00\u6062\u590D\u663E\u793A\uFF09">\u5173\u95ED</button>`);
1621
1430
  actions.push("</div>");
1622
1431
  }
1623
1432
  actions.push(progressBar(snapshot));
@@ -1674,495 +1483,144 @@ ${changelogHTML}
1674
1483
  parts.push(chapterOf(3, `<div class="dsh-upd-queue">${rows.join("")}</div>`, note));
1675
1484
  } else parts.push(chapterOf(3, `<div class="dsh-upd-queue"><div class="dsh-upd-changelog-neutral">${escapeHtml$2(view.queueNote ?? "当前没有排队任务,同一使用范围一次只装一个。")}</div></div>`));
1676
1485
  {
1677
- const errLines = [];
1678
- if (b.kind === "failed" || b.kind === "blocked") errLines.push(`<div class="dsh-upd-err">\u7A33\u5B9A\u7801 <code>${escapeHtml$2(String(b.kind === "blocked" ? snapshot?.blockedReason ?? b.kind : "install-failed"))}</code>\uFF1A\u4E0A\u4E00\u6761\u4E2D\u6587\u8BF4\u660E\u5C31\u662F\u8981\u7528\u6237\u505A\u7684\u4E8B\uFF1B\u8981\u5F80\u4E0A\u6E38\u62A5\uFF0C\u7528\u300C\u590D\u5236\u8BCA\u65AD\u300D\u6574\u6BB5\u7C98\uFF08\u5DF2\u8131\u654F\uFF09\u3002</div>`);
1679
- errLines.push(`<div class="dsh-upd-log">\u6DF1\u6316\u770B\u65E5\u5FD7\uFF1A\u6309\u63D2\u4EF6\u6807\u8BC6 <code>${escapeHtml$2(pluginId)}</code> \u8FC7\u6EE4 <code>host.call</code>\u3001<code>host.call.fail</code>\u3001<code>update.install.exec</code> \u4E09\u4E2A\u4E8B\u4EF6\u3002</div>`);
1680
- if (copyNotice) errLines.push(`<div class="dsh-upd-copy" role="status">${escapeHtml$2(copyNotice)}</div>`);
1681
- parts.push(chapterOf(4, errLines.join("")));
1682
- }
1683
- parts.push(chapterOf(5, 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$2(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>`));
1684
- return parts.join("\n");
1685
- }
1686
- function renderUpdatePanelHTML(input) {
1687
- const view = panelViewModel(input);
1688
- const kernel = renderUpdatePanelKernel(input, view);
1689
- const d5 = normalizePanelTheme(input.theme) === "d5-paper";
1690
- const attr = d5 ? " data-theme=\"d5-paper\"" : "";
1691
- const sealAttr = ` data-seal="${escapeHtml$2(view.seal.text)}" data-seal-tone="${view.seal.tone}"`;
1692
- const body = input.mode === "dialog" ? `<div class="dsh-upd-overlay" data-mode="dialog"><div class="dsh-upd" data-mode="dialog" data-plugin="${escapeHtml$2(input.pluginId)}"${sealAttr}${attr}>
1693
- ${kernel}
1694
- </div></div>` : `<div class="dsh-upd" data-mode="embedded" data-plugin="${escapeHtml$2(input.pluginId)}"${sealAttr}${attr}>
1695
- ${kernel}
1696
- </div>`;
1697
- return `<style>${d5 ? `${UPDATE_PANEL_CSS}
1698
- ${UPDATE_PANEL_D5_CSS}` : UPDATE_PANEL_CSS}</style>
1699
- ${body}`;
1700
- }
1701
- function isObject$2(value) {
1702
- return !!value && typeof value === "object" && !Array.isArray(value);
1703
- }
1704
- function asSnapshot$1(value) {
1705
- if (!isObject$2(value)) return null;
1706
- const s = value;
1707
- if (typeof s["runningVersion"] !== "string") return null;
1708
- if (typeof s["canInstall"] !== "boolean") return null;
1709
- return value;
1710
- }
1711
- function asQueue(value) {
1712
- if (!isObject$2(value) || typeof value["busy"] !== "boolean") return null;
1713
- return value;
1714
- }
1715
- function randomRequestId() {
1716
- try {
1717
- const rand = Math.floor(Math.random() * 16777215).toString(36);
1718
- return `r${Date.now().toString(36)}${rand}`.slice(0, 64);
1719
- } catch {
1720
- return `r${Date.now()}`;
1721
- }
1722
- }
1723
- function getTimer$1() {
1724
- const g = globalThis;
1725
- const set = g["setInterval"];
1726
- const clear = g["clearInterval"];
1727
- if (typeof set === "function" && typeof clear === "function") return {
1728
- set: (fn, ms) => set(fn, ms),
1729
- clear: (h) => clear(h)
1730
- };
1731
- const setT = g["setTimeout"];
1732
- const clearT = g["clearTimeout"];
1733
- return {
1734
- set: (fn, ms) => setT(fn, ms),
1735
- clear: (h) => clearT(h)
1736
- };
1737
- }
1738
- async function defaultCopyText$1(text) {
1739
- try {
1740
- const clip = globalThis["navigator"]?.clipboard;
1741
- const write = clip?.writeText;
1742
- if (clip && typeof write === "function") {
1743
- await write.call(clip, text);
1744
- return;
1745
- }
1746
- } catch {}
1747
- }
1748
- function mountUpdatePanel(container, options) {
1749
- if (!container || typeof container.innerHTML !== "string") throw new Error("[dsh-plugin-update] 挂载面板需要一个有 innerHTML 的容器");
1750
- if (!options || typeof options !== "object") throw new Error("[dsh-plugin-update] 挂载面板缺少配置:插件标识 pluginId 必填");
1751
- const pluginId = options.pluginId;
1752
- if (typeof pluginId !== "string" || !pluginId) throw new Error(`[dsh-plugin-update] \u63D2\u4EF6\u6807\u8BC6 pluginId \u5FC5\u586B\uFF1A\u987B\u4E3A\u975E\u7A7A\u5B57\u7B26\u4E32\uFF08\u6536\u5230 ${JSON.stringify(pluginId)}\uFF09`);
1753
- if (typeof options.call !== "function") throw new Error("[dsh-plugin-update] 挂载面板需要传输函数 call(面板调宿主电话的唯一接触面)");
1754
- const prefix = options.prefix === void 0 ? "wf" : options.prefix;
1755
- const phoneNames = buildPhoneNames(prefix);
1756
- const changelogPhone = buildChangelogPhoneName(prefix);
1757
- const autoChangelogEnabled = typeof options.changelogMarkdown !== "string" && options.autoChangelog !== false;
1758
- let manualChangelogOverride = typeof options.changelogMarkdown === "string";
1759
- const changelogCache = /* @__PURE__ */ new Map();
1760
- const changelogInflight = /* @__PURE__ */ new Set();
1761
- const changelogFailedAt = /* @__PURE__ */ new Map();
1762
- let autoSeq = 0;
1763
- const pollMs = options.pollMs === void 0 ? DEFAULT_PANEL_POLL_MS : options.pollMs;
1764
- if (typeof pollMs !== "number" || !Number.isFinite(pollMs) || pollMs < 250) throw new Error(`[dsh-plugin-update] \u9762\u677F\u8F6E\u8BE2\u95F4\u9694\u975E\u6CD5\uFF1A\u4E0D\u5F97\u5C0F\u4E8E 250 \u6BEB\u79D2\uFF08\u6536\u5230 ${JSON.stringify(options.pollMs)}\uFF09`);
1765
- let mode = options.mode ?? "embedded";
1766
- if (mode !== "embedded" && mode !== "dialog") 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`);
1767
- if (options.theme !== void 0 && options.theme !== "default" && options.theme !== "archive" && options.theme !== "d5-paper") 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`);
1768
- let theme = normalizePanelTheme(options.theme ?? "default");
1769
- let showOthers = options.showOthers === true;
1770
- const call = options.call;
1771
- const onRestartRequested = options.onRestartRequested;
1772
- const onCloseRequested = typeof options.onCloseRequested === "function" ? options.onCloseRequested : null;
1773
- let lastHTML = "";
1774
- const copyText = options.copyText ?? defaultCopyText$1;
1775
- const skipStore = options.skipStore ?? createBrowserSkipStore(pluginId);
1776
- const hostKind = typeof options.hostKind === "string" && options.hostKind ? options.hostKind : null;
1777
- const profileNameOption = typeof options.profileName === "string" && options.profileName ? options.profileName : null;
1778
- const diagCopyFormat = options.diagCopyFormat === "line" ? "line" : "block";
1779
- let changelogMarkdown = typeof options.changelogMarkdown === "string" ? options.changelogMarkdown : null;
1780
- let snapshot = null;
1781
- let manual = null;
1782
- let queue = null;
1783
- let receipt = null;
1784
- let requestId = null;
1785
- let lastError = null;
1786
- let lastErrorKind = null;
1787
- let lastDiag = null;
1788
- let copyNotice = null;
1789
- let noticeExpiresAt = 0;
1790
- let busyAct = null;
1791
- let mounted = true;
1792
- let envProfileName = null;
1793
- let envHostKind = null;
1794
- function sayCopy(text) {
1795
- copyNotice = text;
1796
- try {
1797
- noticeExpiresAt = Date.now() + 5e3;
1798
- } catch {
1799
- noticeExpiresAt = 0;
1800
- }
1801
- }
1802
- function queueArgs() {
1803
- return {
1804
- includeQueue: true,
1805
- includeEnv: true,
1806
- showOthers,
1807
- ...requestId ? { requestId } : {}
1808
- };
1809
- }
1810
- function render() {
1811
- if (!mounted) return;
1812
- if (!copyNotice) noticeExpiresAt = 0;
1813
- else if (noticeExpiresAt && Date.now() > noticeExpiresAt) {
1814
- copyNotice = null;
1815
- noticeExpiresAt = 0;
1816
- }
1817
- const latest = snapshot?.latestVersion ?? null;
1818
- const skippedLatest = !!latest && validReleaseVersion(latest) && skipStore.has(latest);
1819
- const nextHTML = renderUpdatePanelHTML({
1820
- snapshot,
1821
- manual,
1822
- queue,
1823
- busyAct,
1824
- skippedLatest,
1825
- lastError,
1826
- errorKind: lastErrorKind,
1827
- changelogMarkdown,
1828
- mode,
1829
- showOthers,
1830
- pluginId,
1831
- copyNotice,
1832
- theme,
1833
- profileName: profileNameOption ?? envProfileName,
1834
- hostKind: hostKind ?? envHostKind
1835
- });
1836
- if (nextHTML !== lastHTML) {
1837
- lastHTML = nextHTML;
1838
- container.innerHTML = nextHTML;
1839
- }
1840
- }
1841
- function applyStatusReply(reply) {
1842
- if (!isObject$2(reply)) return;
1843
- if (reply["ok"] === true) {
1844
- const s = asSnapshot$1(reply["snapshot"]);
1845
- if (s) snapshot = s;
1846
- manual = typeof reply["manual"] === "string" ? reply["manual"] : null;
1847
- if (reply["receipt"] && isObject$2(reply["receipt"]) && typeof reply["receipt"]["checkId"] === "string") receipt = { checkId: reply["receipt"]["checkId"] };
1848
- const q = asQueue(reply["queue"]);
1849
- if (q) queue = q;
1850
- const env = reply["env"];
1851
- if (isObject$2(env)) {
1852
- const pn = env["profileName"];
1853
- const hk = env["environmentKind"];
1854
- if (typeof pn === "string" && pn) envProfileName = pn;
1855
- if (typeof hk === "string" && hk) envHostKind = hk;
1856
- }
1857
- lastError = null;
1858
- lastErrorKind = null;
1859
- lastDiag = Object.prototype.hasOwnProperty.call(reply, "diag") ? reply["diag"] : null;
1860
- } else {
1861
- lastError = typeof reply["error"] === "string" ? reply["error"] : "check-failed";
1862
- lastErrorKind = typeof reply["errorKind"] === "string" && reply["errorKind"].trim() ? reply["errorKind"].trim() : null;
1863
- lastDiag = Object.prototype.hasOwnProperty.call(reply, "diag") ? reply["diag"] : null;
1864
- }
1865
- }
1866
- function pendingAutoChangelogVersion() {
1867
- if (!autoChangelogEnabled || manualChangelogOverride || !mounted) return null;
1868
- const v = snapshot?.latestVersion ?? null;
1869
- if (typeof v !== "string" || !validReleaseVersion(v)) return null;
1870
- if (!snapshot || snapshot.canInstall !== true) return null;
1871
- try {
1872
- if (skipStore.has(v)) return null;
1873
- } catch {}
1874
- if (changelogInflight.has(v)) return null;
1875
- const hasCache = changelogCache.has(v);
1876
- const failedAt = changelogFailedAt.has(v) ? changelogFailedAt.get(v) : null;
1877
- let nowMs = 0;
1878
- try {
1879
- nowMs = Date.now();
1880
- } catch {
1881
- nowMs = 0;
1882
- }
1883
- if (!shouldFetchChangelog({
1884
- hasCache,
1885
- failedAt,
1886
- now: nowMs,
1887
- isManual: false
1888
- })) return null;
1889
- return v;
1890
- }
1891
- function maybeAutoChangelog() {
1892
- const v = pendingAutoChangelogVersion();
1893
- if (v === null) return;
1894
- changelogInflight.add(v);
1895
- const seq = autoSeq;
1896
- Promise.resolve().then(() => call(changelogPhone, { version: v })).then((reply) => {
1897
- changelogInflight.delete(v);
1898
- if (!mounted || seq !== autoSeq) return;
1899
- if (!reply || typeof reply !== "object" || reply["ok"] !== true) {
1900
- try {
1901
- changelogFailedAt.set(v, Date.now());
1902
- } catch {
1903
- try {
1904
- changelogFailedAt.set(v, 0);
1905
- } catch {}
1906
- }
1907
- return;
1908
- }
1909
- const md = reply["markdown"];
1910
- changelogCache.set(v, typeof md === "string" ? md : null);
1911
- if (typeof md === "string") {
1912
- changelogMarkdown = md;
1913
- render();
1914
- }
1915
- }, () => {
1916
- changelogInflight.delete(v);
1917
- if (mounted && seq === autoSeq) try {
1918
- changelogFailedAt.set(v, Date.now());
1919
- } catch {
1920
- try {
1921
- changelogFailedAt.set(v, 0);
1922
- } catch {}
1923
- }
1924
- });
1925
- }
1926
- async function refresh() {
1927
- if (!mounted) return;
1928
- try {
1929
- const reply = await call(phoneNames.updateStatus, queueArgs());
1930
- if (!mounted) return;
1931
- applyStatusReply(reply);
1932
- } catch {
1933
- if (!mounted) return;
1934
- lastError = "check-failed";
1935
- lastErrorKind = null;
1936
- lastDiag = null;
1937
- }
1938
- render();
1939
- maybeAutoChangelog();
1940
- }
1941
- async function act(kind, arg) {
1942
- if (!mounted) return;
1943
- copyNotice = null;
1944
- switch (kind) {
1945
- case "check":
1946
- if (busyAct) return;
1947
- busyAct = "check";
1948
- copyNotice = "正在查新版…";
1949
- render();
1950
- try {
1951
- const reply = await call(phoneNames.updateCheck, queueArgs());
1952
- if (!mounted) return;
1953
- applyStatusReply(reply);
1954
- changelogFailedAt.clear();
1955
- } catch {
1956
- if (!mounted) return;
1957
- lastError = "check-failed";
1958
- lastErrorKind = null;
1959
- lastDiag = null;
1960
- } finally {
1961
- busyAct = null;
1962
- if (copyNotice === "正在查新版…") copyNotice = null;
1963
- }
1964
- render();
1965
- maybeAutoChangelog();
1966
- return;
1967
- case "install":
1968
- if (busyAct) return;
1969
- busyAct = "install";
1970
- copyNotice = "正在安装…";
1971
- render();
1972
- try {
1973
- if (!receipt) {
1974
- const checked = await call(phoneNames.updateCheck, queueArgs());
1975
- if (!mounted) return;
1976
- applyStatusReply(checked);
1977
- }
1978
- if (!mounted) return;
1979
- if (!receipt) {
1980
- lastError = "check-expired";
1981
- lastErrorKind = "check-expired";
1982
- lastDiag = null;
1983
- render();
1984
- return;
1985
- }
1986
- requestId = randomRequestId();
1987
- const reply = await call(phoneNames.updateInstall, {
1988
- checkId: receipt.checkId,
1989
- requestId,
1990
- ...queueArgs()
1991
- });
1992
- if (!mounted) return;
1993
- applyStatusReply(reply);
1994
- } catch {
1995
- if (!mounted) return;
1996
- lastError = "check-failed";
1997
- lastErrorKind = null;
1998
- lastDiag = null;
1999
- } finally {
2000
- busyAct = null;
2001
- if (copyNotice === "正在安装…") copyNotice = null;
2002
- }
2003
- render();
2004
- maybeAutoChangelog();
2005
- return;
2006
- case "skip": {
2007
- const v = snapshot?.latestVersion ?? null;
2008
- if (v && validReleaseVersion(v)) try {
2009
- skipStore.skip(v);
2010
- } catch {}
2011
- render();
2012
- return;
2013
- }
2014
- case "reset-skip":
2015
- try {
2016
- skipStore.reset(arg ?? snapshot?.latestVersion ?? void 0);
2017
- } catch {}
2018
- await refresh();
2019
- return;
2020
- case "copy-manual":
2021
- if (manual) try {
2022
- await copyText(manual);
2023
- sayCopy("手工命令已复制,粘到终端整行执行即可。");
2024
- } catch {
2025
- sayCopy("复制失败,请手动选中上面的命令。");
2026
- }
2027
- render();
2028
- return;
2029
- case "copy-diag": {
2030
- const jobCode = snapshot?.job?.state === "failed" ? messageCodeOf(snapshot.job.message) || "install-failed" : "";
2031
- const code = failureCodeOf({
2032
- error: lastError,
2033
- errorKind: lastErrorKind
2034
- }, jobCode || snapshot?.blockedReason || "check-failed");
2035
- const detail = snapshot?.job?.message && snapshot.job.message.includes(":") ? snapshot.job.message.slice(snapshot.job.message.indexOf(":") + 1) : snapshot?.job?.message;
2036
- const text = buildUpdateDiagCopy({
2037
- pluginId,
2038
- code,
2039
- detail,
2040
- runningVersion: snapshot?.runningVersion ?? null,
2041
- installedVersion: snapshot?.installedVersion ?? null,
2042
- latestVersion: snapshot?.latestVersion ?? null,
2043
- hostKind,
2044
- queuePosition: queue?.position ?? null,
2045
- requestId,
2046
- checkId: receipt?.checkId ?? null,
2047
- route: null,
2048
- diag: lastDiag,
2049
- manual,
2050
- format: diagCopyFormat
2051
- });
2052
- try {
2053
- await copyText(text);
2054
- sayCopy("诊断已复制,直接粘给插件作者即可(已脱敏)。");
2055
- } catch {
2056
- sayCopy("复制失败,请手动选中上面的信息。");
2057
- }
2058
- render();
2059
- return;
2060
- }
2061
- case "toggle-queue":
2062
- showOthers = !showOthers;
2063
- await refresh();
2064
- return;
2065
- case "restart-hint":
2066
- try {
2067
- if (typeof onRestartRequested === "function") {
2068
- await onRestartRequested();
2069
- sayCopy("已按调用方的重启流程处理;重启后新版生效。");
2070
- } else sayCopy("本宿主未提供重启入口:请手动重启宿主,重启后新版生效。");
2071
- } catch {
2072
- sayCopy("重启入口调用失败:请手动重启宿主,重启后新版生效。");
2073
- }
2074
- render();
2075
- return;
2076
- case "close-view":
2077
- await requestDialogClose();
2078
- return;
2079
- }
2080
- }
2081
- async function setMode(next) {
2082
- if (next !== "embedded" && next !== "dialog") throw new Error(`[dsh-plugin-update] \u6446\u653E\u5F62\u6001\u975E\u6CD5\uFF1A\u53EA\u6536 embedded \u6216 dialog\uFF08\u6536\u5230 ${JSON.stringify(next)}\uFF09`);
2083
- mode = next;
2084
- render();
2085
- }
2086
- async function setTheme(next) {
2087
- if (next !== "default" && next !== "archive" && next !== "d5-paper") 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`);
2088
- theme = normalizePanelTheme(next);
2089
- render();
2090
- }
2091
- async function setShowOthers(show) {
2092
- showOthers = show === true;
2093
- await refresh();
2094
- }
2095
- function onClick(ev) {
2096
- try {
2097
- const t = ev;
2098
- const btn = t?.target && typeof t.target.closest === "function" ? t.target.closest("[data-action]") : null;
2099
- const kind = btn?.getAttribute ? btn.getAttribute("data-action") : null;
2100
- if (!kind) return;
2101
- act(kind);
2102
- } catch {}
2103
- }
2104
- async function requestDialogClose() {
2105
- if (!mounted || mode !== "dialog") return;
2106
- if (onCloseRequested) try {
2107
- await onCloseRequested();
2108
- } catch {}
2109
- unmount();
2110
- }
2111
- function onKeyDown(ev) {
2112
- try {
2113
- const e = ev;
2114
- if (!mounted || mode !== "dialog" || !e || e.key !== "Escape") return;
2115
- requestDialogClose();
2116
- } catch {}
2117
- }
2118
- function setChangelogMarkdown(markdown) {
2119
- changelogMarkdown = typeof markdown === "string" ? markdown : null;
2120
- if (typeof markdown === "string") manualChangelogOverride = true;
2121
- render();
2122
- }
2123
- function unmount() {
2124
- if (!mounted) return;
2125
- mounted = false;
2126
- autoSeq++;
2127
- changelogInflight.clear();
2128
- try {
2129
- timer.clear(handle);
2130
- } catch {}
2131
- try {
2132
- container.removeEventListener?.("click", onClick);
2133
- } catch {}
2134
- try {
2135
- container.removeEventListener?.("keydown", onKeyDown);
2136
- } catch {}
1486
+ const errLines = [];
1487
+ if (b.kind === "failed" || b.kind === "blocked") errLines.push(`<div class="dsh-upd-err">\u7A33\u5B9A\u7801 <code>${escapeHtml$2(String(b.kind === "blocked" ? snapshot?.blockedReason ?? b.kind : "install-failed"))}</code>\uFF1A\u4E0A\u4E00\u6761\u4E2D\u6587\u8BF4\u660E\u5C31\u662F\u8981\u7528\u6237\u505A\u7684\u4E8B\uFF1B\u8981\u5F80\u4E0A\u6E38\u62A5\uFF0C\u7528\u300C\u590D\u5236\u8BCA\u65AD\u300D\u6574\u6BB5\u7C98\uFF08\u5DF2\u8131\u654F\uFF09\u3002</div>`);
1488
+ errLines.push(`<div class="dsh-upd-log">\u6DF1\u6316\u770B\u65E5\u5FD7\uFF1A\u6309\u63D2\u4EF6\u6807\u8BC6 <code>${escapeHtml$2(pluginId)}</code> \u8FC7\u6EE4 <code>host.call</code>\u3001<code>host.call.fail</code>\u3001<code>update.install.exec</code> \u4E09\u4E2A\u4E8B\u4EF6\u3002</div>`);
1489
+ if (copyNotice) errLines.push(`<div class="dsh-upd-copy" role="status">${escapeHtml$2(copyNotice)}</div>`);
1490
+ parts.push(chapterOf(4, errLines.join("")));
2137
1491
  }
2138
- render();
2139
- try {
2140
- container.addEventListener?.("click", onClick);
2141
- } catch {}
2142
- try {
2143
- container.addEventListener?.("keydown", onKeyDown);
2144
- } catch {}
2145
- const timer = getTimer$1();
2146
- const handle = timer.set(() => {
2147
- refresh();
2148
- }, pollMs);
2149
- try {
2150
- const h = handle;
2151
- if (h && typeof h.unref === "function") h.unref();
2152
- } catch {}
2153
- refresh();
1492
+ parts.push(chapterOf(5, 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$2(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>`));
1493
+ if (showActions && mode === "dialog") parts.push(`<div class="dsh-upd-footer"><span class="dsh-upd-foot-note">\u5B89\u88C5\u5728\u5BBF\u4E3B\u4FA7\u7EE7\u7EED\u8DD1\uFF0C\u91CD\u5F00\u6062\u590D\u663E\u793A</span><button type="button" data-action="close-view" title="\u5173\u95ED\u9762\u677F\uFF08\u5B89\u88C5\u5728\u5BBF\u4E3B\u4FA7\u7EE7\u7EED\u8DD1\uFF0C\u53EF\u91CD\u5F00\u6062\u590D\u663E\u793A\uFF09">\u5173\u95ED</button></div>`);
1494
+ return parts.join("\n");
1495
+ }
1496
+ function renderUpdatePanelHTML(input) {
1497
+ const view = panelViewModel(input);
1498
+ const kernel = renderUpdatePanelKernel(input, view);
1499
+ const d5 = normalizePanelTheme(input.theme) === "d5-paper";
1500
+ const attr = d5 ? " data-theme=\"d5-paper\"" : "";
1501
+ const sealAttr = ` data-seal="${escapeHtml$2(view.seal.text)}" data-seal-tone="${view.seal.tone}"`;
1502
+ const body = input.mode === "dialog" ? `<div class="dsh-upd-overlay" data-mode="dialog"><div class="dsh-upd" data-mode="dialog" data-plugin="${escapeHtml$2(input.pluginId)}"${sealAttr}${attr}>
1503
+ ${kernel}
1504
+ </div></div>` : `<div class="dsh-upd" data-mode="embedded" data-plugin="${escapeHtml$2(input.pluginId)}"${sealAttr}${attr}>
1505
+ ${kernel}
1506
+ </div>`;
1507
+ return `<style>${d5 ? `${UPDATE_PANEL_CSS}
1508
+ ${UPDATE_PANEL_D5_CSS}` : UPDATE_PANEL_CSS}</style>
1509
+ ${body}`;
1510
+ }
1511
+ //#endregion
1512
+ //#region ../../node_modules/.pnpm/dsh-plugin-update@0.5.4/node_modules/dsh-plugin-update/dist/entry.js
1513
+ const UPDATE_ENTRY_CSS = [
1514
+ ".dsh-upd-entry{display:inline-flex;align-items:center;gap:8px;font:13px/1.6 system-ui,\"Microsoft YaHei\",sans-serif;color:var(--dsh-upd-fg,#1f2937)}",
1515
+ ".dsh-upd-entry-btn{font:inherit;border:1px solid var(--dsh-upd-line,#d1d5db);border-radius:6px;",
1516
+ "background:var(--dsh-upd-btn,#f9fafb);color:inherit;padding:4px 12px;cursor:pointer}",
1517
+ ".dsh-upd-entry-btn:hover{border-color:var(--dsh-upd-primary,#2563eb)}",
1518
+ ".dsh-upd-entry-btn,.dsh-upd-entry-dot{transition:background-color .15s ease,border-color .15s ease,color .15s ease,transform .06s ease}",
1519
+ ".dsh-upd-entry-btn:active:not(:disabled){transform:translateY(1px)}",
1520
+ ".dsh-upd-entry-btn:disabled,.dsh-upd-entry-dot:disabled{opacity:.55;cursor:wait}",
1521
+ ".dsh-upd-entry-btn[aria-busy=\"true\"]::after{content:\"\";display:inline-block;width:10px;height:10px;margin-left:7px;vertical-align:-1px;",
1522
+ "border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:dsh-upd-entry-spin .8s linear infinite}",
1523
+ "@keyframes dsh-upd-entry-spin{to{transform:rotate(360deg)}}",
1524
+ "@media (prefers-reduced-motion: reduce){.dsh-upd-entry-btn,.dsh-upd-entry-dot{transition:none}.dsh-upd-entry-btn[aria-busy=\"true\"]::after{animation:none}}",
1525
+ ".dsh-upd-entry-btn:focus-visible,.dsh-upd-entry-dot:focus-visible{outline:2px solid var(--dsh-upd-focus,#2563eb);outline-offset:1px}",
1526
+ ".dsh-upd-entry[data-state=\"update\"] .dsh-upd-entry-btn{border-color:var(--dsh-upd-ok-line,#059669);color:var(--dsh-upd-ok-line,#059669)}",
1527
+ ".dsh-upd-entry[data-state=\"restart\"] .dsh-upd-entry-btn{border-color:var(--dsh-upd-warn-line,#d97706);color:var(--dsh-upd-warn-line,#d97706)}",
1528
+ ".dsh-upd-entry[data-state=\"failed\"] .dsh-upd-entry-btn{border-color:var(--dsh-upd-bad-line,#dc2626);color:var(--dsh-upd-bad-line,#dc2626)}",
1529
+ ".dsh-upd-entry-dot{width:10px;height:10px;padding:0;border:0;border-radius:50%;background:var(--dsh-upd-line,#9ca3af);cursor:pointer}",
1530
+ ".dsh-upd-entry[data-state=\"update\"] .dsh-upd-entry-dot{background:var(--dsh-upd-ok-line,#059669)}",
1531
+ ".dsh-upd-entry[data-state=\"busy\"] .dsh-upd-entry-dot,.dsh-upd-entry[data-state=\"restart\"] .dsh-upd-entry-dot{background:var(--dsh-upd-warn-line,#d97706)}",
1532
+ ".dsh-upd-entry[data-state=\"failed\"] .dsh-upd-entry-dot{background:var(--dsh-upd-bad-line,#dc2626)}",
1533
+ ".dsh-upd-entry-note{font-size:12.5px;opacity:.9;background:var(--dsh-upd-bg,#ffffff);border:1px solid var(--dsh-upd-line,#e5e7eb);border-radius:4px;padding:1px 8px}",
1534
+ ".dsh-upd-entry[data-theme=\"d5-paper\"] .dsh-upd-entry-note{background:var(--d5-card);border-color:var(--d5-line-strong);color:var(--d5-ink)}",
1535
+ ".dsh-upd-entry[data-theme=\"d5-paper\"]{--d5-ink:#1a1a1a;--d5-muted:#6f675a;--d5-line-strong:#c4b896;--d5-accent:#c8402a;--d5-card:#fffdf6;",
1536
+ "font-family:Georgia,\"Songti SC\",\"STSong\",\"SimSun\",serif;color:var(--d5-ink)}",
1537
+ ".dsh-upd-entry[data-theme=\"d5-paper\"] .dsh-upd-entry-btn{border-color:var(--d5-line-strong);background:var(--d5-card);color:var(--d5-ink);border-radius:3px}",
1538
+ ".dsh-upd-entry[data-theme=\"d5-paper\"] .dsh-upd-entry-btn:hover{border-color:var(--d5-accent);color:var(--d5-accent)}",
1539
+ "@media (prefers-color-scheme: dark){.dsh-upd-entry[data-theme=\"d5-paper\"]{--d5-ink:#ece5d3;--d5-muted:#a89c83;--d5-line-strong:#5c4e3b;--d5-accent:#e0684e;--d5-card:#1e1a15}}",
1540
+ "@media (prefers-color-scheme: dark){.dsh-upd-entry{color:#e5e7eb}",
1541
+ ".dsh-upd-entry-btn{--dsh-upd-btn:#1f2937;--dsh-upd-line:#374151}}"
1542
+ ].join("\n");
1543
+ function isTerminalPhase(phase) {
1544
+ return phase === "done" || phase === "failed" || phase === "skipped" || phase === "current";
1545
+ }
1546
+ function asMillis(value, fallback) {
1547
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : fallback;
1548
+ }
1549
+ function isNonEmptyString(value) {
1550
+ return typeof value === "string" && value.length > 0;
1551
+ }
1552
+ const PHASES = [
1553
+ "pending",
1554
+ "checking",
1555
+ "ready",
1556
+ "installing",
1557
+ "current",
1558
+ "done",
1559
+ "failed",
1560
+ "skipped"
1561
+ ];
1562
+ function asPhase(value) {
1563
+ return typeof value === "string" && PHASES.includes(value) ? value : "pending";
1564
+ }
1565
+ function batchRequestId(sessionId, key) {
1566
+ return "batch:" + sessionId + ":" + key;
1567
+ }
1568
+ function emptyBatchSession() {
2154
1569
  return {
2155
- refresh,
2156
- act,
2157
- setMode,
2158
- setTheme,
2159
- setShowOthers,
2160
- setChangelogMarkdown,
2161
- unmount
1570
+ version: 1,
1571
+ id: "",
1572
+ selfKey: null,
1573
+ stopOnFailure: false,
1574
+ order: [],
1575
+ entries: [],
1576
+ createdAt: 0,
1577
+ updatedAt: 0
2162
1578
  };
2163
1579
  }
1580
+ function normalizeBatchSession(raw) {
1581
+ try {
1582
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return emptyBatchSession();
1583
+ const input = raw;
1584
+ if (input["version"] !== 1) return emptyBatchSession();
1585
+ const id = isNonEmptyString(input["id"]) ? input["id"] : "";
1586
+ const selfKey = isNonEmptyString(input["selfKey"]) ? input["selfKey"] : null;
1587
+ const entries = [];
1588
+ const seen = /* @__PURE__ */ new Set();
1589
+ const rawEntries = input["entries"];
1590
+ if (Array.isArray(rawEntries)) for (const item of rawEntries) {
1591
+ if (!item || typeof item !== "object" || Array.isArray(item)) continue;
1592
+ const e = item;
1593
+ const key = e["key"];
1594
+ if (!isNonEmptyString(key) || seen.has(key)) continue;
1595
+ seen.add(key);
1596
+ const requestId = isNonEmptyString(e["requestId"]) ? e["requestId"] : batchRequestId(id, key);
1597
+ entries.push({
1598
+ key,
1599
+ phase: asPhase(e["phase"]),
1600
+ requestId,
1601
+ targetVersion: isNonEmptyString(e["targetVersion"]) ? e["targetVersion"] : null,
1602
+ restartRequired: e["restartRequired"] === true,
1603
+ error: isNonEmptyString(e["error"]) ? e["error"] : null,
1604
+ updatedAt: asMillis(e["updatedAt"], 0)
1605
+ });
1606
+ }
1607
+ const order = entries.map((e) => e.key);
1608
+ return {
1609
+ version: 1,
1610
+ id,
1611
+ selfKey: selfKey && seen.has(selfKey) ? selfKey : null,
1612
+ stopOnFailure: input["stopOnFailure"] === true,
1613
+ order,
1614
+ entries,
1615
+ createdAt: asMillis(input["createdAt"], 0),
1616
+ updatedAt: asMillis(input["updatedAt"], 0)
1617
+ };
1618
+ } catch {
1619
+ return emptyBatchSession();
1620
+ }
1621
+ }
2164
1622
  //#endregion
2165
- //#region ../../node_modules/.pnpm/dsh-plugin-update@0.5.1/node_modules/dsh-plugin-update/dist/panel-batch.js
1623
+ //#region ../../node_modules/.pnpm/dsh-plugin-update@0.5.4/node_modules/dsh-plugin-update/dist/panel-batch.js
2166
1624
  const BATCH_PANEL_POLL = {
2167
1625
  defaultMs: 1500,
2168
1626
  minMs: 250
@@ -2701,6 +2159,7 @@ ${body}`;
2701
2159
  const call = options.call;
2702
2160
  const titles = options.titles && typeof options.titles === "object" ? options.titles : {};
2703
2161
  const onRestartRequested = options.onRestartRequested;
2162
+ const onCloseRequested = typeof options.onCloseRequested === "function" ? options.onCloseRequested : null;
2704
2163
  const copyText = options.copyText ?? defaultCopyText;
2705
2164
  const skipStores = /* @__PURE__ */ new Map();
2706
2165
  const autoChangelogEnabled = options.autoChangelog !== false;
@@ -3025,16 +2484,23 @@ ${body}`;
3025
2484
  render();
3026
2485
  return;
3027
2486
  case "close":
3028
- if (mode === "dialog") unmount();
2487
+ await requestDialogClose();
3029
2488
  return;
3030
2489
  default: return;
3031
2490
  }
3032
2491
  }
2492
+ async function requestDialogClose() {
2493
+ if (!mounted || mode !== "dialog") return;
2494
+ if (onCloseRequested) try {
2495
+ await onCloseRequested();
2496
+ } catch {}
2497
+ unmount();
2498
+ }
3033
2499
  function onKeyDown(ev) {
3034
2500
  try {
3035
2501
  const e = ev;
3036
2502
  if (!mounted || mode !== "dialog" || !e || e.key !== "Escape") return;
3037
- unmount();
2503
+ requestDialogClose();
3038
2504
  } catch {}
3039
2505
  }
3040
2506
  function setTheme(next) {
@@ -3104,108 +2570,89 @@ ${body}`;
3104
2570
  };
3105
2571
  }
3106
2572
  //#endregion
3107
- //#region ../../node_modules/.pnpm/dsh-plugin-update@0.5.1/node_modules/dsh-plugin-update/dist/entry.js
2573
+ //#region ../../node_modules/.pnpm/dsh-plugin-update@0.5.4/node_modules/dsh-plugin-update/dist/entry-batch.js
3108
2574
  const LABEL_IDLE = "检查更新";
3109
- const LABEL_FAILED = "更新失败,点此查看";
3110
2575
  const LABEL_BUSY = "正在安装…";
3111
- const LABEL_RESTART = "待重启";
3112
- function hasUpdateOf(snapshot) {
3113
- if (!snapshot) return false;
3114
- const latest = snapshot.latestVersion;
3115
- if (typeof latest !== "string" || !latest.trim()) return false;
3116
- return latest.trim() !== snapshot.runningVersion;
3117
- }
3118
- function entryStateKind(state) {
3119
- const snapshot = state?.snapshot ?? null;
3120
- const job = snapshot?.job ?? null;
3121
- if (job && (job.state === "installing" || job.state === "verifying")) return "busy";
3122
- if (snapshot && (snapshot.blockedReason === "pending-restart" || job?.state === "restart-required")) return "restart";
3123
- if (state && state.error || job && (job.state === "failed" || job.state === "interrupted")) return "failed";
3124
- if (hasUpdateOf(snapshot)) return "update";
3125
- return "idle";
3126
- }
3127
- function entryLabelFor(state) {
3128
- switch (entryStateKind(state)) {
3129
- case "busy": return LABEL_BUSY;
3130
- case "restart": return LABEL_RESTART;
3131
- case "failed": return LABEL_FAILED;
3132
- case "update": {
3133
- const latest = state?.snapshot?.latestVersion;
3134
- return typeof latest === "string" && latest.trim() ? `\u6709\u65B0\u7248 ${latest.trim()}` : LABEL_IDLE;
3135
- }
3136
- default: return LABEL_IDLE;
3137
- }
3138
- }
3139
- const UPDATE_ENTRY_CSS = [
3140
- ".dsh-upd-entry{display:inline-flex;align-items:center;gap:8px;font:13px/1.6 system-ui,\"Microsoft YaHei\",sans-serif;color:var(--dsh-upd-fg,#1f2937)}",
3141
- ".dsh-upd-entry-btn{font:inherit;border:1px solid var(--dsh-upd-line,#d1d5db);border-radius:6px;",
3142
- "background:var(--dsh-upd-btn,#f9fafb);color:inherit;padding:4px 12px;cursor:pointer}",
3143
- ".dsh-upd-entry-btn:hover{border-color:var(--dsh-upd-primary,#2563eb)}",
3144
- ".dsh-upd-entry-btn,.dsh-upd-entry-dot{transition:background-color .15s ease,border-color .15s ease,color .15s ease,transform .06s ease}",
3145
- ".dsh-upd-entry-btn:active:not(:disabled){transform:translateY(1px)}",
3146
- ".dsh-upd-entry-btn:disabled,.dsh-upd-entry-dot:disabled{opacity:.55;cursor:wait}",
3147
- ".dsh-upd-entry-btn[aria-busy=\"true\"]::after{content:\"\";display:inline-block;width:10px;height:10px;margin-left:7px;vertical-align:-1px;",
3148
- "border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:dsh-upd-entry-spin .8s linear infinite}",
3149
- "@keyframes dsh-upd-entry-spin{to{transform:rotate(360deg)}}",
3150
- "@media (prefers-reduced-motion: reduce){.dsh-upd-entry-btn,.dsh-upd-entry-dot{transition:none}.dsh-upd-entry-btn[aria-busy=\"true\"]::after{animation:none}}",
3151
- ".dsh-upd-entry-btn:focus-visible,.dsh-upd-entry-dot:focus-visible{outline:2px solid var(--dsh-upd-focus,#2563eb);outline-offset:1px}",
3152
- ".dsh-upd-entry[data-state=\"update\"] .dsh-upd-entry-btn{border-color:var(--dsh-upd-ok-line,#059669);color:var(--dsh-upd-ok-line,#059669)}",
3153
- ".dsh-upd-entry[data-state=\"restart\"] .dsh-upd-entry-btn{border-color:var(--dsh-upd-warn-line,#d97706);color:var(--dsh-upd-warn-line,#d97706)}",
3154
- ".dsh-upd-entry[data-state=\"failed\"] .dsh-upd-entry-btn{border-color:var(--dsh-upd-bad-line,#dc2626);color:var(--dsh-upd-bad-line,#dc2626)}",
3155
- ".dsh-upd-entry-dot{width:10px;height:10px;padding:0;border:0;border-radius:50%;background:var(--dsh-upd-line,#9ca3af);cursor:pointer}",
3156
- ".dsh-upd-entry[data-state=\"update\"] .dsh-upd-entry-dot{background:var(--dsh-upd-ok-line,#059669)}",
3157
- ".dsh-upd-entry[data-state=\"busy\"] .dsh-upd-entry-dot,.dsh-upd-entry[data-state=\"restart\"] .dsh-upd-entry-dot{background:var(--dsh-upd-warn-line,#d97706)}",
3158
- ".dsh-upd-entry[data-state=\"failed\"] .dsh-upd-entry-dot{background:var(--dsh-upd-bad-line,#dc2626)}",
3159
- ".dsh-upd-entry-note{font-size:12.5px;opacity:.9;background:var(--dsh-upd-bg,#ffffff);border:1px solid var(--dsh-upd-line,#e5e7eb);border-radius:4px;padding:1px 8px}",
3160
- ".dsh-upd-entry[data-theme=\"d5-paper\"] .dsh-upd-entry-note{background:var(--d5-card);border-color:var(--d5-line-strong);color:var(--d5-ink)}",
3161
- ".dsh-upd-entry[data-theme=\"d5-paper\"]{--d5-ink:#1a1a1a;--d5-muted:#6f675a;--d5-line-strong:#c4b896;--d5-accent:#c8402a;--d5-card:#fffdf6;",
3162
- "font-family:Georgia,\"Songti SC\",\"STSong\",\"SimSun\",serif;color:var(--d5-ink)}",
3163
- ".dsh-upd-entry[data-theme=\"d5-paper\"] .dsh-upd-entry-btn{border-color:var(--d5-line-strong);background:var(--d5-card);color:var(--d5-ink);border-radius:3px}",
3164
- ".dsh-upd-entry[data-theme=\"d5-paper\"] .dsh-upd-entry-btn:hover{border-color:var(--d5-accent);color:var(--d5-accent)}",
3165
- "@media (prefers-color-scheme: dark){.dsh-upd-entry[data-theme=\"d5-paper\"]{--d5-ink:#ece5d3;--d5-muted:#a89c83;--d5-line-strong:#5c4e3b;--d5-accent:#e0684e;--d5-card:#1e1a15}}",
3166
- "@media (prefers-color-scheme: dark){.dsh-upd-entry{color:#e5e7eb}",
3167
- ".dsh-upd-entry-btn{--dsh-upd-btn:#1f2937;--dsh-upd-line:#374151}}"
3168
- ].join("\n");
3169
- const ENTRY_ATTR = "data-dsh-upd-entry";
3170
- const ENTRY_SELECTOR = "[data-dsh-upd-entry]";
3171
2576
  function isObject(value) {
3172
2577
  return !!value && typeof value === "object" && !Array.isArray(value);
3173
2578
  }
3174
- function asSnapshot(value) {
3175
- if (!isObject(value)) return null;
3176
- if (typeof value["runningVersion"] !== "string") return null;
3177
- if (typeof value["canInstall"] !== "boolean") return null;
2579
+ function asRows(value) {
2580
+ if (!Array.isArray(value)) return null;
3178
2581
  return value;
3179
2582
  }
3180
2583
  function escapeHtml(value) {
3181
2584
  return String(value).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
3182
2585
  }
3183
- function mountUpdateEntry(container, options) {
3184
- if (!container || typeof container.innerHTML !== "string") throw new Error("[dsh-plugin-update] 挂更新入口件需要一个有 innerHTML 的容器");
3185
- if (!options || typeof options !== "object") throw new Error("[dsh-plugin-update] 挂更新入口件缺少配置:插件标识 pluginId 必填");
3186
- const pluginId = options.pluginId;
3187
- if (typeof pluginId !== "string" || !pluginId) throw new Error(`[dsh-plugin-update] \u63D2\u4EF6\u6807\u8BC6 pluginId \u5FC5\u586B\uFF1A\u987B\u4E3A\u975E\u7A7A\u5B57\u7B26\u4E32\uFF08\u6536\u5230 ${JSON.stringify(pluginId)}\uFF09`);
3188
- if (typeof options.call !== "function") throw new Error("[dsh-plugin-update] 挂更新入口件需要传输函数 call(入口件调宿主电话的唯一接触面)");
3189
- const rawPrefix = options.prefix;
3190
- const prefix = rawPrefix === void 0 ? "wf" : rawPrefix;
3191
- const phoneNames = buildPhoneNames(prefix);
2586
+ function batchEntrySummary(state) {
2587
+ const rows = state?.rows ?? null;
2588
+ const counts = batchLedgerCounts(Array.isArray(rows) ? rows : []);
2589
+ const total = Array.isArray(rows) ? rows.length : 0;
2590
+ let kind;
2591
+ if (!Array.isArray(rows) || rows.length === 0) kind = state?.error ? "failed" : "idle";
2592
+ else if (counts.installing > 0) kind = "busy";
2593
+ else if (counts.failed > 0) kind = "failed";
2594
+ else if (counts.restart > 0) kind = "restart";
2595
+ else if (counts.updatable > 0) kind = "update";
2596
+ else kind = "idle";
2597
+ if (kind !== "busy" && state?.error) {
2598
+ if (Array.isArray(rows) && rows.length > 0 && counts.failed === 0 && counts.installing === 0) kind = "failed";
2599
+ }
2600
+ const hasUpdate = counts.installing > 0 || counts.failed > 0 || counts.restart > 0 || counts.updatable > 0;
2601
+ let label;
2602
+ switch (kind) {
2603
+ case "busy":
2604
+ label = LABEL_BUSY;
2605
+ break;
2606
+ case "failed":
2607
+ label = counts.failed > 0 ? counts.failed + " 家失败,点此查看" : "更新失败,点此查看";
2608
+ break;
2609
+ case "restart":
2610
+ label = counts.restart + " 家待重启";
2611
+ break;
2612
+ case "update":
2613
+ label = counts.updatable + " 家可更新";
2614
+ break;
2615
+ default: label = LABEL_IDLE;
2616
+ }
2617
+ return {
2618
+ kind,
2619
+ label,
2620
+ updatable: counts.updatable,
2621
+ installing: counts.installing,
2622
+ pending: counts.pending,
2623
+ restart: counts.restart,
2624
+ failed: counts.failed,
2625
+ skipped: counts.skipped,
2626
+ settled: counts.settled,
2627
+ total,
2628
+ hasUpdate
2629
+ };
2630
+ }
2631
+ function mountUpdateBatchEntry(container, options) {
2632
+ if (!container || typeof container.innerHTML !== "string") throw new Error("[dsh-plugin-update] 挂批量入口件需要一个有 innerHTML 的容器");
2633
+ if (!options || typeof options !== "object") throw new Error("[dsh-plugin-update] 挂批量入口件缺少配置:批量电话前缀 prefix 必填");
2634
+ if (typeof options.call !== "function") throw new Error("[dsh-plugin-update] 挂批量入口件需要传输函数 call(入口件调宿主电话的唯一接触面)");
2635
+ const phones = buildBatchPhoneNames(options.prefix);
3192
2636
  const variant = options.variant ?? "button";
3193
- if (variant !== "button" && variant !== "badge" && variant !== "inline") throw new Error(`[dsh-plugin-update] \u5165\u53E3\u4EF6\u5F62\u6001\u975E\u6CD5\uFF1A\u53EA\u6536 button / badge / inline\uFF08\u6536\u5230 ${JSON.stringify(options.variant)}\uFF09`);
2637
+ if (variant !== "button" && variant !== "badge" && variant !== "inline") throw new Error("[dsh-plugin-update] 入口件形态非法:只收 button / badge / inline(收到 " + JSON.stringify(options.variant) + ")");
3194
2638
  const autoCheck = options.autoCheck ?? "mount";
3195
- if (autoCheck !== "mount" && autoCheck !== "never") throw new Error(`[dsh-plugin-update] \u81EA\u52A8\u68C0\u67E5\u65F6\u673A\u975E\u6CD5\uFF1A\u53EA\u6536 mount \u6216 never\uFF08\u6536\u5230 ${JSON.stringify(options.autoCheck)}\uFF09`);
3196
- const openOn = options.openOn ?? "has-update";
3197
- if (openOn !== "has-update" && openOn !== "always" && openOn !== "manual" && openOn !== "direct") throw new Error(`[dsh-plugin-update] \u70B9\u51FB\u53BB\u5411\u975E\u6CD5\uFF1A\u53EA\u6536 has-update / always / manual / direct\uFF08\u6536\u5230 ${JSON.stringify(options.openOn)}\uFF09`);
3198
- if (options.theme !== void 0 && options.theme !== "default" && options.theme !== "archive" && options.theme !== "d5-paper") 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`);
2639
+ if (autoCheck !== "mount" && autoCheck !== "never") throw new Error("[dsh-plugin-update] 自动检查时机非法:只收 mount 或 never(收到 " + JSON.stringify(options.autoCheck) + ")");
2640
+ const openOn = options.openOn ?? "always";
2641
+ if (openOn !== "has-update" && openOn !== "always" && openOn !== "manual" && openOn !== "direct") throw new Error("[dsh-plugin-update] 点击去向非法:只收 has-update / always / manual / direct(收到 " + JSON.stringify(options.openOn) + ")");
2642
+ if (options.theme !== void 0 && options.theme !== "default" && options.theme !== "archive" && options.theme !== "d5-paper") throw new Error("[dsh-plugin-update] 主题非法:只收 default 或 archive(d5-paper 为旧别名仍可用)(收到 " + JSON.stringify(options.theme) + ")");
3199
2643
  let theme = normalizePanelTheme(options.theme ?? "default");
3200
2644
  const pollMs = options.pollMs;
3201
- if (pollMs !== void 0 && (typeof pollMs !== "number" || !Number.isFinite(pollMs) || pollMs < 250)) throw new Error(`[dsh-plugin-update] \u9762\u677F\u8F6E\u8BE2\u95F4\u9694\u975E\u6CD5\uFF1A\u4E0D\u5F97\u5C0F\u4E8E 250 \u6BEB\u79D2\uFF08\u6536\u5230 ${JSON.stringify(options.pollMs)}\uFF09`);
2645
+ if (pollMs !== void 0 && (typeof pollMs !== "number" || !Number.isFinite(pollMs) || pollMs < 250)) throw new Error("[dsh-plugin-update] 面板轮询间隔非法:不得小于 250 毫秒(收到 " + JSON.stringify(options.pollMs) + ")");
3202
2646
  const labelOverride = typeof options.label === "string" && options.label ? options.label : null;
3203
- const profileName = typeof options.profileName === "string" && options.profileName ? options.profileName : null;
2647
+ const titles = options.titles && typeof options.titles === "object" ? options.titles : {};
3204
2648
  const onActivate = typeof options.onActivate === "function" ? options.onActivate : null;
3205
2649
  const call = options.call;
3206
- let snapshot = null;
2650
+ const ENTRY_ATTR = "data-dsh-upd-entry";
2651
+ const ENTRY_SELECTOR = "[data-dsh-upd-entry]";
2652
+ let rows = null;
3207
2653
  let error = null;
3208
2654
  let note = null;
2655
+ let loaded = false;
3209
2656
  let activating = false;
3210
2657
  let mounted = true;
3211
2658
  let panel = null;
@@ -3230,28 +2677,27 @@ ${body}`;
3230
2677
  }
3231
2678
  function stateOf() {
3232
2679
  return {
3233
- snapshot,
2680
+ rows,
3234
2681
  error
3235
2682
  };
3236
2683
  }
3237
- function currentLabel() {
3238
- return labelOverride ?? entryLabelFor(stateOf());
2684
+ function summaryOf() {
2685
+ return batchEntrySummary(stateOf());
3239
2686
  }
3240
- function hasUpdate() {
3241
- return hasUpdateOf(snapshot);
2687
+ function currentLabel() {
2688
+ return labelOverride ?? summaryOf().label;
3242
2689
  }
3243
2690
  function phoneArgs() {
3244
2691
  return {};
3245
2692
  }
3246
2693
  function entryHTML() {
3247
- const kind = entryStateKind(stateOf());
2694
+ const summary = summaryOf();
3248
2695
  const text = activating ? "正在查新版…" : currentLabel();
3249
2696
  const busyAttr = activating ? " disabled aria-busy=\"true\"" : "";
3250
2697
  const themeAttr = theme === "d5-paper" ? " data-theme=\"d5-paper\"" : "";
3251
- const control = variant === "badge" ? `<button type="button" class="dsh-upd-entry-dot" ${ENTRY_ATTR}="activate" title="${escapeHtml(text)}" aria-label="${escapeHtml(text)}"${busyAttr}></button>` : `<button type="button" class="dsh-upd-entry-btn" ${ENTRY_ATTR}="activate"${busyAttr}>${escapeHtml(text)}</button>`;
3252
- const noteHTML = note ? `<span class="dsh-upd-entry-note" data-dsh-upd-note="1">${escapeHtml(note)}</span>` : "";
3253
- return `<style>${UPDATE_ENTRY_CSS}</style>
3254
- <span class="dsh-upd-entry" data-variant="${variant}" data-state="${kind}"${themeAttr}>${control}${noteHTML}</span>`;
2698
+ const control = variant === "badge" ? "<button type=\"button\" class=\"dsh-upd-entry-dot\" " + ENTRY_ATTR + "=\"activate\" title=\"" + escapeHtml(text) + "\" aria-label=\"" + escapeHtml(text) + "\"" + busyAttr + "></button>" : "<button type=\"button\" class=\"dsh-upd-entry-btn\" " + ENTRY_ATTR + "=\"activate\"" + busyAttr + ">" + escapeHtml(text) + "</button>";
2699
+ const noteHTML = note ? "<span class=\"dsh-upd-entry-note\" data-dsh-upd-note=\"1\">" + escapeHtml(note) + "</span>" : "";
2700
+ return "<style>" + UPDATE_ENTRY_CSS + "</style>\n<span class=\"dsh-upd-entry\" data-variant=\"" + variant + "\" data-state=\"" + summary.kind + "\"" + themeAttr + ">" + control + noteHTML + "</span>";
3255
2701
  }
3256
2702
  function render() {
3257
2703
  if (!mounted) return;
@@ -3263,14 +2709,15 @@ ${body}`;
3263
2709
  error = failureCodeOf(isObject(reply) ? reply : null, "check-failed");
3264
2710
  return;
3265
2711
  }
3266
- const next = asSnapshot(reply["snapshot"]);
3267
- if (next) snapshot = next;
2712
+ const next = asRows(reply["rows"]);
2713
+ if (next) rows = next;
3268
2714
  error = null;
2715
+ loaded = true;
3269
2716
  }
3270
2717
  async function refresh() {
3271
2718
  if (!mounted) return;
3272
2719
  try {
3273
- const reply = await call(phoneNames.updateStatus, phoneArgs());
2720
+ const reply = await call(phones.status, phoneArgs());
3274
2721
  if (!mounted) return;
3275
2722
  applyReply(reply);
3276
2723
  } catch {
@@ -3281,7 +2728,7 @@ ${body}`;
3281
2728
  }
3282
2729
  async function checkNow() {
3283
2730
  try {
3284
- const reply = await call(phoneNames.updateCheck, phoneArgs());
2731
+ const reply = await call(phones.check, phoneArgs());
3285
2732
  if (!mounted) return;
3286
2733
  applyReply(reply);
3287
2734
  } catch {
@@ -3291,15 +2738,15 @@ ${body}`;
3291
2738
  }
3292
2739
  function mountPanel(mode) {
3293
2740
  if (!mounted || panelMode !== null) return;
3294
- panel = mountUpdatePanel(panelHost, {
3295
- pluginId,
3296
- prefix,
2741
+ panel = mountUpdateBatchPanel(panelHost, {
2742
+ prefix: options.prefix,
3297
2743
  mode,
3298
2744
  theme,
3299
2745
  pollMs,
3300
- profileName,
2746
+ titles,
2747
+ onRestartRequested: options.onRestartRequested,
2748
+ copyText: options.copyText,
3301
2749
  autoChangelog: options.autoChangelog,
3302
- changelogMarkdown: options.changelogMarkdown ?? null,
3303
2750
  onCloseRequested: () => close(),
3304
2751
  call: panelCall
3305
2752
  });
@@ -3340,23 +2787,29 @@ ${body}`;
3340
2787
  }
3341
2788
  if (!mounted) return;
3342
2789
  if (variant === "badge" || openOn === "manual") {
3343
- if (onActivate) onActivate({
3344
- hasUpdate: hasUpdate(),
3345
- latestVersion: snapshot?.latestVersion ?? null
3346
- });
2790
+ if (onActivate) onActivate(summaryOf());
3347
2791
  else openDialog();
3348
2792
  render();
3349
2793
  return;
3350
2794
  }
3351
- if (openOn === "always" || hasUpdate() || !snapshot || error) {
2795
+ const summary = summaryOf();
2796
+ if (openOn === "always" || summary.hasUpdate || !loaded || error) {
3352
2797
  openDialog();
3353
2798
  return;
3354
2799
  }
3355
- note = `\u5DF2\u662F\u6700\u65B0 ${snapshot.runningVersion}`;
2800
+ note = batchLedgerText({
2801
+ updatable: summary.updatable,
2802
+ installing: summary.installing,
2803
+ pending: summary.pending,
2804
+ restart: summary.restart,
2805
+ failed: summary.failed,
2806
+ skipped: summary.skipped,
2807
+ settled: summary.settled
2808
+ });
3356
2809
  render();
3357
2810
  }
3358
2811
  function setTheme(next) {
3359
- if (next !== "default" && next !== "archive" && next !== "d5-paper") 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`);
2812
+ if (next !== "default" && next !== "archive" && next !== "d5-paper") throw new Error("[dsh-plugin-update] 主题非法:只收 default 或 archive(d5-paper 为旧别名仍可用)(收到 " + JSON.stringify(next) + ")");
3360
2813
  theme = normalizePanelTheme(next);
3361
2814
  if (panel) panel.setTheme(theme);
3362
2815
  else render();
@@ -3410,42 +2863,14 @@ ${body}`;
3410
2863
  open,
3411
2864
  close,
3412
2865
  label: currentLabel,
2866
+ summary: summaryOf,
3413
2867
  setTheme,
3414
2868
  unmount
3415
2869
  };
3416
2870
  }
3417
- //#endregion
3418
- //#region src/install.ts
3419
- /** dsh-life-pack 安装口径(P10 脚手架,P1 #2 定案单命令双包)。
3420
- *
3421
- * 真相(#2 真机结论):dsh plugin 是 pnpm 薄转发,add 单品等价 pnpm add,
3422
- * 传递依赖落盘是;激活否:reconcile 只扫直接 dependencies,传递的 manager
3423
- * 不进 bundles;pnpm 隔离布局下从 profile 解析不到它。
3424
- *
3425
- * 口径:任何单品落地时总管必须已在位导航。实现为单命令双包:
3426
- * dsh plugin add dsh-life-pack dsh-calorie
3427
- * 两者皆直接依赖,由 reconcile 按序激活。单品 package 内保留对总管的
3428
- * dependencies 只作本地开发兜底,不作为单 add 即激活的依据。
3429
- * 不许单卸总管:缺席时总管对应 tab 显示推荐安装并提示补装(见 nav.ts)。
3430
- * 安装验收以 bundles 双含为准,另加单 add 单品后 bundles 无总管的负向断言。
3431
- */
3432
- const MANAGER_PACKAGE = "dsh-life-pack";
3433
- //#endregion
3434
- //#region src/update-targets.ts
3435
- /** 七个更新目标(总管自己 + 六个单品),薄接线唯一定义地(票 1170)。
3436
- *
3437
- * 包名不新开第二源:六单品取自 nav.ts 的 MANAGER_TABS,总管自己取自 install.ts。
3438
- * 七个 phonePrefix 必须互不相同,否则串台。
3439
- */
3440
- const MANAGER_TARGET_KEY = "life-pack";
3441
2871
  /** 批量电话前缀:宿主与面板共用,唯一定义地(面板 mount 参数与宿主登记同源)。 */
3442
2872
  const BATCH_PREFIX = "life";
3443
- const UPDATE_TARGETS = [{
3444
- key: MANAGER_TARGET_KEY,
3445
- title: "爱生活",
3446
- packageName: MANAGER_PACKAGE,
3447
- phonePrefix: "ilife-life-pack"
3448
- }, ...MANAGER_TABS.map((tab) => ({
2873
+ [...MANAGER_TABS.map((tab) => ({
3449
2874
  key: tab.skill,
3450
2875
  title: tab.title,
3451
2876
  packageName: tab.plugin,
@@ -3453,21 +2878,12 @@ ${body}`;
3453
2878
  }))];
3454
2879
  //#endregion
3455
2880
  //#region src/update-dialog.ts
3456
- /** 面板薄包装(票 1170,浏览器侧:无 node 内建,可进 client 束)。
2881
+ /** 面板薄包装(票 1170 起,浏览器侧:无 node 内建,可进 client 束;完全上游 UI,本包零自家样式)。
3457
2882
  *
3458
- * 只定死三组参数,不自建文案与样式:入口形态 button、打开策略 manual(点击交由 onOpen 打开批量弹窗)、
3459
- * 皮肤 archive;批量面板内嵌形态(弹窗壳由本包自家 overlay 提供,关闭由自家按钮收)+ 皮肤 archive;
2883
+ * 只定死三组参数,不自建文案与样式:批量入口形态 button、皮肤 archive、打开策略沿上游缺省 always
2884
+ *(查完总是开批量面板;0.5.4 #49 到达,manual 桥接已删);弹窗 dialog 由入口件内置(含关闭落地);
3460
2885
  * 轮询与更新日志沿上游缺省,中文标题沿目标表(不传 titles 覆盖)。
3461
2886
  */
3462
- /** 总管自己那一行的电话名前缀(入口件状态源,取自目标表,不手写字面量)。 */
3463
- function managerPrefix() {
3464
- const row = UPDATE_TARGETS.find((target) => target.key === MANAGER_TARGET_KEY);
3465
- return row !== void 0 ? row.phonePrefix : "ilife-life-pack";
3466
- }
3467
- /** 总管自己在批量内的插件标识(与宿主侧上游缺省 batchPrefix-key 同口径)。 */
3468
- function managerPluginId() {
3469
- return "life-life-pack";
3470
- }
3471
2887
  /** 把 RpcCallFace 收成面板要的一参形状:上游回包原样透传(含失败回包的 errorKind 与 diag)。
3472
2888
  *
3473
2889
  * 宿主电话表把上游回包整体装进回执 value(成功失败皆如此,只有宿主级故障才走外层失败信封),
@@ -3514,29 +2930,18 @@ ${body}`;
3514
2930
  };
3515
2931
  };
3516
2932
  }
3517
- /** 挂入口按钮(点击打开批量弹窗,状态取总管自己一行的只读快照)。 */
3518
- function mountLifeUpdateEntry(container, call, onOpen, overrides) {
3519
- return mountUpdateEntry(container, {
3520
- pluginId: managerPluginId(),
3521
- prefix: managerPrefix(),
2933
+ /** 挂批量入口(一颗按钮看七家聚合,弹窗 dialog 由入口件内置,关闭落地自带)。
2934
+ *
2935
+ * 0.5.4 #49 到达:`prefix: life` 直调批量五电话,徽标与面板总账同一份数法;manual 桥接与容器事件同步已整段删除。
2936
+ */
2937
+ function mountLifeBatchEntry(container, call, overrides) {
2938
+ return mountUpdateBatchEntry(container, {
2939
+ prefix: BATCH_PREFIX,
3522
2940
  call,
3523
2941
  variant: "button",
3524
- openOn: "manual",
3525
2942
  theme: "archive",
3526
2943
  autoCheck: overrides?.autoCheck ?? "mount",
3527
- label: overrides?.label,
3528
- onActivate: () => {
3529
- onOpen();
3530
- }
3531
- });
3532
- }
3533
- /** 挂批量面板(内嵌形态进自家弹窗壳,离开时 unmount 只停轮询)。 */
3534
- function mountLifeBatchPanel(container, call) {
3535
- return mountUpdateBatchPanel(container, {
3536
- prefix: BATCH_PREFIX,
3537
- call,
3538
- theme: "archive",
3539
- mode: "embedded"
2944
+ label: overrides?.label
3540
2945
  });
3541
2946
  }
3542
2947
  //#endregion
@@ -5327,48 +4732,13 @@ ${body}`;
5327
4732
  display: "inline-flex",
5328
4733
  color: "var(--dsw-alias-label-secondary, #9a9a9a)"
5329
4734
  },
5330
- /** 更新入口挂点:只占位,按钮本体由上游入口件渲染(票 1170)。 */
4735
+ /** 更新入口挂点:只占位,按钮本体由上游入口件渲染(票 1170);批量弹窗壳亦由上游提供,本包零自家样式。 */
5331
4736
  updateEntrySlot: {
5332
4737
  display: "inline-flex",
5333
4738
  alignItems: "center"
5334
- },
5335
- /** 批量弹窗遮罩(自家壳:颜色走主题变量,按钮复用 interactionCss 同一缝)。 */
5336
- updateBackdrop: {
5337
- position: "fixed",
5338
- inset: 0,
5339
- display: "flex",
5340
- alignItems: "center",
5341
- justifyContent: "center",
5342
- padding: 16,
5343
- background: "rgba(0, 0, 0, 0.45)",
5344
- zIndex: 1e3
5345
- },
5346
- updateBox: {
5347
- position: "relative",
5348
- width: "min(720px, 92vw)",
5349
- maxHeight: "84vh",
5350
- overflow: "auto",
5351
- borderRadius: 12,
5352
- border: "1px solid var(--dsw-alias-border-l1, rgba(128,128,128,.35))",
5353
- background: "var(--dsw-alias-bg-layer-1, #232324)",
5354
- color: "var(--dsw-alias-label-primary, inherit)",
5355
- padding: "12px 12px 16px"
5356
- },
5357
- updateClose: {
5358
- position: "sticky",
5359
- top: 0,
5360
- float: "right",
5361
- marginLeft: 8,
5362
- border: "1px solid var(--dsw-alias-border-l1, rgba(128,128,128,.35))",
5363
- background: "transparent",
5364
- color: "var(--dsw-alias-label-primary, inherit)",
5365
- borderRadius: 999,
5366
- padding: "4px 12px",
5367
- fontSize: 13,
5368
- cursor: "pointer"
5369
4739
  }
5370
4740
  };
5371
- /** 更新区(票 1170 薄接线):标题行挂入口按钮,弹窗里是七目标批量面板;缺席页签仍显示静态推荐,不进批量 targets。 */
4741
+ /** 更新区(票 1170 薄接线,完全上游 UI):标题行挂上游入口按钮,点开即上游 dialog 七目标批量面板;缺席页签仍显示静态推荐,不进批量 targets。 */
5372
4742
  /** 标签解析(resolveSlotLabel 同形:thunk 跟活,无则空字串;见 slots lib:27-29)。 */
5373
4743
  function resolveLabel(label) {
5374
4744
  if (typeof label === "function") return label();
@@ -5421,7 +4791,7 @@ ${body}`;
5421
4791
  }, react.createElement("span", { style: S.morePkg }, row.pkg), react.createElement("span", { style: S.moreDesc }, row.desc), react.createElement("span", { style: S.moreIcon }, react.createElement(ExternalIcon, null)))));
5422
4792
  }
5423
4793
  /** 版本胶囊那一行字:只在这里拼一次,屏上与用例读的是同一串。 */
5424
- const MANAGER_VERSION_TEXT = "dsh-life-pack · 0.3.40";
4794
+ const MANAGER_VERSION_TEXT = "dsh-life-pack · 0.3.41";
5425
4795
  /** 爱生活面板:总设置区 + 检查更新(七家)+ 爱生活页签条(slot 驱动)+ 技能设置页投影/缺席卡。 */
5426
4796
  function LifePackSection(props) {
5427
4797
  const tabsId = react.useId();
@@ -5440,40 +4810,18 @@ ${body}`;
5440
4810
  })), Object.fromEntries(Object.entries(health.rows).map(([id, row]) => [id, row.report ?? void 0]))), [rows, health.rows]);
5441
4811
  const lightOf = (id) => lights.find((light) => light.id === id);
5442
4812
  const [activeId, setActiveId] = react.useState(void 0);
5443
- /** 批量弹窗开关(票 1170):入口件 onActivate 置真,自家关闭按钮与 Esc 置假。 */
5444
- const [batchOpen, setBatchOpen] = react.useState(false);
4813
+ /** 批量入口挂载位(一颗按钮看七家聚合;0.5.4 #49 到达,弹窗 dialog 由入口件内置,开关态亦归上游)。 */
5445
4814
  const entryRef = react.useRef(null);
5446
- const dialogRef = react.useRef(null);
5447
4815
  const callReady = props.getCall() !== null;
5448
4816
  /** 入口件挂载:连接后到才挂(取用器现取),卸载即 unmount(只停入口轮询)。 */
5449
4817
  react.useEffect(() => {
5450
4818
  const mount = entryRef.current;
5451
4819
  if (mount === null || !callReady) return;
5452
- const entry = mountLifeUpdateEntry(mount, managerCallAdapter(props.getCall()), () => {
5453
- setBatchOpen(true);
5454
- });
4820
+ const entry = mountLifeBatchEntry(mount, managerCallAdapter(props.getCall()));
5455
4821
  return () => {
5456
4822
  entry.unmount();
5457
4823
  };
5458
4824
  }, [callReady]);
5459
- /** 批量面板挂载:弹窗开才挂,关即 unmount(只停轮询,宿主侧安装继续跑)。 */
5460
- react.useEffect(() => {
5461
- const mount = dialogRef.current;
5462
- if (mount === null || !batchOpen || !callReady) return;
5463
- const panel = mountLifeBatchPanel(mount, managerCallAdapter(props.getCall()));
5464
- return () => {
5465
- panel.unmount();
5466
- };
5467
- }, [batchOpen, callReady]);
5468
- function closeBatch() {
5469
- setBatchOpen(false);
5470
- }
5471
- function onDialogKeyDown(event) {
5472
- if (event.key === "Escape") {
5473
- event.stopPropagation();
5474
- setBatchOpen(false);
5475
- }
5476
- }
5477
4825
  const [visitedIds, setVisitedIds] = react.useState(() => /* @__PURE__ */ new Set());
5478
4826
  const active = MANAGER_TABS.some((t) => t.plugin === activeId) ? activeId : MANAGER_TABS[0].plugin;
5479
4827
  react.useEffect(() => {
@@ -5707,21 +5055,7 @@ ${body}`;
5707
5055
  const reco = recoFor(tab);
5708
5056
  return react.createElement("div", { style: S.reco }, reco.hint, react.createElement("span", { style: S.cmd }, reco.installCmd));
5709
5057
  })());
5710
- }), batchOpen ? react.createElement("div", {
5711
- role: "dialog",
5712
- "aria-modal": "true",
5713
- "aria-label": "批量更新",
5714
- style: S.updateBackdrop,
5715
- onKeyDown: onDialogKeyDown
5716
- }, react.createElement("style", { "data-ilife-interaction": "t1" }, interactionCss()), react.createElement("div", { style: S.updateBox }, react.createElement("button", {
5717
- type: "button",
5718
- "data-ilife-press": "",
5719
- style: S.updateClose,
5720
- "aria-label": "关闭更新面板(批量推进在宿主侧继续跑)",
5721
- onClick: closeBatch
5722
- }, "关闭"), react.createElement("div", { ref: (element) => {
5723
- dialogRef.current = element;
5724
- } }))) : null, react.createElement(MorePluginsCard, null));
5058
+ }), react.createElement(MorePluginsCard, null));
5725
5059
  }
5726
5060
  function apply(ctx) {
5727
5061
  const getCall = () => ctx.connection?.rpc?.call ?? null;