dsh-plugin-update 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  格式为 Keep-a-Changelog 子集:`Added/Fixed/Changed` 必写,`Deprecated/Removed/Security` 有则透传,`Unreleased` 面板忽略,禁止 git-log 直倒。面板缺日志时中性提示,不挡安装。
4
4
 
5
+ ## [0.5.0] - 2026-10-05
6
+
7
+ ### Added
8
+ - 主题首选名 `archive`(档案卷纸面浅色):`d5-paper` 为旧别名仍可用,渲染逐字相同;入口件与批量面板同一套取值。
9
+ - 弹窗关闭落地 `onCloseRequested`:dialog 下点「关闭」/按 Esc 先交调用方撤 DOM 再停轮询;入口件打开的 dialog 已内置(收 dialog + 还原按钮 + 重查一次)。
10
+
11
+ ### Fixed
12
+ - 弹窗点「关闭」没反应:此前只停轮询不撤 DOM(入口件打开的 dialog 还会卡死),现走关闭落地。
13
+ - 悬停/聚焦闪烁:轮询每秒整树重写 innerHTML 打断 hover/focus,现输出逐字相同时不碰 DOM(状态变化仍即时重绘;单面板与批量面板同口径)。
14
+
5
15
  ## [0.4.0] - 2026-10-05
6
16
 
7
17
  ### Added
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  给 DSH 插件加「检查更新 / 安装更新」能力的 npm 包。宿主侧一段接线,面板侧挂一个现成组件,装不上时给用户一条可复制的手工命令。
4
4
 
5
- 要求 Node 22 或更高,零运行时依赖。当前版本 `0.4.0`。
5
+ 要求 Node 22 或更高,零运行时依赖。当前版本 `0.5.0`。
6
6
 
7
7
  装上它你会拿到六样东西:
8
8
 
