dsh-plugin-update 0.5.3 → 0.5.5

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/entry.js CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  mountUpdatePanel,
6
6
  normalizePanelTheme
7
7
  } from "./panel.js";
8
+ import { BILINGUAL_CSS, bilingualHTML, bilingualText } from "./bilingual.js";
8
9
  const LABEL_IDLE = "\u68C0\u67E5\u66F4\u65B0";
9
10
  const LABEL_FAILED = "\u66F4\u65B0\u5931\u8D25\uFF0C\u70B9\u6B64\u67E5\u770B";
10
11
  const LABEL_BUSY = "\u6B63\u5728\u5B89\u88C5\u2026";
@@ -28,6 +29,36 @@ function entryStateKind(state) {
28
29
  if (hasUpdateOf(snapshot)) return "update";
29
30
  return "idle";
30
31
  }
32
+ function entryBilingualKeyFor(state) {
33
+ switch (entryStateKind(state)) {
34
+ case "busy":
35
+ return "entry.label.busy";
36
+ case "restart":
37
+ return "entry.label.restart";
38
+ case "failed":
39
+ return "entry.label.failed";
40
+ case "update": {
41
+ const latest = state?.snapshot?.latestVersion;
42
+ return typeof latest === "string" && latest.trim() ? "entry.label.has-update" : "entry.label.idle";
43
+ }
44
+ default:
45
+ return "entry.label.idle";
46
+ }
47
+ }
48
+ function entryBilingualValuesFor(state) {
49
+ const key = entryBilingualKeyFor(state);
50
+ if (key === "entry.label.has-update") {
51
+ const v = state?.snapshot?.latestVersion;
52
+ return { version: typeof v === "string" ? v.trim() : "" };
53
+ }
54
+ return {};
55
+ }
56
+ function entryBilingualHTMLFor(state) {
57
+ return bilingualHTML(entryBilingualKeyFor(state), entryBilingualValuesFor(state));
58
+ }
59
+ function entryBilingualTextFor(state) {
60
+ return bilingualText(entryBilingualKeyFor(state), entryBilingualValuesFor(state));
61
+ }
31
62
  function entryLabelFor(state) {
32
63
  switch (entryStateKind(state)) {
33
64
  case "busy":
@@ -66,13 +97,13 @@ const UPDATE_ENTRY_CSS = [
66
97
  '.dsh-upd-entry[data-state="failed"] .dsh-upd-entry-dot{background:var(--dsh-upd-bad-line,#dc2626)}',
67
98
  // 小字自带底(深色宿主 + 浅色变量时也读得出;浅底宿主上只是多一圈细线,不抢戏)。
68
99
  ".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}",
69
- '.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)}',
70
- '.dsh-upd-entry[data-theme="d5-paper"]{--d5-ink:#1a1a1a;--d5-muted:#6f675a;--d5-line-strong:#c4b896;--d5-accent:#c8402a;--d5-card:#fffdf6;',
100
+ '.dsh-upd-entry[data-theme="archive"] .dsh-upd-entry-note{background:var(--d5-card);border-color:var(--d5-line-strong);color:var(--d5-ink)}',
101
+ '.dsh-upd-entry[data-theme="archive"]{--d5-ink:#1a1a1a;--d5-muted:#6f675a;--d5-line-strong:#c4b896;--d5-accent:#c8402a;--d5-card:#fffdf6;',
71
102
  'font-family:Georgia,"Songti SC","STSong","SimSun",serif;color:var(--d5-ink)}',
72
103
  // 按钮脸自己不透明(深色宿主 + 浅色系统变量时也读得出;hover 红在深浅底上都可见)。
73
- '.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}',
74
- '.dsh-upd-entry[data-theme="d5-paper"] .dsh-upd-entry-btn:hover{border-color:var(--d5-accent);color:var(--d5-accent)}',
75
- '@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}}',
104
+ '.dsh-upd-entry[data-theme="archive"] .dsh-upd-entry-btn{border-color:var(--d5-line-strong);background:var(--d5-card);color:var(--d5-ink);border-radius:3px}',
105
+ '.dsh-upd-entry[data-theme="archive"] .dsh-upd-entry-btn:hover{border-color:var(--d5-accent);color:var(--d5-accent)}',
106
+ '@media (prefers-color-scheme: dark){.dsh-upd-entry[data-theme="archive"]{--d5-ink:#ece5d3;--d5-muted:#a89c83;--d5-line-strong:#5c4e3b;--d5-accent:#e0684e;--d5-card:#1e1a15}}',
76
107
  "@media (prefers-color-scheme: dark){.dsh-upd-entry{color:#e5e7eb}",
77
108
  ".dsh-upd-entry-btn{--dsh-upd-btn:#1f2937;--dsh-upd-line:#374151}}"
78
109
  ].join("\n");
@@ -133,7 +164,7 @@ function mountUpdateEntry(container, options) {
133
164
  const call = options.call;
134
165
  let snapshot = null;
135
166
  let error = null;
136
- let note = null;
167
+ let noteVersion = null;
137
168
  let activating = false;
138
169
  let mounted = true;
139
170
  let panel = null;
@@ -170,12 +201,14 @@ function mountUpdateEntry(container, options) {
170
201
  }
171
202
  function entryHTML() {
172
203
  const kind = entryStateKind(stateOf());
173
- const text = activating ? "\u6B63\u5728\u67E5\u65B0\u7248\u2026" : currentLabel();
204
+ const labelHTML = labelOverride ? escapeHtml(labelOverride) : activating ? bilingualHTML("entry.action.checking") : entryBilingualHTMLFor(stateOf());
205
+ const labelText = labelOverride ?? (activating ? bilingualText("entry.action.checking") : entryBilingualTextFor(stateOf()));
174
206
  const busyAttr = activating ? ' disabled aria-busy="true"' : "";
175
- const themeAttr = theme === "d5-paper" ? ' data-theme="d5-paper"' : "";
176
- 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>`;
177
- const noteHTML = note ? `<span class="dsh-upd-entry-note" data-dsh-upd-note="1">${escapeHtml(note)}</span>` : "";
178
- return `<style>${UPDATE_ENTRY_CSS}</style>
207
+ const themeAttr = theme === "archive" ? ' data-theme="archive"' : "";
208
+ const control = variant === "badge" ? `<button type="button" class="dsh-upd-entry-dot" ${ENTRY_ATTR}="activate" title="${escapeHtml(labelText)}" aria-label="${escapeHtml(labelText)}"${busyAttr}></button>` : `<button type="button" class="dsh-upd-entry-btn" ${ENTRY_ATTR}="activate"${busyAttr}>${labelHTML}</button>`;
209
+ const noteHTML = noteVersion ? `<span class="dsh-upd-entry-note" data-dsh-upd-note="1">${bilingualHTML("entry.note.up-to-date", { version: noteVersion })}</span>` : "";
210
+ return `<style>${UPDATE_ENTRY_CSS}
211
+ ${BILINGUAL_CSS}</style>
179
212
  <span class="dsh-upd-entry" data-variant="${variant}" data-state="${kind}"${themeAttr}>${control}${noteHTML}</span>`;
180
213
  }
181
214
  function render() {
@@ -258,7 +291,7 @@ function mountUpdateEntry(container, options) {
258
291
  return;
259
292
  }
260
293
  activating = true;
261
- note = null;
294
+ noteVersion = null;
262
295
  render();
263
296
  try {
264
297
  await checkNow();
@@ -276,7 +309,7 @@ function mountUpdateEntry(container, options) {
276
309
  openDialog();
277
310
  return;
278
311
  }
279
- note = `\u5DF2\u662F\u6700\u65B0 ${snapshot.runningVersion}`;
312
+ noteVersion = snapshot.runningVersion;
280
313
  render();
281
314
  }
282
315
  function setTheme(next) {
@@ -342,6 +375,10 @@ function mountUpdateEntry(container, options) {
342
375
  }
343
376
  export {
344
377
  UPDATE_ENTRY_CSS,
378
+ entryBilingualHTMLFor,
379
+ entryBilingualKeyFor,
380
+ entryBilingualTextFor,
381
+ entryBilingualValuesFor,
345
382
  entryLabelFor,
346
383
  entryStateKind,
347
384
  mountUpdateEntry
package/dist/host.js CHANGED
@@ -13,6 +13,7 @@ import { manualCommand } from "./commands.js";
13
13
  import { fetchReleaseChangelogText } from "./changelog-io.js";
14
14
  import { shouldFetchChangelog } from "./changelog.js";
15
15
  import { buildDiag } from "./diag.js";
16
+ import { LOG_EVENT_CALL, LOG_EVENT_CALL_FAIL } from "./log-events.js";
16
17
  import { fetchNpmVersionRelease, isVersionAllowedInChannel, updateError, validReleaseVersion, validRequestId } from "./service.js";
17
18
  import { createUpdateDiskPorts, createUpdateExecutor, createUpdateQueuePorts } from "./store.js";
18
19
  import { createUpdateReader, defaultHomeDir, resolveProfileName, resolveTargetPackage } from "./reader.js";
@@ -496,7 +497,7 @@ function loggedPhone(method, kind, pluginId, fn, diagCtx) {
496
497
  try {
497
498
  const out = await fn(safeArgs);
498
499
  const snapshot = out && out.snapshot ? out.snapshot : out;
499
- emit("info", "host.call", { method, latencyMs: Date.now() - t0, ok: true, kind, pluginId });
500
+ emit("info", LOG_EVENT_CALL, { method, latencyMs: Date.now() - t0, ok: true, kind, pluginId });
500
501
  return {
501
502
  ok: true,
502
503
  snapshot,
@@ -509,7 +510,7 @@ function loggedPhone(method, kind, pluginId, fn, diagCtx) {
509
510
  };
510
511
  } catch (error) {
511
512
  const payload = toUpdateErrorPayload(error);
512
- emit("warn", "host.call.fail", { method, kind, errorHash: hash8(String(error?.message || payload.error)), pluginId });
513
+ emit("warn", LOG_EVENT_CALL_FAIL, { method, kind, errorHash: hash8(String(error?.message || payload.error)), pluginId });
513
514
  let diag;
514
515
  try {
515
516
  const cfg = diagCtx?.config;
@@ -581,11 +582,11 @@ function loggedChangelogPhone(method, pluginId, fn, diagCtx) {
581
582
  const safeArgs = args && typeof args === "object" ? args : {};
582
583
  try {
583
584
  const out = await fn(safeArgs);
584
- emit("info", "host.call", { method, latencyMs: Date.now() - t0, ok: true, kind: "update-changelog", pluginId });
585
+ emit("info", LOG_EVENT_CALL, { method, latencyMs: Date.now() - t0, ok: true, kind: "update-changelog", pluginId });
585
586
  return { ok: true, version: out.version, markdown: out.markdown };
586
587
  } catch (error) {
587
588
  const payload = toUpdateErrorPayload(error);
588
- emit("warn", "host.call.fail", { method, kind: "update-changelog", errorHash: hash8(String(error?.message || payload.error)), pluginId });
589
+ emit("warn", LOG_EVENT_CALL_FAIL, { method, kind: "update-changelog", errorHash: hash8(String(error?.message || payload.error)), pluginId });
589
590
  let diag;
590
591
  try {
591
592
  const cfg = diagCtx?.config;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * src/log-events.ts —— 日志事件名(单源)。
3
+ *
4
+ * 宿主发射(host/store)、面板路牌(panel)、测试断言都从这里取:
5
+ * 改名即全改,不存在“发射侧改了、路牌还是旧名”的静默分叉。
6
+ * 本文件零导入,宿主/面板同缝(与 redaction 同口径)。
7
+ */
8
+ export declare const LOG_EVENT_CALL = "host.call";
9
+ export declare const LOG_EVENT_CALL_FAIL = "host.call.fail";
10
+ export declare const LOG_EVENT_INSTALL_EXEC = "update.install.exec";
@@ -0,0 +1,9 @@
1
+ // AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/log-events.ts
2
+ const LOG_EVENT_CALL = "host.call";
3
+ const LOG_EVENT_CALL_FAIL = "host.call.fail";
4
+ const LOG_EVENT_INSTALL_EXEC = "update.install.exec";
5
+ export {
6
+ LOG_EVENT_CALL,
7
+ LOG_EVENT_CALL_FAIL,
8
+ LOG_EVENT_INSTALL_EXEC
9
+ };
@@ -49,6 +49,11 @@ export interface BatchPanelOptions {
49
49
  * 老宿主没给行电话名即回中性提示;false 关闭。
50
50
  */
51
51
  autoChangelog?: boolean;
52
+ /**
53
+ * 弹窗关闭的落地(与单插件面板同一口径):只在 dialog 下点「关闭」/按 Esc 时调用;调用方在此撤掉弹窗 DOM。
54
+ * 不传则回退为只停轮询(unmount),DOM 留给调用方处理。入口件打开的 dialog 已内置(收 dialog + 还原按钮)。
55
+ */
56
+ onCloseRequested?: () => void | Promise<void>;
52
57
  }
53
58
  /** 面板可点的动作(HTML 上 data-act 一一对应;测试走同一条路)。 */
54
59
  export type BatchPanelActionKind = 'check' | 'install' | 'resume' | 'cancel' | 'row-install' | 'row-skip' | 'row-resume-skip' | 'row-cancel-queue' | 'row-copy-manual' | 'row-copy-diag' | 'toggle-details' | 'restart' | 'close';
@@ -131,7 +131,7 @@ function renderBatchPanelHTML(input) {
131
131
  parts.push('<div class="dsh-upd-batch-macros">');
132
132
  parts.push('<button type="button" data-act="check"' + disabled + ' title="\u91CD\u65B0\u8BFB\u53D6\u6279\u91CF\u72B6\u6001\uFF08\u53EA\u8BFB\uFF09">\u68C0\u67E5\u66F4\u65B0</button>');
133
133
  parts.push('<button type="button" data-act="install" data-primary="1"' + disabled + ' title="\u628A\u6709\u65B0\u7248\u7684\u51E0\u5BB6\u4E00\u6B21\u63D0\u4EA4\uFF1B\u540C\u4E00\u4F1A\u8BDD\u540C\u4E00\u5E42\u7B49\u7F16\u53F7">\u5168\u90E8\u66F4\u65B0</button>');
134
- if (mode === "dialog") parts.push('<button type="button" data-act="close" title="\u5173\u95ED\u9762\u677F\uFF08\u6279\u91CF\u63A8\u8FDB\u5728\u5BBF\u4E3B\u4FA7\u7EE7\u7EED\u8DD1\uFF09">\u5173\u95ED</button>');
134
+ if (mode === "dialog") parts.push('<button type="button" data-act="close" title="\u5173\u95ED\u7A97\u53E3\uFF0C\u6279\u91CF\u66F4\u65B0\u4E0D\u53D7\u5F71\u54CD">\u5173\u95ED</button>');
135
135
  parts.push("</div>");
136
136
  parts.push("</div>");
137
137
  const summary = loaded ? batchSummaryText(counts, rows, total, installing, lastError !== null) : "\u6B63\u5728\u8BFB\u53D6\u6279\u91CF\u66F4\u65B0\u72B6\u6001\u2026";
@@ -169,8 +169,8 @@ function renderBatchPanelHTML(input) {
169
169
  parts.push('<div class="dsh-upd-batch-notice" role="status">' + escapeHtml(notice) + "</div>");
170
170
  }
171
171
  const kernel = parts.join(String.fromCharCode(10));
172
- const d5 = normalizePanelTheme(theme) === "d5-paper";
173
- const attr = d5 ? ' data-theme="d5-paper"' : "";
172
+ const d5 = normalizePanelTheme(theme) === "archive";
173
+ const attr = d5 ? ' data-theme="archive"' : "";
174
174
  const seal = batchSealOf(counts);
175
175
  const root = '<div class="dsh-upd dsh-upd-batch" data-mode="' + mode + '" data-seal="' + escapeHtml(seal.text) + '" data-seal-tone="' + seal.tone + '"' + attr + ">\n" + kernel + "\n</div>";
176
176
  const body = mode === "dialog" ? '<div class="dsh-upd-overlay" data-mode="dialog">' + root + "</div>" : root;
@@ -388,16 +388,16 @@ const UPDATE_BATCH_PANEL_CSS = [
388
388
  ].join(String.fromCharCode(10));
389
389
  const UPDATE_BATCH_PANEL_D5_CSS = [
390
390
  "/* \u6279\u91CF\u9762\u677F\u7684 D5 \u76AE\u80A4\uFF1A\u53EA\u6362\u989C\u8272/\u5B57\u4F53/\u95F4\u8DDD\uFF1B\u6839\u4E0A\u7684\u5927\u5370\u7AE0\u7531 UPDATE_PANEL_D5_CSS \u8D1F\u8D23\u3002 */",
391
- '.dsh-upd[data-theme="d5-paper"] .dsh-upd-batch-head{padding-right:120px}',
392
- '.dsh-upd[data-theme="d5-paper"] .dsh-upd-batch-title{font-family:var(--d5-serif);font-size:21px;letter-spacing:.04em}',
393
- '.dsh-upd[data-theme="d5-paper"] .dsh-upd-batch-title i{color:var(--d5-accent)}',
394
- '.dsh-upd[data-theme="d5-paper"] .dsh-upd-brow-main{background:transparent;border:0;color:var(--d5-ink)}',
395
- '.dsh-upd[data-theme="d5-paper"] .dsh-upd-brow-main:hover{color:var(--d5-accent)}',
396
- '.dsh-upd[data-theme="d5-paper"] .dsh-upd-bname{font-family:var(--d5-serif)}',
397
- '.dsh-upd[data-theme="d5-paper"] .dsh-upd-bver{font-family:var(--d5-mono);color:var(--d5-muted)}',
398
- '.dsh-upd[data-theme="d5-paper"] .dsh-upd-batch-ledger{color:var(--d5-muted)}',
399
- '.dsh-upd[data-theme="d5-paper"] .dsh-upd-batch-sum{color:var(--d5-ink)}',
400
- '.dsh-upd[data-theme="d5-paper"] .dsh-upd-brow-set{border-top-color:var(--d5-line)}'
391
+ '.dsh-upd[data-theme="archive"] .dsh-upd-batch-head{padding-right:120px}',
392
+ '.dsh-upd[data-theme="archive"] .dsh-upd-batch-title{font-family:var(--d5-serif);font-size:21px;letter-spacing:.04em}',
393
+ '.dsh-upd[data-theme="archive"] .dsh-upd-batch-title i{color:var(--d5-accent)}',
394
+ '.dsh-upd[data-theme="archive"] .dsh-upd-brow-main{background:transparent;border:0;color:var(--d5-ink)}',
395
+ '.dsh-upd[data-theme="archive"] .dsh-upd-brow-main:hover{color:var(--d5-accent)}',
396
+ '.dsh-upd[data-theme="archive"] .dsh-upd-bname{font-family:var(--d5-serif)}',
397
+ '.dsh-upd[data-theme="archive"] .dsh-upd-bver{font-family:var(--d5-mono);color:var(--d5-muted)}',
398
+ '.dsh-upd[data-theme="archive"] .dsh-upd-batch-ledger{color:var(--d5-muted)}',
399
+ '.dsh-upd[data-theme="archive"] .dsh-upd-batch-sum{color:var(--d5-ink)}',
400
+ '.dsh-upd[data-theme="archive"] .dsh-upd-brow-set{border-top-color:var(--d5-line)}'
401
401
  ].join(String.fromCharCode(10));
402
402
  function escapeHtml(text) {
403
403
  return String(text).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
@@ -627,6 +627,7 @@ function mountUpdateBatchPanel(container, options) {
627
627
  const call = options.call;
628
628
  const titles = options.titles && typeof options.titles === "object" ? options.titles : {};
629
629
  const onRestartRequested = options.onRestartRequested;
630
+ const onCloseRequested = typeof options.onCloseRequested === "function" ? options.onCloseRequested : null;
630
631
  const copyText = options.copyText ?? defaultCopyText;
631
632
  const skipStores = /* @__PURE__ */ new Map();
632
633
  const autoChangelogEnabled = options.autoChangelog !== false;
@@ -968,26 +969,36 @@ function mountUpdateBatchPanel(container, options) {
968
969
  return;
969
970
  }
970
971
  case "close": {
971
- if (mode === "dialog") unmount();
972
+ await requestDialogClose();
972
973
  return;
973
974
  }
974
975
  default:
975
976
  return;
976
977
  }
977
978
  }
979
+ async function requestDialogClose() {
980
+ if (!mounted || mode !== "dialog") return;
981
+ if (onCloseRequested) {
982
+ try {
983
+ await onCloseRequested();
984
+ } catch {
985
+ }
986
+ }
987
+ unmount();
988
+ }
978
989
  function onKeyDown(ev) {
979
990
  try {
980
991
  const e = ev;
981
992
  if (!mounted || mode !== "dialog" || !e || e.key !== "Escape") return;
982
- unmount();
993
+ void requestDialogClose();
983
994
  } catch {
984
995
  }
985
996
  }
986
997
  function setTheme(next) {
987
- if (next !== "default" && next !== "d5-paper") {
988
- throw new Error("[dsh-plugin-update] \u4E3B\u9898\u975E\u6CD5\uFF1A\u53EA\u6536 default \u6216 d5-paper\uFF08\u6536\u5230 " + JSON.stringify(next) + "\uFF09");
998
+ if (next !== "default" && next !== "archive" && next !== "d5-paper") {
999
+ 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");
989
1000
  }
990
- theme = next;
1001
+ theme = normalizePanelTheme(next);
991
1002
  render();
992
1003
  }
993
1004
  function setMode(next) {
package/dist/panel.d.ts CHANGED
@@ -9,16 +9,16 @@ export type UpdatePanelMode = 'embedded' | 'dialog';
9
9
  */
10
10
  export type UpdatePanelTheme = 'default' | 'archive' | 'd5-paper';
11
11
  /**
12
- * 主题归一:archive 与旧别名 d5-paper 走同一套渲染(DOM 属性仍为 d5-paper,既有覆盖样式不断);
12
+ * 主题归一:archive 为首选名,旧别名 d5-paper 仍收并归到 archive(DOM 属性只出 archive);
13
13
  * 其余一律回 default(纯渲染函数永不抛;挂载/setTheme 的非法值另行抛错)。
14
14
  */
15
- export declare function normalizePanelTheme(value: unknown): 'default' | 'd5-paper';
15
+ export declare function normalizePanelTheme(value: unknown): 'default' | 'archive';
16
16
  /** 与宿主通话的传输函数:面板只认这个签名,不认任何宿主对象的具体形状。 */
17
17
  export type UpdatePanelCall = (phoneName: string, args: Record<string, unknown>) => Promise<Record<string, unknown>>;
18
18
  /** 复制文本的出口:不传即尝试浏览器剪贴板,都没有则只留“已生成、请手动复制”提示。 */
19
19
  export type UpdatePanelCopyText = (text: string) => void | Promise<void>;
20
20
  /** 面板可点的动作(与 HTML 里 data-action 一一对应,测试走同一条路)。 */
21
- export type UpdatePanelActionKind = 'check' | 'install' | 'skip' | 'reset-skip' | 'copy-manual' | 'copy-diag' | 'toggle-queue' | 'restart-hint' | 'close-view';
21
+ export type UpdatePanelActionKind = 'check' | 'install' | 'skip' | 'reset-skip' | 'copy-manual' | 'copy-diag' | 'toggle-queue' | 'restart-hint' | 'close-view' | 'dismiss-failure';
22
22
  export interface UpdatePanelOptions {
23
23
  /** 插件标识:必填,与宿主侧 createHostUpdate 传的 pluginId 一致。 */
24
24
  pluginId: string;
@@ -30,6 +30,8 @@ export interface UpdatePanelOptions {
30
30
  theme?: UpdatePanelTheme;
31
31
  /** 是否看他人排队明细:默认只看自己的(他人仅露正忙占位,位置照给)。 */
32
32
  showOthers?: boolean;
33
+ /** 是否显示深挖日志指引:默认显示;面向纯终端用户的嵌入可关(失败证据行不受影响)。 */
34
+ showLogHint?: boolean;
33
35
  /** 轮询间隔毫秒:默认 1000,不得小于 250。 */
34
36
  pollMs?: number;
35
37
  /** 与宿主通话的函数(面板侧唯一的宿主接触面)。 */
@@ -235,7 +237,7 @@ export interface PanelRenderInput extends PanelViewInput {
235
237
  showOthers: boolean;
236
238
  pluginId: string;
237
239
  copyNotice: string | null;
238
- /** 可选主题:不传即默认(输出与旧版一字不差);`d5-paper` 切 D5 档案卷。 */
240
+ /** 可选主题:不传即默认(输出与旧版一字不差);`archive` 切档案卷(旧别名 `d5-paper` 仍收)。 */
239
241
  theme?: UpdatePanelTheme;
240
242
  /** 使用范围名(profile):面板「使用范围」一栏的唯一来源,缺省显示“未知”,不猜。 */
241
243
  profileName?: string | null;
@@ -246,6 +248,10 @@ export interface PanelRenderInput extends PanelViewInput {
246
248
  * 缺省即静默(输出与旧版一字不差);只在挂载器置忙的那次 render 里传。
247
249
  */
248
250
  busyAct?: 'check' | 'install' | null;
251
+ /** 失败引用(失败档案的查询键;缺省即无失败,不画卷宗)。 */
252
+ failure?: PanelFailureRef | null;
253
+ /** 日志指引开关:false 即藏通用日志行(失败证据行不受影响)。 */
254
+ showLogHint?: boolean;
249
255
  /**
250
256
  * 动作面归谁:`default`(缺省)由内核画动作按钮;`none` 只画内容、不画按钮。
251
257
  * 给「调用方自己提供动作面」的场景(如批量面板的详情:动作由批量面板经自己的通道提供)。
@@ -253,6 +259,16 @@ export interface PanelRenderInput extends PanelViewInput {
253
259
  */
254
260
  actions?: 'default' | 'none';
255
261
  }
262
+ /** 内核渲染用的失败引用(只读快照:证据仍在锁存里,这里只传展示键)。 */
263
+ export interface PanelFailureRef {
264
+ requestId: string | null;
265
+ checkId: string | null;
266
+ atMs: number | null;
267
+ source: 'check' | 'install' | null;
268
+ volatile: boolean;
269
+ }
270
+ /** 锁存时刻 → 本地 HH:MM:SS(失败档案“失败于”用;非法值回 null,不画时间)。 */
271
+ export declare function formatLatchTime(atMs: unknown): string | null;
256
272
  /** 内核 HTML(双形态行为等价的根:同一视图产出同一内核,只换外层)。 */
257
273
  export declare function renderUpdatePanelKernel(input: PanelRenderInput, view: PanelView): string;
258
274
  /** 整面板 HTML(含样式;重绘即整体替换 innerHTML,故每次都带 style 也只留一份)。 */