dsh-plugin-update 0.5.4 → 0.5.6
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 +17 -0
- package/README.md +1 -1
- package/dist/bilingual.d.ts +39 -0
- package/dist/bilingual.js +58 -0
- package/dist/entry-batch.js +1 -1
- package/dist/entry.d.ts +12 -0
- package/dist/entry.js +50 -13
- package/dist/host.js +5 -4
- package/dist/log-events.d.ts +10 -0
- package/dist/log-events.js +9 -0
- package/dist/panel-batch.js +18 -16
- package/dist/panel.d.ts +27 -4
- package/dist/panel.js +454 -145
- package/dist/store.js +2 -1
- package/package.json +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
格式为 Keep-a-Changelog 子集:`Added/Fixed/Changed` 必写,`Deprecated/Removed/Security` 有则透传,`Unreleased` 面板忽略,禁止 git-log 直倒。面板缺日志时中性提示,不挡安装。
|
|
4
4
|
|
|
5
|
+
## [0.5.6] - 2026-10-06
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- 弹窗分栏滚动、更新日志折叠与动效体系;双语防倒退门禁四道检查(文案源/快照/契约/零 draft)与自测(#56)。
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- 用户路径抛错锁存非瞬态化(#58):查/装动作的传输抛错改为非瞬态锁存(安装抛错码为 install-failed,不再冒充查失败),轮询不再洗掉;抛错原文脱敏后进复制摘要;安装内补查失败精确归因到查阶段;轮询抛错仍瞬态自愈。
|
|
12
|
+
|
|
13
|
+
## [0.5.5] - 2026-10-06
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- 档案卷主题收敛 archive 与面板紧凑化:运行时 data-theme 统一为 archive(d5-paper 仅作输入旧别名归一),entry/entry-batch/panel-batch 输出与 CSS 选择器同步,panel-batch setTheme 补 archive 并归一存储;根/卷头/横幅/版本条/章节/章头/队列行/footer 间距收紧;删 changelog-wrap 与章节线重影内边框;页脚去技术词,批量关闭 title 同步。
|
|
17
|
+
- 双语底座 #54(先行):bilingual 字典模块、entry key 映射链、noteVersion 运行时值;research/52-inventory 双语盘点与原型矩阵随附。
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
- 安装/查失败常驻与复制诊断锁定(#58):失败横幅保留到下一次用户主动查/装的新结论、任务态或版本变化,只读轮询不再洗回可装页;「复制诊断」锁定致命那次回包(稳定码、摘要、版本、宿主、请求与检查编号冻结在失败时刻);失败旁新增「知道了」可显式确认回到可装页;健康无失败时复制改为当前状态快照,不再伪造失败码;04 章失败时变为卷宗(真实稳定码、本次查询键与失败时刻、证据冻结声明、凭编号对日志的现成查询,平时为路牌+中性行,可用 `showLogHint: false` 藏路牌);日志事件名收归 `log-events.ts` 单源(宿主发射与面板路牌同源);对抗审查补强:瞬态读失败不跨重挂、查锁存不再错配旧安装编号、瞬态失败换诚实文案、跨挂载表有界 100 家;`update-busy` 仍为瞬态不锁存;四电话形状与快照六字段零变更。
|
|
21
|
+
|
|
5
22
|
## [0.5.4] - 2026-10-06
|
|
6
23
|
|
|
7
24
|
### Added
|
package/README.md
CHANGED
|
@@ -131,7 +131,7 @@ const panel = mountUpdatePanel(document.getElementById('update-slot'), {
|
|
|
131
131
|
// 弹窗版想让「关闭」按钮真把弹窗撤掉:传 onCloseRequested(面板点关闭/Esc 时先调它撤 DOM,再停轮询;入口件打开的 dialog 已内置)。
|
|
132
132
|
```
|
|
133
133
|
|
|
134
|
-
组件内部消化的事(调用者不再写):按 `panelPollMs` 轮询查状态(下限 250 毫秒);安装按钮状态跟随快照的 `canInstall`,不另写门控规则;装不了的原因按第 5.2 节展示中文一句话;`pending-restart` 单独横幅加重启指引,不再给安装按钮;手工命令展示与复制;排队位置展示与 `showOthers` 开关;跳过按版本记(“已跳过 X.Y.Z · 恢复”在同一行,不藏进设置页);失败时旁边的“复制诊断”一键给出脱敏后的自包含文本(稳定码、版本、宿主、队列位置),深挖仍看日志(组件里留着第 6 节第 11
|
|
134
|
+
组件内部消化的事(调用者不再写):按 `panelPollMs` 轮询查状态(下限 250 毫秒);安装按钮状态跟随快照的 `canInstall`,不另写门控规则;装不了的原因按第 5.2 节展示中文一句话;`pending-restart` 单独横幅加重启指引,不再给安装按钮;手工命令展示与复制;排队位置展示与 `showOthers` 开关;跳过按版本记(“已跳过 X.Y.Z · 恢复”在同一行,不藏进设置页);失败时旁边的“复制诊断”一键给出脱敏后的自包含文本(稳定码、版本、宿主、队列位置),深挖仍看日志(组件里留着第 6 节第 11 条的过滤口径)。失败横幅常驻:查/装失败后保留到下一次用户主动查/装的新结论、任务态或版本变化,只读轮询不再洗掉;复制诊断锁定致命那次回包的证据(码、摘要、版本、宿主、编号冻结在失败时刻);失败旁有「知道了」可显式确认回到可装页(只清面板提示,不调电话)。无失败时复制诊断给当前状态快照(版本、宿主、范围、队列),不伪造失败码。04 章平时是路牌(中性行+日志过滤口径,可用 `showLogHint: false` 隐藏);失败时变为卷宗:真实稳定码、本次查询键(请求/检查编号+失败时刻)、证据冻结声明、凭编号对日志的现成查询。日志事件名(`host.call`/`host.call.fail`/`update.install.exec`)由宿主与面板共用单源,改名即全改。
|
|
135
135
|
|
|
136
136
|
待重启横幅上有个「重启宿主」入口。宿主没有“重启自己”的电话,所以默认点击只如实提示「请手动重启宿主」;想把重启流程接进来就传 `onRestartRequested`(面板调它,成败都回执):
|
|
137
137
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** 字典版本钉(#53 Q3 解耦):key 冻即解阻塞 #54,文案按词标 draft/locked;开发钉此版本,转正只换文案不换 key。 */
|
|
2
|
+
export declare const BILINGUAL_DICT_VERSION = "2026-10-06-pin53";
|
|
3
|
+
/** 首批 key 全集(entry 7 键,命名沿 #52 §1 key 提案,area.group.name)。 */
|
|
4
|
+
export type BilingualKey = 'entry.label.idle' | 'entry.label.failed' | 'entry.label.busy' | 'entry.label.restart' | 'entry.label.has-update' | 'entry.action.checking' | 'entry.note.up-to-date';
|
|
5
|
+
/** 一条双语:en/zh 模板 + 成熟度。模板内具名槽如 {version},运行时值填入(值永不翻译)。 */
|
|
6
|
+
export interface BilingualEntry {
|
|
7
|
+
en: string;
|
|
8
|
+
zh: string;
|
|
9
|
+
/** true=待母语+域内双签(#53 Q3),false=已双签转正。首批全 draft(无评审不假锁)。 */
|
|
10
|
+
draft: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 集中字典(机器源,类型锁死:Record<BilingualKey, BilingualEntry> 缺键即 tsc 报错)。
|
|
14
|
+
* 人读源为 #57 规格 + #53 结论英文定调;英文终稿待母语评审,故全标 draft。
|
|
15
|
+
* zh 逐字等于 entry.ts 现有散落文案(兼容既有测试与接入方 label() 口径)。
|
|
16
|
+
*/
|
|
17
|
+
export declare const BILINGUAL_STRINGS: Record<BilingualKey, BilingualEntry>;
|
|
18
|
+
/** 取一条双语原文(类型锁死:未知 key 在 tsc 即拦;运行时未知亦抛,不静默回退)。 */
|
|
19
|
+
export declare function bilingualEntry(key: BilingualKey): BilingualEntry;
|
|
20
|
+
/**
|
|
21
|
+
* 纯文本双语(一行,英文在前中文在后,空白分隔):用于 title / aria-label 等放不下 HTML 的属性位。
|
|
22
|
+
* 返回原文(未转义),调用方按上下文转义后再拼入属性。
|
|
23
|
+
*/
|
|
24
|
+
export declare function bilingualText(key: BilingualKey, values?: Record<string, unknown>): string;
|
|
25
|
+
/**
|
|
26
|
+
* 双语语义块 HTML(唯一渲染出口):英文在前中文在后,空白分隔,lang 属性齐全。
|
|
27
|
+
* 返回已转义可直接拼入 innerHTML 的片段;调用方不得再对整体转义,不得拆 span 重组。
|
|
28
|
+
*/
|
|
29
|
+
export declare function bilingualHTML(key: BilingualKey, values?: Record<string, unknown>): string;
|
|
30
|
+
/** 是否 draft(供 #56 门禁读零 draft 放行条件;#54 自身不断言零 draft)。 */
|
|
31
|
+
export declare function isDraft(key: BilingualKey): boolean;
|
|
32
|
+
/** 全表 draft key 一览(#56 门禁:空数组才放行合入主分支)。 */
|
|
33
|
+
export declare function draftKeys(): BilingualKey[];
|
|
34
|
+
/**
|
|
35
|
+
* 双语块最小样式(与 UPDATE_ENTRY_CSS 同缝注入):
|
|
36
|
+
* 平时行内双语(英文前中文后,空白分隔),窄处竖排不断行(@media 360px 以下 column)。
|
|
37
|
+
* 只管排版,不管字重字号(字重间距留给 #55 视觉矩阵验收)。
|
|
38
|
+
*/
|
|
39
|
+
export declare const BILINGUAL_CSS: string;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/bilingual.ts
|
|
2
|
+
const BILINGUAL_DICT_VERSION = "2026-10-06-pin53";
|
|
3
|
+
const BILINGUAL_STRINGS = {
|
|
4
|
+
"entry.label.idle": { en: "Check for updates", zh: "\u68C0\u67E5\u66F4\u65B0", draft: true },
|
|
5
|
+
"entry.label.failed": { en: "Update failed \u2014 View details", zh: "\u66F4\u65B0\u5931\u8D25\uFF0C\u70B9\u6B64\u67E5\u770B", draft: true },
|
|
6
|
+
"entry.label.busy": { en: "Installing\u2026", zh: "\u6B63\u5728\u5B89\u88C5\u2026", draft: true },
|
|
7
|
+
"entry.label.restart": { en: "Restart required", zh: "\u5F85\u91CD\u542F", draft: true },
|
|
8
|
+
"entry.label.has-update": { en: "Update available {version}", zh: "\u6709\u65B0\u7248 {version}", draft: true },
|
|
9
|
+
"entry.action.checking": { en: "Checking for updates\u2026", zh: "\u6B63\u5728\u67E5\u65B0\u7248\u2026", draft: true },
|
|
10
|
+
"entry.note.up-to-date": { en: "Up to date {version}", zh: "\u5DF2\u662F\u6700\u65B0 {version}", draft: true }
|
|
11
|
+
};
|
|
12
|
+
function escapeHtml(value) {
|
|
13
|
+
return String(value ?? "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
14
|
+
}
|
|
15
|
+
function formatTemplate(template, values) {
|
|
16
|
+
const vals = values ?? {};
|
|
17
|
+
return String(template).replace(/\{([A-Za-z0-9_]+)\}/g, (_, name) => {
|
|
18
|
+
const v = vals[name];
|
|
19
|
+
if (v === null || v === void 0) return "";
|
|
20
|
+
return String(v).trim();
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function bilingualEntry(key) {
|
|
24
|
+
const hit = BILINGUAL_STRINGS[key];
|
|
25
|
+
if (!hit) throw new Error("[dsh-plugin-update] \u672A\u77E5\u6587\u6848 key\uFF1A" + String(key));
|
|
26
|
+
return hit;
|
|
27
|
+
}
|
|
28
|
+
function bilingualText(key, values) {
|
|
29
|
+
const e = bilingualEntry(key);
|
|
30
|
+
return formatTemplate(e.en, values) + " " + formatTemplate(e.zh, values);
|
|
31
|
+
}
|
|
32
|
+
function bilingualHTML(key, values) {
|
|
33
|
+
const e = bilingualEntry(key);
|
|
34
|
+
const en = escapeHtml(formatTemplate(e.en, values));
|
|
35
|
+
const zh = escapeHtml(formatTemplate(e.zh, values));
|
|
36
|
+
return '<span class="dsh-upd-bi"><span lang="en">' + en + '</span> <span lang="zh">' + zh + "</span></span>";
|
|
37
|
+
}
|
|
38
|
+
function isDraft(key) {
|
|
39
|
+
return bilingualEntry(key).draft;
|
|
40
|
+
}
|
|
41
|
+
function draftKeys() {
|
|
42
|
+
return Object.keys(BILINGUAL_STRINGS).filter((k) => BILINGUAL_STRINGS[k].draft);
|
|
43
|
+
}
|
|
44
|
+
const BILINGUAL_CSS = [
|
|
45
|
+
".dsh-upd-bi{display:inline-flex;align-items:baseline;gap:.5em;max-width:100%}",
|
|
46
|
+
'.dsh-upd-bi [lang="en"],.dsh-upd-bi [lang="zh"]{overflow-wrap:anywhere}',
|
|
47
|
+
"@media (max-width:360px){.dsh-upd-bi{flex-direction:column;gap:0}}"
|
|
48
|
+
].join("\n");
|
|
49
|
+
export {
|
|
50
|
+
BILINGUAL_CSS,
|
|
51
|
+
BILINGUAL_DICT_VERSION,
|
|
52
|
+
BILINGUAL_STRINGS,
|
|
53
|
+
bilingualEntry,
|
|
54
|
+
bilingualHTML,
|
|
55
|
+
bilingualText,
|
|
56
|
+
draftKeys,
|
|
57
|
+
isDraft
|
|
58
|
+
};
|
package/dist/entry-batch.js
CHANGED
|
@@ -166,7 +166,7 @@ function mountUpdateBatchEntry(container, options) {
|
|
|
166
166
|
const summary = summaryOf();
|
|
167
167
|
const text = activating ? "\u6B63\u5728\u67E5\u65B0\u7248\u2026" : currentLabel();
|
|
168
168
|
const busyAttr = activating ? ' disabled aria-busy="true"' : "";
|
|
169
|
-
const themeAttr = theme === "
|
|
169
|
+
const themeAttr = theme === "archive" ? ' data-theme="archive"' : "";
|
|
170
170
|
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>";
|
|
171
171
|
const noteHTML = note ? '<span class="dsh-upd-entry-note" data-dsh-upd-note="1">' + escapeHtml(note) + "</span>" : "";
|
|
172
172
|
return "<style>" + UPDATE_ENTRY_CSS + '</style>\n<span class="dsh-upd-entry" data-variant="' + variant + '" data-state="' + summary.kind + '"' + themeAttr + ">" + control + noteHTML + "</span>";
|
package/dist/entry.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type UpdatePanelContainer, type UpdatePanelTheme } from './panel.js';
|
|
2
2
|
import type { UpdateSnapshot } from './ports.js';
|
|
3
|
+
import { type BilingualKey } from './bilingual.js';
|
|
3
4
|
/** 入口件的形态。 */
|
|
4
5
|
export type EntryVariant = 'button' | 'badge' | 'inline';
|
|
5
6
|
/** 什么时候主动查一次(只读,联网一次)。 */
|
|
@@ -65,6 +66,17 @@ export interface UpdateEntryController {
|
|
|
65
66
|
* 待重启是正常终态(不是失败),所以排在失败前面;正在装的任务比一条陈旧的失败码更可信。
|
|
66
67
|
*/
|
|
67
68
|
export declare function entryStateKind(state: UpdateEntryState | null | undefined): EntryStateKind;
|
|
69
|
+
/**
|
|
70
|
+
* 入口件双语 key(#54 底座调用链):与 entryStateKind 同一输入(快照 + 失败码),分支只认稳定码,永不读文案。
|
|
71
|
+
* 数据层只传 key + {version} 运行时值,表现层经 bilingualHTML 拼语义块。
|
|
72
|
+
*/
|
|
73
|
+
export declare function entryBilingualKeyFor(state: UpdateEntryState | null | undefined): BilingualKey;
|
|
74
|
+
/** 双语插值(运行时值永不翻译:同一 version 在中英两 span 各出现一次)。 */
|
|
75
|
+
export declare function entryBilingualValuesFor(state: UpdateEntryState | null | undefined): Record<string, string>;
|
|
76
|
+
/** 一路调用链的语义块 HTML(entryHTML 按钮正文唯一来源,无覆盖时必走此函数)。 */
|
|
77
|
+
export declare function entryBilingualHTMLFor(state: UpdateEntryState | null | undefined): string;
|
|
78
|
+
/** 属性位纯文本双语(title / aria-label 用,放不下 HTML 时的同内容平面版)。 */
|
|
79
|
+
export declare function entryBilingualTextFor(state: UpdateEntryState | null | undefined): string;
|
|
68
80
|
/** 入口件文案(唯一出处:测试与接入方都读它,不各写一份)。 */
|
|
69
81
|
export declare function entryLabelFor(state: UpdateEntryState | null | undefined): string;
|
|
70
82
|
export declare const UPDATE_ENTRY_CSS: string;
|
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="
|
|
70
|
-
'.dsh-upd-entry[data-theme="
|
|
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="
|
|
74
|
-
'.dsh-upd-entry[data-theme="
|
|
75
|
-
'@media (prefers-color-scheme: dark){.dsh-upd-entry[data-theme="
|
|
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
|
|
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
|
|
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 === "
|
|
176
|
-
const control = variant === "badge" ? `<button type="button" class="dsh-upd-entry-dot" ${ENTRY_ATTR}="activate" title="${escapeHtml(
|
|
177
|
-
const noteHTML =
|
|
178
|
-
return `<style>${UPDATE_ENTRY_CSS}
|
|
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
|
-
|
|
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
|
-
|
|
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",
|
|
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",
|
|
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",
|
|
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",
|
|
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
|
+
};
|
package/dist/panel-batch.js
CHANGED
|
@@ -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\
|
|
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) === "
|
|
173
|
-
const attr = d5 ? ' data-theme="
|
|
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;
|
|
@@ -383,21 +383,23 @@ 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));
|
|
389
391
|
const UPDATE_BATCH_PANEL_D5_CSS = [
|
|
390
392
|
"/* \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="
|
|
392
|
-
'.dsh-upd[data-theme="
|
|
393
|
-
'.dsh-upd[data-theme="
|
|
394
|
-
'.dsh-upd[data-theme="
|
|
395
|
-
'.dsh-upd[data-theme="
|
|
396
|
-
'.dsh-upd[data-theme="
|
|
397
|
-
'.dsh-upd[data-theme="
|
|
398
|
-
'.dsh-upd[data-theme="
|
|
399
|
-
'.dsh-upd[data-theme="
|
|
400
|
-
'.dsh-upd[data-theme="
|
|
393
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-batch-head{padding-right:120px}',
|
|
394
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-batch-title{font-family:var(--d5-serif);font-size:21px;letter-spacing:.04em}',
|
|
395
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-batch-title i{color:var(--d5-accent)}',
|
|
396
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-brow-main{background:transparent;border:0;color:var(--d5-ink)}',
|
|
397
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-brow-main:hover{color:var(--d5-accent)}',
|
|
398
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-bname{font-family:var(--d5-serif)}',
|
|
399
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-bver{font-family:var(--d5-mono);color:var(--d5-muted)}',
|
|
400
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-batch-ledger{color:var(--d5-muted)}',
|
|
401
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-batch-sum{color:var(--d5-ink)}',
|
|
402
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-brow-set{border-top-color:var(--d5-line)}'
|
|
401
403
|
].join(String.fromCharCode(10));
|
|
402
404
|
function escapeHtml(text) {
|
|
403
405
|
return String(text).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
@@ -995,10 +997,10 @@ function mountUpdateBatchPanel(container, options) {
|
|
|
995
997
|
}
|
|
996
998
|
}
|
|
997
999
|
function setTheme(next) {
|
|
998
|
-
if (next !== "default" && next !== "d5-paper") {
|
|
999
|
-
throw new Error("[dsh-plugin-update] \u4E3B\u9898\u975E\u6CD5\uFF1A\u53EA\u6536 default \u6216
|
|
1000
|
+
if (next !== "default" && next !== "archive" && next !== "d5-paper") {
|
|
1001
|
+
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");
|
|
1000
1002
|
}
|
|
1001
|
-
theme = next;
|
|
1003
|
+
theme = normalizePanelTheme(next);
|
|
1002
1004
|
render();
|
|
1003
1005
|
}
|
|
1004
1006
|
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
|
|
12
|
+
* 主题归一:archive 为首选名,旧别名 d5-paper 仍收并归到 archive(DOM 属性只出 archive);
|
|
13
13
|
* 其余一律回 default(纯渲染函数永不抛;挂载/setTheme 的非法值另行抛错)。
|
|
14
14
|
*/
|
|
15
|
-
export declare function normalizePanelTheme(value: unknown): 'default' | '
|
|
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' | 'toggle-changelog' | '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
|
/** 与宿主通话的函数(面板侧唯一的宿主接触面)。 */
|
|
@@ -84,6 +86,8 @@ export interface UpdatePanelController {
|
|
|
84
86
|
setTheme(theme: UpdatePanelTheme): Promise<void>;
|
|
85
87
|
/** 切换排队可见性(重查一次,位置口径不变)。 */
|
|
86
88
|
setShowOthers(show: boolean): Promise<void>;
|
|
89
|
+
/** 收起/展开更新日志(纯视图态,不调电话;轮询与换肤不丢)。 */
|
|
90
|
+
setChangelogCollapsed(collapsed: boolean): Promise<void>;
|
|
87
91
|
/**
|
|
88
92
|
* 换一版更新日志后重绘(#23:宿主侧备好新文本后调用;传 null/空串即回中性提示)。
|
|
89
93
|
* 只换日志节,安装门控只跟快照,一字不动。
|
|
@@ -235,7 +239,7 @@ export interface PanelRenderInput extends PanelViewInput {
|
|
|
235
239
|
showOthers: boolean;
|
|
236
240
|
pluginId: string;
|
|
237
241
|
copyNotice: string | null;
|
|
238
|
-
/** 可选主题:不传即默认(输出与旧版一字不差);`d5-paper`
|
|
242
|
+
/** 可选主题:不传即默认(输出与旧版一字不差);`archive` 切档案卷(旧别名 `d5-paper` 仍收)。 */
|
|
239
243
|
theme?: UpdatePanelTheme;
|
|
240
244
|
/** 使用范围名(profile):面板「使用范围」一栏的唯一来源,缺省显示“未知”,不猜。 */
|
|
241
245
|
profileName?: string | null;
|
|
@@ -246,13 +250,32 @@ export interface PanelRenderInput extends PanelViewInput {
|
|
|
246
250
|
* 缺省即静默(输出与旧版一字不差);只在挂载器置忙的那次 render 里传。
|
|
247
251
|
*/
|
|
248
252
|
busyAct?: 'check' | 'install' | null;
|
|
253
|
+
/** 失败引用(失败档案的查询键;缺省即无失败,不画卷宗)。 */
|
|
254
|
+
failure?: PanelFailureRef | null;
|
|
255
|
+
/** 日志指引开关:false 即藏通用日志行(失败证据行不受影响)。 */
|
|
256
|
+
showLogHint?: boolean;
|
|
249
257
|
/**
|
|
250
258
|
* 动作面归谁:`default`(缺省)由内核画动作按钮;`none` 只画内容、不画按钮。
|
|
251
259
|
* 给「调用方自己提供动作面」的场景(如批量面板的详情:动作由批量面板经自己的通道提供)。
|
|
252
260
|
* 只读渲染下五章内容、进度条、「已跳过」提示一字不减,只是没有动作按钮。
|
|
253
261
|
*/
|
|
254
262
|
actions?: 'default' | 'none';
|
|
263
|
+
/**
|
|
264
|
+
* 更新日志折叠(仅挂载态内部用:用户点了 02 章标题行的收起/展开)。
|
|
265
|
+
* 缺省即展开(安全日志必显、可查找;只读渲染下恒展开)。
|
|
266
|
+
*/
|
|
267
|
+
changelogCollapsed?: boolean;
|
|
268
|
+
}
|
|
269
|
+
/** 内核渲染用的失败引用(只读快照:证据仍在锁存里,这里只传展示键)。 */
|
|
270
|
+
export interface PanelFailureRef {
|
|
271
|
+
requestId: string | null;
|
|
272
|
+
checkId: string | null;
|
|
273
|
+
atMs: number | null;
|
|
274
|
+
source: 'check' | 'install' | null;
|
|
275
|
+
volatile: boolean;
|
|
255
276
|
}
|
|
277
|
+
/** 锁存时刻 → 本地 HH:MM:SS(失败档案“失败于”用;非法值回 null,不画时间)。 */
|
|
278
|
+
export declare function formatLatchTime(atMs: unknown): string | null;
|
|
256
279
|
/** 内核 HTML(双形态行为等价的根:同一视图产出同一内核,只换外层)。 */
|
|
257
280
|
export declare function renderUpdatePanelKernel(input: PanelRenderInput, view: PanelView): string;
|
|
258
281
|
/** 整面板 HTML(含样式;重绘即整体替换 innerHTML,故每次都带 style 也只留一份)。 */
|