@@ -69,7 +69,7 @@ createHostUpdate(
69
69
 
70
70
  本包是 ESM(`"type": "module"`):ESM 插件直接 `import`;CJS 插件用 `await import('dsh-plugin-update')`。
71
71
 
72
- ### 三个电话的入参与回参
72
+ ### 四个电话的入参与回参
73
73
 
74
74
  面板与宿主两侧共用这一份契约(`…` 是你的前缀):
75
75
 
@@ -128,6 +128,7 @@ const panel = mountUpdatePanel(document.getElementById('update-slot'), {
128
128
  call: (name, args) => host.call(name, args),
129
129
  })
130
130
  // 离开时 panel.unmount():只停轮询,安装在宿主侧继续跑;重开面板立刻重查,1 秒内恢复显示。
131
+ // 弹窗版想让「关闭」按钮真把弹窗撤掉:传 onCloseRequested(面板点关闭/Esc 时先调它撤 DOM,再停轮询;入口件打开的 dialog 已内置)。
131
132
  ```
132
133
 
133
134
  组件内部消化的事(调用者不再写):按 `panelPollMs` 轮询查状态(下限 250 毫秒);安装按钮状态跟随快照的 `canInstall`,不另写门控规则;装不了的原因按第 5.2 节展示中文一句话;`pending-restart` 单独横幅加重启指引,不再给安装按钮;手工命令展示与复制;排队位置展示与 `showOthers` 开关;跳过按版本记(“已跳过 X.Y.Z · 恢复”在同一行,不藏进设置页);失败时旁边的“复制诊断”一键给出脱敏后的自包含文本(稳定码、版本、宿主、队列位置),深挖仍看日志(组件里留着第 6 节第 11 条的过滤口径)。
@@ -141,7 +142,7 @@ mountUpdatePanel(slot, { pluginId: 'my-notes-plugin', prefix: 'notes', call: hos
141
142
 
142
143
  面板画成档案五章:**01 检查与安装、02 更新日志、03 更新队列、04 错误信息、05 手工命令**(五章恒在,缺内容给中性提示,不跳号)。档案头一行是「插件名 + 使用范围 + `profile` 牌」:使用范围由面板自动向宿主索取(电话入参 `includeEnv`,宿主只回**范围名与宿主种类**、不回任何路径),显示的就是更新要落到的那个 profile——`web` 与 `desktop` 各装一份,装错范围是严重故障,所以这一栏宁可显示“未知”也不猜。调用方知道得更准时可以用 `profileName` 显式覆盖。
143
144
 
144
- 可选专业主题(D5 档案卷,不替换默认):挂载时加 `theme: 'd5-paper'` 即换肤(右上大印章「待查/可装/安装中/待重启/受阻/已最新」+ 横幅小印章一字 + profile 牌 + 待重启衬线横幅配手绘 SVG 标 + 窄屏印章固定 + 省略号逐字折叠 + 浅深双主题跟随系统),内核 DOM 顺序不动、复制诊断常在;不传即最小可用默认样式。运行时用 `panel.setTheme('d5-paper' | 'default')` 可切。
145
+ 可选档案卷主题(纸面浅色案卷风,不替换默认):挂载时加 `theme: 'archive'` 即换肤(右上大印章「待查/可装/安装中/待重启/受阻/已最新」+ 横幅小印章一字 + profile 牌 + 待重启衬线横幅配手绘 SVG 标 + 窄屏印章固定 + 省略号逐字折叠 + 浅深双主题跟随系统),内核 DOM 顺序不动、复制诊断常在;不传即最小可用默认深色样式。运行时用 `panel.setTheme('archive' | 'default')` 可切。旧值 `theme: 'd5-paper'` 仍可用(同一套渲染)。入口件与批量面板是同一个 `theme` 参数,取值同一套。
145
146
 
146
147
  类型定义随包分发(`dsh-plugin-update/panel` 的 `.d.ts`),不用自编译;面板离线可读,与包版本绑定。
147
148
 
@@ -216,7 +217,7 @@ for (const [name, handler] of Object.entries(multi.handlers)) registry.set(name,
216
217
  ```
217
218
 
218
219
  五个批量电话(`<prefix>` 即上面的 `life`):`batchStatus` / `batchCheck` / `batchInstall` / `batchResume` / `batchCancel`;
219
- 每个目标的三个单插件电话照旧以**各自前缀**暴露(`ilife-bill.updateStatus` 等)。
220
+ 每个目标的四个单插件电话照旧以**各自前缀**暴露(`ilife-bill.updateStatus` 等)。
220
221
 
221
222
  回包形状(成功恰好四项,失败只有三项):
222
223
 
@@ -233,7 +234,7 @@ import { mountUpdateBatchPanel } from 'dsh-plugin-update/panel-batch'
233
234
  const panel = mountUpdateBatchPanel(el, {
234
235
  prefix: 'life',
235
236
  call: (name, args) => host.call(name, args),
236
- theme: 'd5-paper', // 与单插件面板同一套皮肤
237
+ theme: 'archive', // 与单插件面板同一套皮肤(archive = 档案卷,旧值 d5-paper 仍可用)
237
238
  })
238
239
  ```
239
240
 
@@ -301,7 +302,7 @@ const panel = mountUpdatePanel(document.getElementById('update-slot'), {
301
302
 
302
303
  宿主种类与安装出口都由本包自己探测和选择,**升级依赖即可,宿主侧与面板侧都不用改代码**:
303
304
 
304
- 1. 依赖版本提到 `^0.4.0`。
305
+ 1. 依赖版本提到 `^0.5.0`。
305
306
  2. 重新装 / 发一版你自己的插件,让新依赖进当前使用范围(运行时用的是 `node_modules` 里那份)。
306
307
  3. 如果你自己接过一版宿主安装出口,把它删掉——它会挡在本包的路由前面。
307
308
 
@@ -474,7 +475,7 @@ dsh plugin --profile my-web add --save-exact my-notes-plugin@1.2.3 --registry=ht
474
475
 
475
476
  照第 2 节把名字换成自己的之后,逐条打勾:
476
477
 
477
- 1. 宿主启动不报错,`update.phoneNames` 读到自家前缀的三个电话名,查状态返回六字段快照。
478
+ 1. 宿主启动不报错,`update.phoneNames` 读到自家前缀的四个电话名,查状态返回六字段快照。
478
479
  2. 面板按间隔轮询到快照,能展示第 5.2 节的原因文案;有新版时安装按钮可用,无新版与待重启时按钮状态正确。
479
480
  3. 模拟一次 `pending-restart` 能看到第 5.4 节的横幅,模拟一次自动装失败能看到第 5.3 节的命令可复制执行。
480
481
  4. 第二家同机隔离与串行:两家各传自己的插件标识与电话名前缀,电话名、落盘目录、锁文件逐个不同;安装执行跨插件串行——同范围撞上时后到者报 `update-busy`,凭队列位置(第 5.7 节)重试。
package/dist/entry.d.ts CHANGED
@@ -22,7 +22,8 @@ export interface UpdateEntryOptions {
22
22
  /** 调宿主电话:(phoneName, args) => Promise<reply>。 */
23
23
  call: (name: string, args: Record<string, unknown>) => Promise<unknown>;
24
24
  variant?: EntryVariant;
25
- theme?: 'default' | 'd5-paper';
25
+ /** 主题:`default` 最小可用深色,`archive` 档案卷纸面浅色(`d5-paper` 为旧别名仍可用)。 */
26
+ theme?: UpdatePanelTheme;
26
27
  /** 缺省 'mount':进页面静默查一次(只读)。'never' 则只在用户点击时查。 */
27
28
  autoCheck?: EntryAutoCheck;
28
29
  /** 缺省 'has-update':有新版才开面板,没有就只在原地给一句提示。 */
@@ -52,7 +53,7 @@ export interface UpdateEntryController {
52
53
  close(): void;
53
54
  /** 当前按钮上的状态文案(接入方做自定义排版时读它)。 */
54
55
  label(): string;
55
- setTheme(theme: 'default' | 'd5-paper'): void;
56
+ setTheme(theme: UpdatePanelTheme): void;
56
57
  unmount(): void;
57
58
  }
58
59
  /**
package/dist/entry.js CHANGED
@@ -2,7 +2,8 @@
2
2
  import { DEFAULT_PREFIX, MIN_PANEL_POLL_MS, buildPhoneNames } from "./config.js";
3
3
  import {
4
4
  failureCodeOf,
5
- mountUpdatePanel
5
+ mountUpdatePanel,
6
+ normalizePanelTheme
6
7
  } from "./panel.js";
7
8
  const LABEL_IDLE = "\u68C0\u67E5\u66F4\u65B0";
8
9
  const LABEL_FAILED = "\u66F4\u65B0\u5931\u8D25\uFF0C\u70B9\u6B64\u67E5\u770B";
@@ -113,10 +114,10 @@ function mountUpdateEntry(container, options) {
113
114
  if (openOn !== "has-update" && openOn !== "always" && openOn !== "manual") {
114
115
  throw new Error(`[dsh-plugin-update] \u70B9\u51FB\u53BB\u5411\u975E\u6CD5\uFF1A\u53EA\u6536 has-update / always / manual\uFF08\u6536\u5230 ${JSON.stringify(options.openOn)}\uFF09`);
115
116
  }
116
- let theme = options.theme ?? "default";
117
- if (theme !== "default" && theme !== "d5-paper") {
118
- throw new Error(`[dsh-plugin-update] \u4E3B\u9898\u975E\u6CD5\uFF1A\u53EA\u6536 default \u6216 d5-paper\uFF08\u6536\u5230 ${JSON.stringify(options.theme)}\uFF09`);
117
+ if (options.theme !== void 0 && options.theme !== "default" && options.theme !== "archive" && options.theme !== "d5-paper") {
118
+ 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`);
119
119
  }
