dsh-plugin-update 0.5.5 → 0.5.7

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/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  格式为 Keep-a-Changelog 子集:`Added/Fixed/Changed` 必写,`Deprecated/Removed/Security` 有则透传,`Unreleased` 面板忽略,禁止 git-log 直倒。面板缺日志时中性提示,不挡安装。
4
4
 
5
+ ## [0.5.7] - 2026-10-06
6
+
7
+ ### Fixed
8
+ - 拆入口通道安装禁令(#58 真因):入口件 HTTP 直挂不再源码级禁 updateInstall(入口件核心本就没有安装代码路径,禁令只连坐入口件打开的面板;真正门禁在宿主侧核心);入口打开面板后安装电话端到端验证通过。
9
+
10
+ ## [0.5.6] - 2026-10-06
11
+
12
+ ### Added
13
+ - 弹窗分栏滚动、更新日志折叠与动效体系;双语防倒退门禁四道检查(文案源/快照/契约/零 draft)与自测(#56)。
14
+
15
+ ### Fixed
16
+ - 用户路径抛错锁存非瞬态化(#58):查/装动作的传输抛错改为非瞬态锁存(安装抛错码为 install-failed,不再冒充查失败),轮询不再洗掉;抛错原文脱敏后进复制摘要;安装内补查失败精确归因到查阶段;轮询抛错仍瞬态自愈。
17
+
5
18
  ## [0.5.5] - 2026-10-06
6
19
 
7
20
  ### Added
package/dist/http.d.ts CHANGED
@@ -102,7 +102,12 @@ export interface UpdateEntryHttpOptions extends Omit<UpdateEntryOptions, "call"
102
102
  timeout?: number | HttpTimeouts;
103
103
  timeoutMs?: number | HttpTimeouts;
104
104
  }
105
- /** 入口件 HTTP 直挂:内部源码级禁安装电话,内核复用 mountUpdateEntry。 */
105
+ /** 入口件 HTTP 直挂:调用通道与面板直挂同一条,内核复用 mountUpdateEntry。
106
+ *
107
+ * 通道层不禁安装:入口件核心没有调安装的代码路径(只查 + 打开面板,见 README 铁律),
108
+ * 禁令只会连坐入口件打开的面板(#58:安装键亮着但永远抛错);真正的门禁在宿主侧核心
109
+ *(凭证/排队/安装键/装后校验),面板安装与面板直挂走同一套,不过不少。
110
+ */
106
111
  export declare function mountUpdateEntryHttp(container: UpdatePanelContainer, options: UpdateEntryHttpOptions): UpdateEntryController;
107
112
  export interface UpdateBatchPanelHttpOptions extends Omit<BatchPanelOptions, "call" | "prefix"> {
108
113
  /** 批量电话前缀(与宿主侧一致;五电话名从它算出,不写字面量)。 */
package/dist/http.js CHANGED
@@ -261,15 +261,9 @@ function mountUpdateEntryHttp(container, options) {
261
261
  throw new Error("[dsh-plugin-update] \u6302 HTTP \u5165\u53E3\u4EF6\u7F3A\u5C11\u914D\u7F6E\uFF1A\u63D2\u4EF6\u6807\u8BC6 pluginId \u4E0E\u7F51\u5173\u6839 baseUrl \u5FC5\u586B");
262
262
  }
263
263
  const prefix = options.prefix ?? DEFAULT_PREFIX;
264
- const rawCall = createHttpCall({ prefix, baseUrl: options.baseUrl, routes: options.routes, fetch: options.fetch, fetchImpl: options.fetchImpl, timeout: options.timeout, timeoutMs: options.timeoutMs });
265
- const guarded = async (name, args) => {
266
- if (shortActionOf(name) === "updateInstall") {
267
- throw new Error("[dsh-plugin-update] \u5165\u53E3\u4EF6\u53EA\u5141\u8BB8\u53EA\u8BFB\u7535\u8BDD\uFF0CupdateInstall \u7981\u6B62\uFF08\u6E90\u7801\u7EA7\uFF09");
268
- }
269
- return rawCall(name, args);
270
- };
264
+ const call = createHttpCall({ prefix, baseUrl: options.baseUrl, routes: options.routes, fetch: options.fetch, fetchImpl: options.fetchImpl, timeout: options.timeout, timeoutMs: options.timeoutMs });
271
265
  const { baseUrl: _b, routes: _r, fetch: _f, fetchImpl: _fi, timeout: _t, timeoutMs: _tm, prefix: _p, ...entryRest } = options;
272
- return mountUpdateEntry(container, { ...entryRest, pluginId: options.pluginId, prefix, call: guarded });
266
+ return mountUpdateEntry(container, { ...entryRest, pluginId: options.pluginId, prefix, call });
273
267
  }
274
268
  function mountUpdateBatchPanelHttp(container, options) {
275
269
  if (!options || typeof options !== "object") {
package/dist/lang.d.ts ADDED
@@ -0,0 +1,29 @@
1
+ /** 本包跟随的两种语言:中文(历史行为)与英文(其余语言落英文)。 */
2
+ export type AppLang = 'zh' | 'en';
3
+ /** 显式覆盖的两种形态:稳定码 'zh' | 'en',或带 getActive/subscribe 的服务对象(可注入假信号源,测试不依赖 jsdom)。 */
4
+ export interface LocaleService {
5
+ getActive(): string;
6
+ subscribe?(cb: () => void): () => void;
7
+ }
8
+ /** 入口件/面板/批量挂载点的语言选项:显式覆盖,不传即跟随全局信号。 */
9
+ export type LocaleOption = 'zh' | 'en' | LocaleService | string | null | undefined;
10
+ type LangCallback = (lang: AppLang) => void;
11
+ /**
12
+ * 归一语言标签:zh* → zh;形如 BCP47 的已知非中文标签 → en;空/垃圾 → zh(零回归)。
13
+ * 大小写不敏感,_ 视作 -。
14
+ */
15
+ export declare function normalizeLangTag(tag: unknown): AppLang;
16
+ /**
17
+ * 解出当前语言:显式覆盖 > html[lang] > navigator.languages > zh。
18
+ * 纯函数(除读全局信号外无副作用);无信号永不抛,落 zh。
19
+ */
20
+ export declare function resolveLang(override?: LocaleOption): AppLang;
21
+ /**
22
+ * 订阅语言变化:html[lang] 变化或可选服务源触发时回新语言。
23
+ * 模块级单例:多个挂载件共用一个 MutationObserver;返回 unsubscribe,unmount 后停订。
24
+ * 无 DOM 环境安全降级:没有 document/MutationObserver 时只记回调、永不触发,不抛。
25
+ */
26
+ export declare function subscribeLang(cb: LangCallback, override?: LocaleOption): () => void;
27
+ /** 测试/维护用:清空全部订阅并断开单例观察者(正常业务代码不调它)。 */
28
+ export declare function __resetLangState(): void;
29
+ export {};
@@ -383,6 +383,8 @@ const UPDATE_BATCH_PANEL_CSS = [
383
383
  ".dsh-upd-bdetail{margin:2px 0 10px;overflow:visible}",
384
384
  ".dsh-upd .dsh-upd-bdetail .dsh-upd{max-width:none}",
385
385
  ".dsh-upd-overlay .dsh-upd-bdetail .dsh-upd{max-height:none;overflow:visible}",
386
+ ".dsh-upd-overlay .dsh-upd-bdetail .dsh-upd-body{overflow:visible}",
387
+ ".dsh-upd-overlay .dsh-upd-bdetail .dsh-upd-body .dsh-upd-chap-head{position:static}",
386
388
  "@media (prefers-color-scheme: dark){.dsh-upd-batch-title i{color:var(--dsh-upd-focus,#93c5fd)}}",
387
389
  "@media (prefers-reduced-motion: reduce){.dsh-upd-batch *{transition:none !important;animation:none !important}}"
388
390
  ].join(String.fromCharCode(10));
package/dist/panel.d.ts CHANGED
@@ -18,7 +18,7 @@ export type UpdatePanelCall = (phoneName: string, args: 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' | 'dismiss-failure';
21
+ export type UpdatePanelActionKind = 'check' | 'install' | 'skip' | 'reset-skip' | 'copy-manual' | 'copy-diag' | 'toggle-queue' | 'toggle-changelog' | 'restart-hint' | 'close-view' | 'dismiss-failure';
22
22
  export interface UpdatePanelOptions {
23
23
  /** 插件标识:必填,与宿主侧 createHostUpdate 传的 pluginId 一致。 */
24
24
  pluginId: string;
@@ -86,6 +86,8 @@ export interface UpdatePanelController {
86
86
  setTheme(theme: UpdatePanelTheme): Promise<void>;
87
87
  /** 切换排队可见性(重查一次,位置口径不变)。 */
88
88
  setShowOthers(show: boolean): Promise<void>;
89
+ /** 收起/展开更新日志(纯视图态,不调电话;轮询与换肤不丢)。 */
90
+ setChangelogCollapsed(collapsed: boolean): Promise<void>;
89
91
  /**
90
92
  * 换一版更新日志后重绘(#23:宿主侧备好新文本后调用;传 null/空串即回中性提示)。
91
93
  * 只换日志节,安装门控只跟快照,一字不动。
@@ -258,6 +260,11 @@ export interface PanelRenderInput extends PanelViewInput {
258
260
  * 只读渲染下五章内容、进度条、「已跳过」提示一字不减,只是没有动作按钮。
259
261
  */
260
262
  actions?: 'default' | 'none';
263
+ /**
264
+ * 更新日志折叠(仅挂载态内部用:用户点了 02 章标题行的收起/展开)。
265
+ * 缺省即展开(安全日志必显、可查找;只读渲染下恒展开)。
266
+ */
267
+ changelogCollapsed?: boolean;
261
268
  }
262
269
  /** 内核渲染用的失败引用(只读快照:证据仍在锁存里,这里只传展示键)。 */
263
270
  export interface PanelFailureRef {
package/dist/panel.js CHANGED
@@ -528,7 +528,15 @@ const UPDATE_PANEL_CSS = [
528
528
  ".dsh-upd-changelog-more-note{font-size:12px;opacity:.7;margin:2px 0 4px}",
529
529
  ".dsh-upd-changelog-neutral{color:inherit;opacity:.8}",
530
530
  ".dsh-upd-overlay{position:fixed;inset:0;background:rgba(0,0,0,.35);display:flex;align-items:center;justify-content:center;z-index:9999}",
531
- ".dsh-upd-overlay .dsh-upd{background:var(--dsh-upd-bg,#ffffff);max-height:85vh;overflow:auto}",
531
+ ".dsh-upd-overlay .dsh-upd{background:var(--dsh-upd-bg,#ffffff);max-height:85vh;display:flex;flex-direction:column;overflow:hidden}",
532
+ // —— 弹窗分栏滚动:头(抬头/档案头/横幅/版本条)与尾固定,只有 01–05 章节区滚动 ——
533
+ ".dsh-upd-body{min-height:0}",
534
+ ".dsh-upd-overlay .dsh-upd-body{flex:1 1 auto;overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:var(--dsh-upd-line,#e5e7eb) transparent}",
535
+ ".dsh-upd-overlay .dsh-upd-body::-webkit-scrollbar{width:8px}",
536
+ ".dsh-upd-overlay .dsh-upd-body::-webkit-scrollbar-thumb{background:var(--dsh-upd-line,#e5e7eb);border-radius:4px}",
537
+ ".dsh-upd-overlay .dsh-upd-body::-webkit-scrollbar-track{background:transparent}",
538
+ // —— 章节标题磁吸:滚动时节标题贴顶(纯 CSS sticky;背景跟随主题,D5 另覆)——
539
+ ".dsh-upd-overlay .dsh-upd-body .dsh-upd-chap-head{position:sticky;top:0;z-index:1;background:var(--dsh-upd-bg,#ffffff);padding-top:2px}",
532
540
  // —— 档案头 / 版本条 / 章节 / 进度条 / 跳过行(原型 :208-245 的新结构,默认主题给最小可用样式)——
533
541
  ".dsh-upd-head{display:flex;gap:12px;align-items:baseline;flex-wrap:wrap}",
534
542
  // 卷宗抬头「插件更新 / 更新档案 卷」:D5 档案卷才画,默认(最小)主题不画。
@@ -582,11 +590,18 @@ const UPDATE_PANEL_CSS = [
582
590
  ".dsh-upd button:active:not(:disabled){transform:translateY(1px)}",
583
591
  '.dsh-upd button[aria-busy="true"]{cursor:wait;animation:dsh-upd-pulse 1s ease-in-out infinite}',
584
592
  "@keyframes dsh-upd-pulse{0%,100%{opacity:1}50%{opacity:.55}}",
593
+ // —— 状态横幅淡入 + 日志折叠格动画(纯 CSS;重绘只发生在真变时;reduced-motion 下静止)——
594
+ ".dsh-upd-banner{animation:dsh-upd-fadein .22s ease}",
595
+ "@keyframes dsh-upd-fadein{from{opacity:.35;transform:translateY(2px)}}",
596
+ ".dsh-upd-changelog-foldbox{display:grid;grid-template-rows:1fr;opacity:1;transition:grid-template-rows .22s ease,opacity .18s ease}",
597
+ ".dsh-upd-changelog-foldbox-inner{min-height:0;overflow:hidden}",
598
+ '.dsh-upd-changelog-foldbox[data-open="0"]{grid-template-rows:0fr;opacity:0}',
585
599
  // 在途转圈:纯 CSS ::after,不加 DOM 节点(内核 DOM 冻结);转的是边框缺口,不是 emoji。
586
600
  '.dsh-upd button[aria-busy="true"]::after{content:"";display:inline-block;width:11px;height:11px;margin-left:8px;vertical-align:-1px;',
587
601
  "border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:dsh-upd-spin .8s linear infinite}",
588
602
  "@keyframes dsh-upd-spin{to{transform:rotate(360deg)}}",
589
- '@media (prefers-reduced-motion: reduce){.dsh-upd button{transition:none}.dsh-upd button:active:not(:disabled){transform:none}.dsh-upd button[aria-busy="true"]{animation:none}.dsh-upd-skv{animation:none}}',
603
+ '@media (prefers-reduced-motion: reduce){.dsh-upd button{transition:none}.dsh-upd button:active:not(:disabled){transform:none}.dsh-upd button[aria-busy="true"]{animation:none}.dsh-upd-skv{animation:none}.dsh-upd-banner{animation:none}.dsh-upd-changelog-foldbox{transition:none}}',
604
+ "@media (forced-colors: active){.dsh-upd-overlay .dsh-upd-body .dsh-upd-chap-head{background:Canvas}}",
590
605
  "@media (prefers-color-scheme: dark){.dsh-upd{--dsh-upd-fg:#e5e7eb;--dsh-upd-bg:#111827;--dsh-upd-line:#374151;",
591
606
  "--dsh-upd-btn:#1f2937;--dsh-upd-soft:#1f2937;--dsh-upd-primary:#3b82f6;--dsh-upd-focus:#93c5fd;",
592
607
  // 横幅深色覆盖:底色用低透明度同色系(不是浅色原值),边线提亮,保证「深底浅字」可读。
@@ -650,6 +665,9 @@ const UPDATE_PANEL_D5_CSS = [
650
665
  '.dsh-upd[data-theme="archive"] .dsh-upd-qn{font-family:var(--d5-mono);font-size:11.5px;color:var(--d5-muted)}',
651
666
  '.dsh-upd[data-theme="archive"] .dsh-upd-qseq{color:var(--d5-muted)}',
652
667
  '.dsh-upd[data-theme="archive"] .dsh-upd-chap-note{color:var(--d5-muted)}',
668
+ '.dsh-upd[data-theme="archive"] .dsh-upd-body .dsh-upd-chap-head{background:var(--d5-card)}',
669
+ '.dsh-upd-overlay .dsh-upd[data-theme="archive"] .dsh-upd-body{scrollbar-color:var(--d5-line-strong) transparent}',
670
+ '.dsh-upd-overlay .dsh-upd[data-theme="archive"] .dsh-upd-body::-webkit-scrollbar-thumb{background:var(--d5-line-strong)}',
653
671
  // —— 小印章(原型 :215 `.sealmini`:30px、旋转 -5°、一字)——
654
672
  // 待重启横幅一律不画印章:那一档的标记是左侧手绘 SVG(原型 :446 的 .mark 只有 SVG)。
655
673
  '.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="loading"]::before,.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="idle"]::before,',
@@ -805,9 +823,11 @@ function renderUpdatePanelKernel(input, view) {
805
823
  `<div class="dsh-upd-skipline"><span class="dsh-upd-tag">\u5DF2\u8DF3\u8FC7 ${escapeHtml(snapshot.latestVersion)}</span>\u70B9\u300C\u6062\u590D\u300D\u53EF\u64A4\u9500\uFF0C\u4E4B\u540E\u8FD9\u4E00\u7248\u8FD8\u4F1A\u518D\u63D0\u9192\u3002</div>`
806
824
  );
807
825
  }
808
- parts.push(chapterOf(1, actions.join("")));
826
+ const chapters = [];
827
+ chapters.push(chapterOf(1, actions.join("")));
809
828
  {
810
829
  let inner = "";
830
+ let hasLog = false;
811
831
  if (snapshot && snapshot.latestVersion) {
812
832
  try {
813
833
  const mdText = typeof changelogMarkdown === "string" ? changelogMarkdown : "";
@@ -837,9 +857,11 @@ function renderUpdatePanelKernel(input, view) {
837
857
  } catch {
838
858
  yankedBanner = "";
839
859
  }
840
- inner = `<div class="dsh-upd-changelog-wrap"><div>${escapeHtml(rangeTitle)}</div>${yankedBanner}
860
+ const logOpen = input.changelogCollapsed !== true;
861
+ inner = `<div class="dsh-upd-changelog-wrap"><div>${escapeHtml(rangeTitle)}</div><div class="dsh-upd-changelog-foldbox" data-open="${logOpen ? "1" : "0"}"><div class="dsh-upd-changelog-foldbox-inner">${yankedBanner}
841
862
  ${changelogHTML}
842
- </div>`;
863
+ </div></div></div>`;
864
+ hasLog = Array.isArray(ranged) && ranged.length > 0;
843
865
  } catch {
844
866
  inner = "";
845
867
  }
@@ -849,7 +871,9 @@ ${changelogHTML}
849
871
  snapshot && snapshot.latestVersion ? "\u65E5\u5FD7\u8BFB\u4E0D\u51FA\u6765\uFF0C\u5B89\u88C5\u4E0D\u53D7\u5F71\u54CD\u3002" : "\u8FD8\u6CA1\u67E5\u5230\u65B0\u7248\uFF1B\u67E5\u5230\u540E\u518D\u663E\u793A\u65E5\u5FD7\u3002"
850
872
  )}</div></div>`;
851
873
  }
852
- parts.push(chapterOf(2, inner));
874
+ const logCollapsed = input.changelogCollapsed === true;
875
+ const logNote = showActions && hasLog ? `<span class="dsh-upd-chap-note"><button type="button" data-action="toggle-changelog" title="\u5C55\u5F00\u6216\u6536\u8D77\u66F4\u65B0\u65E5\u5FD7">${logCollapsed ? "\u5C55\u5F00\u66F4\u65B0\u65E5\u5FD7" : "\u6536\u8D77\u66F4\u65B0\u65E5\u5FD7"}</button></span>` : "";
876
+ chapters.push(chapterOf(2, inner, logNote));
853
877
  }
854
878
  {
855
879
  const queued = !!queue && (queue.busy || queue.waiting.length > 0);
@@ -876,9 +900,9 @@ ${changelogHTML}
876
900
  );
877
901
  }
878
902
  const note = showActions ? `<span class="dsh-upd-chap-note"><button type="button" data-action="toggle-queue">${reveal ? "\u9690\u85CF\u4ED6\u4EBA\u660E\u7EC6" : "\u663E\u793A\u5176\u4ED6\u63D2\u4EF6"}</button></span>` : "";
879
- parts.push(chapterOf(3, `<div class="dsh-upd-queue">${rows.join("")}</div>`, note));
903
+ chapters.push(chapterOf(3, `<div class="dsh-upd-queue">${rows.join("")}</div>`, note));
880
904
  } else {
881
- parts.push(
905
+ chapters.push(
882
906
  chapterOf(
883
907
  3,
884
908
  `<div class="dsh-upd-queue"><div class="dsh-upd-changelog-neutral">${escapeHtml(
@@ -930,14 +954,15 @@ ${changelogHTML}
930
954
  }
931
955
  }
932
956
  if (copyNotice) errLines.push(`<div class="dsh-upd-copy" role="status">${escapeHtml(copyNotice)}</div>`);
933
- parts.push(chapterOf(4, errLines.join("")));
957
+ chapters.push(chapterOf(4, errLines.join("")));
934
958
  }
935
- parts.push(
959
+ chapters.push(
936
960
  chapterOf(
937
961
  5,
938
962
  view.showManual && manual ? `<div class="dsh-upd-manual"><div>\u624B\u5DE5\u515C\u5E95\u547D\u4EE4\uFF08\u590D\u5236\u6574\u884C\u6267\u884C\uFF09\uFF1A</div><code>${escapeHtml(manual)}</code></div>` : `<div class="dsh-upd-manual"><div class="dsh-upd-changelog-neutral">\u5F53\u524D\u6CA1\u6709\u53EF\u7528\u7684\u624B\u5DE5\u547D\u4EE4\uFF08\u8BA4\u4E0D\u51FA\u4F7F\u7528\u8303\u56F4\u6216\u5C5E\u6E90\u7801\u5B89\u88C5\u65F6\u4E0D\u7ED9\uFF09\u3002</div></div>`
939
963
  )
940
964
  );
965
+ parts.push('<div class="dsh-upd-body">' + chapters.join("\n") + "</div>");
941
966
  if (showActions && mode === "dialog") {
942
967
  parts.push(
943
968
  `<div class="dsh-upd-footer"><span class="dsh-upd-foot-note">\u5173\u95ED\u4E0D\u5F71\u54CD\u66F4\u65B0\uFF0C\u53EF\u968F\u65F6\u56DE\u6765\u67E5\u770B</span><button type="button" data-action="close-view" title="\u5173\u95ED\u7A97\u53E3\uFF0C\u66F4\u65B0\u4E0D\u53D7\u5F71\u54CD">\u5173\u95ED</button></div>`
@@ -1056,6 +1081,7 @@ function mountUpdatePanel(container, options) {
1056
1081
  }
1057
1082
  let theme = normalizePanelTheme(options.theme ?? "default");
1058
1083
  let showOthers = options.showOthers === true;
1084
+ let changelogCollapsed = false;
1059
1085
  const call = options.call;
1060
1086
  const onRestartRequested = options.onRestartRequested;
1061
1087
  const onCloseRequested = typeof options.onCloseRequested === "function" ? options.onCloseRequested : null;
@@ -1158,6 +1184,38 @@ function mountUpdatePanel(container, options) {
1158
1184
  atMs: latchNow()
1159
1185
  };
1160
1186
  }
1187
+ function thrownDetail(err) {
1188
+ try {
1189
+ const raw = err instanceof Error ? err.message : typeof err === "string" ? err : null;
1190
+ if (typeof raw !== "string") return null;
1191
+ const t = raw.trim();
1192
+ if (!t) return null;
1193
+ if (/^[a-z][a-z-]*$/.test(t) && t.length <= 32) return null;
1194
+ return redactForCopy(t) || null;
1195
+ } catch {
1196
+ return null;
1197
+ }
1198
+ }
1199
+ function userThrowLatch(source, err) {
1200
+ const env = currentEnvPair();
1201
+ return {
1202
+ code: source === "install" ? "install-failed" : "check-failed",
1203
+ kind: null,
1204
+ detail: thrownDetail(err),
1205
+ diag: null,
1206
+ requestId: source === "install" ? requestId : null,
1207
+ checkId: source === "install" ? receipt?.checkId ?? null : null,
1208
+ runningVersion: snapshot?.runningVersion ?? null,
1209
+ installedVersion: snapshot?.installedVersion ?? null,
1210
+ latestVersion: snapshot?.latestVersion ?? null,
1211
+ hostKind: env.hostKind,
1212
+ profileName: env.profileName,
1213
+ source,
1214
+ volatile: false,
1215
+ jobId: null,
1216
+ atMs: latchNow()
1217
+ };
1218
+ }
1161
1219
  function volatileLatch(source) {
1162
1220
  const env = currentEnvPair();
1163
1221
  return {
@@ -1244,6 +1302,7 @@ function mountUpdatePanel(container, options) {
1244
1302
  changelogMarkdown,
1245
1303
  mode,
1246
1304
  showOthers,
1305
+ changelogCollapsed,
1247
1306
  pluginId,
1248
1307
  copyNotice,
1249
1308
  theme,
@@ -1404,9 +1463,9 @@ function mountUpdatePanel(container, options) {
1404
1463
  if (!mounted) return;
1405
1464
  applyStatusReply(reply, "check");
1406
1465
  changelogFailedAt.clear();
1407
- } catch {
1466
+ } catch (err) {
1408
1467
  if (!mounted) return;
1409
- setLatch(volatileLatch("check"));
1468
+ setLatch(userThrowLatch("check", err));
1410
1469
  } finally {
1411
1470
  busyAct = null;
1412
1471
  if (copyNotice === "\u6B63\u5728\u67E5\u65B0\u7248\u2026") copyNotice = null;
@@ -1422,9 +1481,16 @@ function mountUpdatePanel(container, options) {
1422
1481
  render();
1423
1482
  try {
1424
1483
  if (!receipt) {
1425
- const checked = await call(phoneNames.updateCheck, queueArgs());
1426
- if (!mounted) return;
1427
- applyStatusReply(checked, "check");
1484
+ try {
1485
+ const checked = await call(phoneNames.updateCheck, queueArgs());
1486
+ if (!mounted) return;
1487
+ applyStatusReply(checked, "check");
1488
+ } catch (err) {
1489
+ if (!mounted) return;
1490
+ setLatch(userThrowLatch("check", err));
1491
+ render();
1492
+ return;
1493
+ }
1428
1494
  }
1429
1495
  if (!mounted) return;
1430
1496
  if (!receipt) {
@@ -1457,9 +1523,9 @@ function mountUpdatePanel(container, options) {
1457
1523
  });
1458
1524
  if (!mounted) return;
1459
1525
  applyStatusReply(reply, "install");
1460
- } catch {
1526
+ } catch (err) {
1461
1527
  if (!mounted) return;
1462
- setLatch(volatileLatch("install"));
1528
+ setLatch(userThrowLatch("install", err));
1463
1529
  } finally {
1464
1530
  busyAct = null;
1465
1531
  if (copyNotice === "\u6B63\u5728\u5B89\u88C5\u2026") copyNotice = null;
@@ -1567,6 +1633,11 @@ function mountUpdatePanel(container, options) {
1567
1633
  await refresh();
1568
1634
  return;
1569
1635
  }
1636
+ case "toggle-changelog": {
1637
+ changelogCollapsed = !changelogCollapsed;
1638
+ render();
1639
+ return;
1640
+ }
1570
1641
  // 「重启宿主」:宿主没有重启自己的电话,所以只做入口——
1571
1642
  // 调用方给了 onRestartRequested 就交给它;没给就如实说“请手动重启”,不假装。
1572
1643
  case "restart-hint": {
@@ -1607,6 +1678,10 @@ function mountUpdatePanel(container, options) {
1607
1678
  showOthers = show === true;
1608
1679
  await refresh();
1609
1680
  }
1681
+ async function setChangelogCollapsed(collapsed) {
1682
+ changelogCollapsed = collapsed === true;
1683
+ render();
1684
+ }
1610
1685
  function onClick(ev) {
1611
1686
  try {
1612
1687
  const t = ev;
@@ -1683,7 +1758,7 @@ function mountUpdatePanel(container, options) {
1683
1758
  } catch {
1684
1759
  }
1685
1760
  })();
1686
- return { refresh, act, setMode, setTheme, setShowOthers, setChangelogMarkdown, unmount };
1761
+ return { refresh, act, setMode, setTheme, setShowOthers, setChangelogCollapsed, setChangelogMarkdown, unmount };
1687
1762
  }
1688
1763
  import { buildPhoneNames as buildPhoneNames2 } from "./config.js";
1689
1764
  import { buildChangelogPhoneName as buildChangelogPhoneName2 } from "./config.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-update",
3
- "version": "0.5.5",
3
+ "version": "0.5.7",
4
4
  "description": "可复用的更新系统装成的 npm 包:任何 DSH 插件照文档能集成检查更新等功能(地图 #579)",
5
5
  "license": "MIT",
6
6
  "author": "王辰浩",
@@ -65,7 +65,9 @@
65
65
  "build": "node build.mjs",
66
66
  "prepack": "npm run build",
67
67
  "pretest": "npm run build",
68
- "test": "node --test tests/*.test.mjs"
68
+ "test": "node --test tests/*.test.mjs",
69
+ "gate:bilingual": "node scripts/bilingual-gate.mjs",
70
+ "gate:bilingual:release": "node scripts/bilingual-gate.mjs --release"
69
71
  },
70
72
  "devDependencies": {
71
73
  "esbuild": "^0.28.2",