120
+ let theme = normalizePanelTheme(options.theme ?? "default");
120
121
  const pollMs = options.pollMs;
121
122
  if (pollMs !== void 0 && (typeof pollMs !== "number" || !Number.isFinite(pollMs) || pollMs < MIN_PANEL_POLL_MS)) {
122
123
  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`);
@@ -219,6 +220,8 @@ function mountUpdateEntry(container, options) {
219
220
  profileName,
220
221
  autoChangelog: options.autoChangelog,
221
222
  changelogMarkdown: options.changelogMarkdown ?? null,
223
+ // 面板点「关闭」即走入口件的完整关闭(收 dialog + 还原按钮 + 重查一次),不再是面板自己停轮询。
224
+ onCloseRequested: () => close(),
222
225
  call: panelCall
223
226
  });
224
227
  panelMode = mode;
@@ -268,11 +271,11 @@ function mountUpdateEntry(container, options) {
268
271
  render();
269
272
  }
270
273
  function setTheme(next) {
271
- if (next !== "default" && next !== "d5-paper") {
272
- throw new Error(`[dsh-plugin-update] \u4E3B\u9898\u975E\u6CD5\uFF1A\u53EA\u6536 default \u6216 d5-paper\uFF08\u6536\u5230 ${JSON.stringify(next)}\uFF09`);
274
+ if (next !== "default" && next !== "archive" && next !== "d5-paper") {
275
+ 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`);
273
276
  }
274
- theme = next;
275
- if (panel) void panel.setTheme(next);
277
+ theme = normalizePanelTheme(next);
278
+ if (panel) void panel.setTheme(theme);
276
279
  else render();
277
280
  }
278
281
  function onClick(ev) {
@@ -12,6 +12,7 @@ import {
12
12
  createBrowserSkipStore,
13
13
  failureCopy,
14
14
  isKnownFailureCode,
15
+ normalizePanelTheme,
15
16
  renderUpdatePanelHTML
16
17
  } from "./panel.js";
17
18
  const BATCH_PANEL_POLL = {
@@ -92,7 +93,7 @@ function batchRowStatus(row, skippedVersion) {
92
93
  }
93
94
  function renderBatchPanelHTML(input) {
94
95
  const rows = Array.isArray(input.rows) ? input.rows : [];
95
- const theme = input.theme === "d5-paper" ? "d5-paper" : "default";
96
+ const theme = normalizePanelTheme(input.theme);
96
97
  const mode = input.mode === "dialog" ? "dialog" : "embedded";
97
98
  const titles = input.titles && typeof input.titles === "object" ? input.titles : {};
98
99
  const skippedVersions = input.skippedVersions && typeof input.skippedVersions === "object" ? input.skippedVersions : {};
@@ -167,7 +168,7 @@ function renderBatchPanelHTML(input) {
167
168
  parts.push('<div class="dsh-upd-batch-notice" role="status">' + escapeHtml(notice) + "</div>");
168
169
  }
169
170
  const kernel = parts.join(String.fromCharCode(10));
170
- const d5 = theme === "d5-paper";
171
+ const d5 = normalizePanelTheme(theme) === "d5-paper";
171
172
  const attr = d5 ? ' data-theme="d5-paper"' : "";
172
173
  const seal = batchSealOf(counts);
173
174
  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>";
@@ -618,10 +619,10 @@ function mountUpdateBatchPanel(container, options) {
618
619
  if (mode !== "embedded" && mode !== "dialog") {
619
620
  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");
620
621
  }
621
- let theme = options.theme === void 0 ? "default" : options.theme;
622
- if (theme !== "default" && theme !== "d5-paper") {
623
- throw new Error("[dsh-plugin-update] \u4E3B\u9898\u975E\u6CD5\uFF1A\u53EA\u6536 default \u6216 d5-paper\uFF08\u6536\u5230 " + JSON.stringify(options.theme) + "\uFF09");
622
+ if (options.theme !== void 0 && options.theme !== "default" && options.theme !== "archive" && options.theme !== "d5-paper") {
623
+ 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");
624
624
  }
625
+ let theme = normalizePanelTheme(options.theme ?? "default");
625
626
  const call = options.call;
626
627
  const titles = options.titles && typeof options.titles === "object" ? options.titles : {};
627
628
  const onRestartRequested = options.onRestartRequested;
@@ -694,9 +695,10 @@ function mountUpdateBatchPanel(container, options) {
694
695
  }
695
696
  );
696
697
  }
698
+ let lastHTML = "";
697
699
  function render() {
698
700
  if (!mounted) return;
699
- container.innerHTML = renderBatchPanelHTML({
701
+ const nextHTML = renderBatchPanelHTML({
700
702
  rows,
701
703
  theme,
702
704
  mode,
@@ -711,6 +713,10 @@ function mountUpdateBatchPanel(container, options) {
711
713
  confirmCancel: confirmCancelArmed,
712
714
  changelogs: changelogView()
713
715
  });
716
+ if (nextHTML !== lastHTML) {
717
+ lastHTML = nextHTML;
718
+ container.innerHTML = nextHTML;
719
+ }
714
720
  }
715
721
  function rowOfKey(key) {
716
722
  return rows.find((row) => row.key === key) ?? null;
package/dist/panel.d.ts CHANGED
@@ -3,7 +3,16 @@ import type { BlockedReason, UpdateSnapshot } from './ports.js';
3
3
  /** 摆放形态:默认内嵌,切弹窗走同一参数。 */
4
4
  export type UpdatePanelMode = 'embedded' | 'dialog';
5
5
  /** 面板主题:默认最小可用样式;`d5-paper` 为可选 D5 档案卷专业主题(只换肤,不换 DOM 顺序)。 */
6
- export type UpdatePanelTheme = 'default' | 'd5-paper';
6
+ /**
7
+ * 面板主题:`default` 最小可用深色;`archive` 档案卷纸面浅色(原型敲定的案卷风格,只换肤)。
8
+ * `d5-paper` 是 archive 的旧别名(历史取值),仍可用,渲染逐字相同。
9
+ */
10
+ export type UpdatePanelTheme = 'default' | 'archive' | 'd5-paper';
11
+ /**
12
+ * 主题归一:archive 与旧别名 d5-paper 走同一套渲染(DOM 属性仍为 d5-paper,既有覆盖样式不断);
13
+ * 其余一律回 default(纯渲染函数永不抛;挂载/setTheme 的非法值另行抛错)。
14
+ */
15
+ export declare function normalizePanelTheme(value: unknown): 'default' | 'd5-paper';
7
16
  /** 与宿主通话的传输函数:面板只认这个签名,不认任何宿主对象的具体形状。 */
8
17
  export type UpdatePanelCall = (phoneName: string, args: Record<string, unknown>) => Promise<Record<string, unknown>>;
9
18
  /** 复制文本的出口:不传即尝试浏览器剪贴板,都没有则只留“已生成、请手动复制”提示。 */
@@ -52,6 +61,11 @@ export interface UpdatePanelOptions {
52
61
  * 回来经内部通道换日志节;显式传过 changelogMarkdown 或传 false 即退回手动模式。
53
62
  */
54
63
  autoChangelog?: boolean;
64
+ /**
65
+ * 弹窗关闭的落地:只在 dialog 下点「关闭」/按 Esc 时调用;调用方在此撤掉弹窗 DOM。
66
+ * 不传则回退为只停轮询(unmount),DOM 留给调用方处理。入口件打开的 dialog 已内置(收 dialog + 还原按钮)。
67
+ */
68
+ onCloseRequested?: () => void | Promise<void>;
55
69
  }
56
70
  /** 挂载点:只要有 innerHTML 的容器即可(浏览器元素或测试替身都行)。 */
57
71
  export interface UpdatePanelContainer {
package/dist/panel.js CHANGED
@@ -7,6 +7,9 @@ import {
7
7
  parseChangelog,
8
8
  renderChangelogHTML
9
9
  } from "./changelog.js";
10
+ function normalizePanelTheme(value) {
11
+ return value === "d5-paper" || value === "archive" ? "d5-paper" : "default";
12
+ }
10
13
  const BLOCKED_COPY = {
11
14
  "unknown-profile": {
12
15
  title: "\u4F7F\u7528\u8303\u56F4\u6216\u63D2\u4EF6\u4F4D\u7F6E\u8BA4\u4E0D\u51FA",
@@ -863,7 +866,7 @@ ${changelogHTML}
863
866
  function renderUpdatePanelHTML(input) {
864
867
  const view = panelViewModel(input);
865
868
  const kernel = renderUpdatePanelKernel(input, view);
866
- const d5 = input.theme === "d5-paper";
869
+ const d5 = normalizePanelTheme(input.theme) === "d5-paper";
867
870
  const attr = d5 ? ' data-theme="d5-paper"' : "";
868
871
  const sealAttr = ` data-seal="${escapeHtml(view.seal.text)}" data-seal-tone="${view.seal.tone}"`;
869
872
  const body = input.mode === "dialog" ? `<div class="dsh-upd-overlay" data-mode="dialog"><div class="dsh-upd" data-mode="dialog" data-plugin="${escapeHtml(input.pluginId)}"${sealAttr}${attr}>
@@ -958,13 +961,15 @@ function mountUpdatePanel(container, options) {
958
961
  if (mode !== "embedded" && mode !== "dialog") {
959
962
  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`);
960
963
  }
961
- let theme = options.theme ?? "default";
962
- if (theme !== "default" && theme !== "d5-paper") {
963
- throw new Error(`[dsh-plugin-update] \u4E3B\u9898\u975E\u6CD5\uFF1A\u53EA\u6536 default \u6216 d5-paper\uFF08\u6536\u5230 ${JSON.stringify(options.theme)}\uFF09`);
964
+ if (options.theme !== void 0 && options.theme !== "default" && options.theme !== "archive" && options.theme !== "d5-paper") {
965
+ 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`);
964
966
  }
967
+ let theme = normalizePanelTheme(options.theme ?? "default");
965
968
  let showOthers = options.showOthers === true;
966
969
  const call = options.call;
967
970
  const onRestartRequested = options.onRestartRequested;
971
+ const onCloseRequested = typeof options.onCloseRequested === "function" ? options.onCloseRequested : null;
972
+ let lastHTML = "";
968
973
  const copyText = options.copyText ?? defaultCopyText;
969
974
  const skipStore = options.skipStore ?? createBrowserSkipStore(pluginId);
970
975
  const hostKind = typeof options.hostKind === "string" && options.hostKind ? options.hostKind : null;
@@ -1005,7 +1010,7 @@ function mountUpdatePanel(container, options) {
1005
1010
  }
1006
1011
  const latest = snapshot?.latestVersion ?? null;
1007
1012
  const skippedLatest = !!latest && validReleaseVersion(latest) && skipStore.has(latest);
1008
- container.innerHTML = renderUpdatePanelHTML({
1013
+ const nextHTML = renderUpdatePanelHTML({
1009
1014
  snapshot,
1010
1015
  manual,
1011
1016
  queue,
@@ -1023,6 +1028,10 @@ function mountUpdatePanel(container, options) {
1023
1028
  profileName: profileNameOption ?? envProfileName,
1024
1029
  hostKind: hostKind ?? envHostKind
1025
1030
  });
1031
+ if (nextHTML !== lastHTML) {
1032
+ lastHTML = nextHTML;
1033
+ container.innerHTML = nextHTML;
1034
+ }
1026
1035
  }
1027
1036
  function applyStatusReply(reply) {
1028
1037
  if (!isObject(reply)) return;
@@ -1256,7 +1265,7 @@ function mountUpdatePanel(container, options) {
1256
1265
  return;
1257
1266
  }
1258
1267
  case "close-view": {
1259
- if (mode === "dialog") unmount();
1268
+ await requestDialogClose();
1260
1269
  return;
1261
1270
  }
1262
1271
  }
@@ -1269,10 +1278,10 @@ function mountUpdatePanel(container, options) {
1269
1278
  render();
1270
1279
  }
1271
1280
  async function setTheme(next) {
1272
- if (next !== "default" && next !== "d5-paper") {
1273
- throw new Error(`[dsh-plugin-update] \u4E3B\u9898\u975E\u6CD5\uFF1A\u53EA\u6536 default \u6216 d5-paper\uFF08\u6536\u5230 ${JSON.stringify(next)}\uFF09`);
1281
+ if (next !== "default" && next !== "archive" && next !== "d5-paper") {
1282
+ 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`);
1274
1283
  }
1275
- theme = next;
1284
+ theme = normalizePanelTheme(next);
1276
1285
  render();
1277
1286
  }
1278
1287
  async function setShowOthers(show) {
@@ -1289,11 +1298,21 @@ function mountUpdatePanel(container, options) {
1289
1298
  } catch {
1290
1299
  }
1291
1300
  }
1301
+ async function requestDialogClose() {
1302
+ if (!mounted || mode !== "dialog") return;
1303
+ if (onCloseRequested) {
1304
+ try {
1305
+ await onCloseRequested();
1306
+ } catch {
1307
+ }
1308
+ }
1309
+ unmount();
1310
+ }
1292
1311
  function onKeyDown(ev) {
1293
1312
  try {
1294
1313
  const e = ev;
1295
1314
  if (!mounted || mode !== "dialog" || !e || e.key !== "Escape") return;
1296
- unmount();
1315
+ void requestDialogClose();
1297
1316
  } catch {
1298
1317
  }
1299
1318
  }
@@ -1409,6 +1428,7 @@ export {
1409
1428
  isQueueBusy,
1410
1429
  isUnreleasedVersion,
1411
1430
  mountUpdatePanel,
1431
+ normalizePanelTheme,
1412
1432
  normalizeQueueState,
1413
1433
  panelViewModel,
1414
1434
  parseChangelog2 as parseChangelog,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-update",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "可复用的更新系统装成的 npm 包:任何 DSH 插件照文档能集成检查更新等功能(地图 #579)",
5
5
  "license": "MIT",
6
6
  "author": "王辰浩",