dsh-plugin-update 0.5.4 → 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/CHANGELOG.md +9 -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 +16 -16
- package/dist/panel.d.ts +20 -4
- package/dist/panel.js +364 -130
- package/dist/store.js +2 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
格式为 Keep-a-Changelog 子集:`Added/Fixed/Changed` 必写,`Deprecated/Removed/Security` 有则透传,`Unreleased` 面板忽略,禁止 git-log 直倒。面板缺日志时中性提示,不挡安装。
|
|
4
4
|
|
|
5
|
+
## [0.5.5] - 2026-10-06
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- 档案卷主题收敛 archive 与面板紧凑化:运行时 data-theme 统一为 archive(d5-paper 仅作输入旧别名归一),entry/entry-batch/panel-batch 输出与 CSS 选择器同步,panel-batch setTheme 补 archive 并归一存储;根/卷头/横幅/版本条/章节/章头/队列行/footer 间距收紧;删 changelog-wrap 与章节线重影内边框;页脚去技术词,批量关闭 title 同步。
|
|
9
|
+
- 双语底座 #54(先行):bilingual 字典模块、entry key 映射链、noteVersion 运行时值;research/52-inventory 双语盘点与原型矩阵随附。
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- 安装/查失败常驻与复制诊断锁定(#58):失败横幅保留到下一次用户主动查/装的新结论、任务态或版本变化,只读轮询不再洗回可装页;「复制诊断」锁定致命那次回包(稳定码、摘要、版本、宿主、请求与检查编号冻结在失败时刻);失败旁新增「知道了」可显式确认回到可装页;健康无失败时复制改为当前状态快照,不再伪造失败码;04 章失败时变为卷宗(真实稳定码、本次查询键与失败时刻、证据冻结声明、凭编号对日志的现成查询,平时为路牌+中性行,可用 `showLogHint: false` 藏路牌);日志事件名收归 `log-events.ts` 单源(宿主发射与面板路牌同源);对抗审查补强:瞬态读失败不跨重挂、查锁存不再错配旧安装编号、瞬态失败换诚实文案、跨挂载表有界 100 家;`update-busy` 仍为瞬态不锁存;四电话形状与快照六字段零变更。
|
|
13
|
+
|
|
5
14
|
## [0.5.4] - 2026-10-06
|
|
6
15
|
|
|
7
16
|
### 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;
|
|
@@ -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="
|
|
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="
|
|
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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
@@ -995,10 +995,10 @@ function mountUpdateBatchPanel(container, options) {
|
|
|
995
995
|
}
|
|
996
996
|
}
|
|
997
997
|
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
|
|
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");
|
|
1000
1000
|
}
|
|
1001
|
-
theme = next;
|
|
1001
|
+
theme = normalizePanelTheme(next);
|
|
1002
1002
|
render();
|
|
1003
1003
|
}
|
|
1004
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
|
|
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' | '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`
|
|
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 也只留一份)。 */
|
package/dist/panel.js
CHANGED
|
@@ -9,8 +9,9 @@ import {
|
|
|
9
9
|
shouldFetchChangelog,
|
|
10
10
|
yankedBannerHTML
|
|
11
11
|
} from "./changelog.js";
|
|
12
|
+
import { LOG_EVENT_CALL, LOG_EVENT_CALL_FAIL, LOG_EVENT_INSTALL_EXEC } from "./log-events.js";
|
|
12
13
|
function normalizePanelTheme(value) {
|
|
13
|
-
return value === "
|
|
14
|
+
return value === "archive" || value === "d5-paper" ? "archive" : "default";
|
|
14
15
|
}
|
|
15
16
|
const BLOCKED_COPY = {
|
|
16
17
|
"unknown-profile": {
|
|
@@ -510,8 +511,8 @@ const UPDATE_PANEL_CSS = [
|
|
|
510
511
|
'.dsh-upd-banner[data-kind="update"]{border-color:var(--dsh-upd-ok-line);background:var(--dsh-upd-ok-bg)}',
|
|
511
512
|
'.dsh-upd-banner[data-kind="busy"]{border-color:var(--dsh-upd-busy-line);background:var(--dsh-upd-busy-bg)}',
|
|
512
513
|
".dsh-upd code{font-family:Consolas,Menlo,monospace;font-size:12px;word-break:break-all}",
|
|
513
|
-
".dsh-upd-manual,.dsh-upd-queue,.dsh-upd-log{margin:
|
|
514
|
-
".dsh-upd-changelog-wrap{margin:
|
|
514
|
+
".dsh-upd-manual,.dsh-upd-queue,.dsh-upd-log{margin:4px 0;font-size:13px}",
|
|
515
|
+
".dsh-upd-changelog-wrap{margin:4px 0 0;font-size:13px}",
|
|
515
516
|
".dsh-upd-changelog-title{font-weight:700;margin:0 0 4px}",
|
|
516
517
|
".dsh-upd-changelog-version{margin:6px 0}",
|
|
517
518
|
".dsh-upd-changelog-catname{font-weight:600;margin:6px 0 2px}",
|
|
@@ -596,7 +597,7 @@ const UPDATE_PANEL_CSS = [
|
|
|
596
597
|
].join("\n");
|
|
597
598
|
const UPDATE_PANEL_D5_CSS = [
|
|
598
599
|
"/* D5 \u6863\u6848\u5377\u53EF\u9009\u4E3B\u9898\uFF1A\u53EA\u6362\u989C\u8272/\u5B57\u4F53/\u95F4\u8DDD\uFF1B\u5185\u6838 DOM \u987A\u5E8F\u4E00\u5B57\u4E0D\u52A8\uFF0C\u4E0D\u65AD\u590D\u5236\u8BCA\u65AD\u3002 */",
|
|
599
|
-
'.dsh-upd[data-theme="
|
|
600
|
+
'.dsh-upd[data-theme="archive"]{--d5-bg:#f7f3ea;--d5-card:#fffdf6;--d5-ink:#1a1a1a;--d5-muted:#6f675a;',
|
|
600
601
|
"--d5-line:#e3d9c4;--d5-line-strong:#c4b896;--d5-accent:#c8402a;--d5-accent-deep:#9c2e1d;",
|
|
601
602
|
"--d5-ok:#1a7f37;--d5-ok-bg:#e9f4ea;--d5-warn:#8a5a00;--d5-warn-bg:#fbf0d0;",
|
|
602
603
|
"--d5-bad:#b3261e;--d5-bad-bg:#fbe9e5;",
|
|
@@ -607,116 +608,125 @@ const UPDATE_PANEL_D5_CSS = [
|
|
|
607
608
|
"font-family:var(--d5-sans);color:var(--d5-ink);background:var(--d5-card);",
|
|
608
609
|
"border:1px solid var(--d5-line-strong);border-radius:4px;box-shadow:var(--d5-shadow)}",
|
|
609
610
|
// 按钮脸自己不透明(宿主底色未知时也读得出;卡片上渲染与 transparent 逐字同色)。
|
|
610
|
-
'.dsh-upd[data-theme="
|
|
611
|
-
'.dsh-upd[data-theme="
|
|
612
|
-
'.dsh-upd[data-theme="
|
|
613
|
-
'.dsh-upd[data-theme="
|
|
614
|
-
'.dsh-upd[data-theme="
|
|
615
|
-
'.dsh-upd[data-theme="
|
|
616
|
-
'.dsh-upd[data-theme="
|
|
617
|
-
'.dsh-upd[data-theme="
|
|
618
|
-
'.dsh-upd[data-theme="
|
|
619
|
-
'.dsh-upd[data-theme="
|
|
620
|
-
'.dsh-upd[data-theme="
|
|
621
|
-
'.dsh-upd[data-theme="
|
|
622
|
-
'.dsh-upd[data-theme="
|
|
623
|
-
'.dsh-upd[data-theme="
|
|
624
|
-
'.dsh-upd[data-theme="
|
|
611
|
+
'.dsh-upd[data-theme="archive"] button{border-color:var(--d5-line-strong);background:var(--d5-card);color:var(--d5-ink);border-radius:3px;font-family:var(--d5-sans)}',
|
|
612
|
+
'.dsh-upd[data-theme="archive"] button:hover:not(:disabled){border-color:var(--d5-accent);color:var(--d5-accent)}',
|
|
613
|
+
'.dsh-upd[data-theme="archive"] button[data-primary="1"]{background:var(--d5-accent);border-color:var(--d5-accent);color:#fff}',
|
|
614
|
+
'.dsh-upd[data-theme="archive"] button[data-primary="1"]:hover:not(:disabled){background:var(--d5-accent-deep);color:#fff}',
|
|
615
|
+
'.dsh-upd[data-theme="archive"] button:focus-visible{outline:2px solid var(--d5-accent);outline-offset:2px}',
|
|
616
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-banner{background:var(--d5-bg);border-color:var(--d5-line-strong)}',
|
|
617
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="update"]{border-color:var(--d5-ok);background:var(--d5-ok-bg)}',
|
|
618
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="busy"]{border-color:var(--d5-warn);background:var(--d5-warn-bg)}',
|
|
619
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="restart"]{border-color:var(--d5-warn);background:var(--d5-warn-bg);font-family:var(--d5-serif);border-width:2px}',
|
|
620
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="failed"],.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="blocked"]{border-color:var(--d5-bad);background:var(--d5-bad-bg)}',
|
|
621
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="done"]{border-color:var(--d5-ok);background:var(--d5-ok-bg)}',
|
|
622
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-changelog-yanked{border-color:var(--d5-warn);background:var(--d5-warn-bg);color:var(--d5-ink)}',
|
|
623
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-breaking-badge{color:var(--d5-accent);border-color:var(--d5-accent)}',
|
|
624
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-changelog-count{color:var(--d5-muted)}',
|
|
625
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-changelog-more-note{color:var(--d5-muted)}',
|
|
625
626
|
// —— 大印章(原型 :206 `.seal`:右上 88px、旋转 -7°、双细框;内容与色调来自根属性,不加节点)——
|
|
626
|
-
'.dsh-upd[data-theme="
|
|
627
|
-
'.dsh-upd[data-theme="
|
|
627
|
+
'.dsh-upd[data-theme="archive"]{position:relative;padding:16px 20px 14px}',
|
|
628
|
+
'.dsh-upd[data-theme="archive"]::before{content:attr(data-seal);position:absolute;top:20px;right:24px;width:88px;height:88px;',
|
|
628
629
|
"display:flex;align-items:center;justify-content:center;text-align:center;letter-spacing:.18em;text-indent:.18em;line-height:1.35;",
|
|
629
630
|
"border:3px solid currentColor;border-radius:14px;transform:rotate(-7deg);font-family:var(--d5-serif);font-weight:700;font-size:21px;",
|
|
630
631
|
"background:color-mix(in srgb,currentColor 8%,transparent);user-select:none;pointer-events:none;",
|
|
631
632
|
"box-shadow:inset 0 0 0 5px var(--d5-card),inset 0 0 0 6px currentColor,0 2px 6px rgba(0,0,0,.12)}",
|
|
632
|
-
'.dsh-upd[data-theme="
|
|
633
|
-
'.dsh-upd[data-theme="
|
|
634
|
-
'.dsh-upd[data-theme="
|
|
635
|
-
'.dsh-upd[data-theme="
|
|
633
|
+
'.dsh-upd[data-theme="archive"][data-seal-tone="ink"]::before{color:var(--d5-muted)}',
|
|
634
|
+
'.dsh-upd[data-theme="archive"][data-seal-tone="green"]::before{color:var(--d5-ok)}',
|
|
635
|
+
'.dsh-upd[data-theme="archive"][data-seal-tone="yellow"]::before{color:var(--d5-warn)}',
|
|
636
|
+
'.dsh-upd[data-theme="archive"][data-seal-tone="red"]::before{color:var(--d5-bad)}',
|
|
636
637
|
// 印章占位:首行(横幅/状态行)右侧留出 120px,文字不许压到印章上(原型 .filehead padding-right:120px)
|
|
637
638
|
// —— 卷宗抬头(原型 :195-203 的刊头,主题切换按钮按用户口径去掉):只有 D5 档案卷才显示 ——
|
|
638
|
-
'.dsh-upd[data-theme="
|
|
639
|
-
'.dsh-upd[data-theme="
|
|
640
|
-
'.dsh-upd[data-theme="
|
|
641
|
-
'.dsh-upd[data-theme="
|
|
639
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-masthead{display:block;padding:0 0 8px;margin:0 0 8px;border-bottom:1px solid var(--d5-line)}',
|
|
640
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-masthead-kicker{display:block;font-size:11px;letter-spacing:.35em;color:var(--d5-muted);margin-bottom:3px}',
|
|
641
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-masthead-title{font-family:var(--d5-serif);font-size:26px;font-weight:700;line-height:1.2}',
|
|
642
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-masthead-title i{color:var(--d5-accent);font-style:normal}',
|
|
642
643
|
// 横幅不再给大印章留 124px:实测(headless 量盒子)印章盒底边 y=109,横幅正文顶边 y=108、
|
|
643
644
|
// 状态行那句在 y=159——印章只压到横幅顶部的留白带,压不到正文。留着反而把 27px 那句话挤成两行
|
|
644
645
|
// (27px 单行需 428px,留白后只剩 366px)。档案头那 120px 保留:那里是真的重叠。
|
|
645
646
|
// —— 更新队列(03 章)D5 皮肤 ——
|
|
646
|
-
'.dsh-upd[data-theme="
|
|
647
|
-
'.dsh-upd[data-theme="
|
|
648
|
-
'.dsh-upd[data-theme="
|
|
649
|
-
'.dsh-upd[data-theme="
|
|
650
|
-
'.dsh-upd[data-theme="
|
|
651
|
-
'.dsh-upd[data-theme="
|
|
647
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-qrow{padding:6px 0}',
|
|
648
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-qk{width:66px;letter-spacing:.18em}',
|
|
649
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-qv{font-family:var(--d5-serif);font-size:16px}',
|
|
650
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-qn{font-family:var(--d5-mono);font-size:11.5px;color:var(--d5-muted)}',
|
|
651
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-qseq{color:var(--d5-muted)}',
|
|
652
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-chap-note{color:var(--d5-muted)}',
|
|
652
653
|
// —— 小印章(原型 :215 `.sealmini`:30px、旋转 -5°、一字)——
|
|
653
654
|
// 待重启横幅一律不画印章:那一档的标记是左侧手绘 SVG(原型 :446 的 .mark 只有 SVG)。
|
|
654
|
-
'.dsh-upd[data-theme="
|
|
655
|
-
'.dsh-upd[data-theme="
|
|
656
|
-
'.dsh-upd[data-theme="
|
|
657
|
-
'.dsh-upd[data-theme="
|
|
655
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="loading"]::before,.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="idle"]::before,',
|
|
656
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="update"]::before,.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="busy"]::before,',
|
|
657
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="blocked"]::before,.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="failed"]::before,',
|
|
658
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="done"]::before{content:attr(data-mini);display:inline-flex;align-items:center;justify-content:center;',
|
|
658
659
|
"width:30px;height:30px;margin-right:10px;vertical-align:middle;border:2px solid currentColor;border-radius:7px;",
|
|
659
660
|
"font-family:var(--d5-serif);font-weight:700;font-size:16px;line-height:26px;transform:rotate(-5deg);flex:none;color:var(--d5-muted)}",
|
|
660
|
-
'.dsh-upd[data-theme="
|
|
661
|
-
'.dsh-upd[data-theme="
|
|
662
|
-
'.dsh-upd[data-theme="
|
|
663
|
-
'.dsh-upd[data-theme="
|
|
664
|
-
'.dsh-upd[data-theme="
|
|
665
|
-
'.dsh-upd[data-theme="
|
|
661
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="update"]::before{color:var(--d5-ok)}',
|
|
662
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="busy"]::before{color:var(--d5-warn)}',
|
|
663
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="blocked"]::before,.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="failed"]::before{color:var(--d5-bad)}',
|
|
664
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="done"]::before{color:var(--d5-ok)}',
|
|
665
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-log code{font-family:var(--d5-mono);font-size:11px;color:var(--d5-muted);border:1px solid var(--d5-line-strong);border-radius:3px;padding:0 6px;letter-spacing:.06em}',
|
|
666
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-manual code{display:block;background:var(--d5-ink);color:var(--d5-bg);font-family:var(--d5-mono);font-size:12.5px;padding:12px 14px;border-radius:4px;white-space:pre-wrap;word-break:break-all}',
|
|
666
667
|
// 待重启标记:手绘 SVG 当**独立 flex 标记**放在文字块左侧(原型 :446 `.mark` 是独立节点),
|
|
667
668
|
// 不能用行内背景——那样换行时三角会落在句子中间把话劈开(现场回归)。
|
|
668
|
-
'.dsh-upd[data-theme="
|
|
669
|
-
'.dsh-upd[data-theme="
|
|
669
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="restart"]>div:first-child{display:flex;gap:10px;align-items:flex-start}',
|
|
670
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="restart"]>div:first-child::before{content:"";flex:none;width:20px;height:20px;margin-top:3px;',
|
|
670
671
|
'background:url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2720%27 height=%2720%27 viewBox=%270 0 24 24%27 fill=%27none%27 stroke=%27%238a5a00%27 stroke-width=%272%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27%3E%3Cpath d=%27M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z%27/%3E%3Cline x1=%2712%27 y1=%279%27 x2=%2712%27 y2=%2713%27/%3E%3Cline x1=%2712%27 y1=%2717%27 x2=%2712.01%27 y2=%2717%27/%3E%3C/svg%3E") no-repeat center/20px 20px}',
|
|
671
|
-
'.dsh-upd[data-theme="
|
|
672
|
-
'.dsh-upd[data-theme="
|
|
672
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-banner>div:first-child{overflow:hidden;text-overflow:ellipsis}',
|
|
673
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-actions{flex-wrap:wrap}',
|
|
673
674
|
// footer 只换肤(#47 定案 A + D5 约束:不换 DOM 顺序;复制诊断永不隐藏,本串不动它)。
|
|
674
|
-
'.dsh-upd[data-theme="
|
|
675
|
-
'.dsh-upd[data-theme="
|
|
675
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-footer{margin-top:14px;padding-top:10px;border-top:1px solid var(--d5-line)}',
|
|
676
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-foot-note{color:var(--d5-muted)}',
|
|
676
677
|
// —— 档案头(原型 :208-211 `.filehead`:serif 插件名 22px + 使用范围 + profile 牌;右侧留章位)——
|
|
677
|
-
'.dsh-upd[data-theme="
|
|
678
|
-
'.dsh-upd[data-theme="
|
|
679
|
-
'.dsh-upd[data-theme="
|
|
680
|
-
'.dsh-upd[data-theme="
|
|
681
|
-
'.dsh-upd[data-theme="
|
|
678
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-head{display:flex;gap:16px;align-items:baseline;flex-wrap:wrap;padding-right:120px}',
|
|
679
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-name{font-family:var(--d5-serif);font-size:22px;font-weight:700}',
|
|
680
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-meta{width:100%;font-size:12.5px;color:var(--d5-muted)}',
|
|
681
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-meta b{color:var(--d5-ink);font-weight:600}',
|
|
682
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-proftag{font-family:var(--d5-mono);font-size:11px;color:var(--d5-muted);border:1px solid var(--d5-line-strong);border-radius:3px;padding:0 6px;margin-left:8px;letter-spacing:.06em}',
|
|
682
683
|
// —— 版本条(原型 :216 `.strip`:三格,格间一线,左上小写标签 + 等宽值)——
|
|
683
|
-
'.dsh-upd[data-theme="
|
|
684
|
-
'.dsh-upd[data-theme="
|
|
685
|
-
'.dsh-upd[data-theme="
|
|
686
|
-
'.dsh-upd[data-theme="
|
|
687
|
-
'.dsh-upd[data-theme="
|
|
684
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-strip{display:flex;flex-wrap:wrap;margin:8px 0 0;border:1px solid var(--d5-line);border-radius:4px;overflow:hidden;font-size:12.5px}',
|
|
685
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-strip>div{flex:1 1 120px;padding:6px 10px;border-left:1px solid var(--d5-line)}',
|
|
686
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-strip>div:first-child{border-left:0}',
|
|
687
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-strip-k{display:block;font-size:11px;letter-spacing:.2em;color:var(--d5-muted)}',
|
|
688
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-strip-v{font-family:var(--d5-mono);font-size:13px}',
|
|
688
689
|
// —— 章节(原型 :218-245:01–05 编号 + 衬线标题 + 细线)——
|
|
689
|
-
'.dsh-upd[data-theme="
|
|
690
|
-
'.dsh-upd[data-theme="
|
|
691
|
-
'.dsh-upd[data-theme="
|
|
692
|
-
'.dsh-upd[data-theme="
|
|
693
|
-
'.dsh-upd[data-theme="
|
|
690
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-chapter{margin-top:16px;padding-top:10px;border-top:1px solid var(--d5-line)}',
|
|
691
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-chap-head{display:flex;align-items:baseline;gap:12px;margin-bottom:6px}',
|
|
692
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-chap-no{font-family:var(--d5-serif);font-style:italic;font-size:15px;color:var(--d5-muted)}',
|
|
693
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-chap-title{font-family:var(--d5-serif);font-size:17px;margin:0;letter-spacing:.1em}',
|
|
694
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-chap-rule{flex:1;border-top:1px solid var(--d5-line);transform:translateY(-4px)}',
|
|
694
695
|
// —— 横幅即状态行 / 待重启横幅(原型 :81-85 `.restart-banner`:2px 边框、圆角 4、内边距 12/16、衬线;右侧留章位)——
|
|
695
|
-
'.dsh-upd[data-theme="
|
|
696
|
-
'.dsh-upd[data-theme="
|
|
696
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-banner{border:2px solid var(--d5-line-strong);border-radius:4px;padding:10px 12px;font-size:14.5px;font-family:var(--d5-serif);display:flex;gap:10px;align-items:center;flex-wrap:wrap}',
|
|
697
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-banner>div:first-child{flex:1 1 auto;min-width:0}',
|
|
697
698
|
// 状态行字号照原型 .status-line=27px(实测去掉横幅右侧占位后可写 486px > 428px,一行放得下)
|
|
698
|
-
'.dsh-upd[data-theme="
|
|
699
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-banner>div:first-child strong{font-family:var(--d5-serif);font-size:27px;font-weight:700;line-height:1.25}',
|
|
699
700
|
// —— 进度条 / 跳过行(原型 :132-133 `.prog`、:129-131 `.skipline .tag`)——
|
|
700
|
-
'.dsh-upd[data-theme="
|
|
701
|
-
'.dsh-upd[data-theme="
|
|
702
|
-
'.dsh-upd[data-theme="
|
|
703
|
-
'.dsh-upd[data-theme="
|
|
704
|
-
'.dsh-upd[data-theme="
|
|
705
|
-
'.dsh-upd[data-theme="
|
|
706
|
-
'@media (max-width:640px){.dsh-upd[data-theme="
|
|
707
|
-
'@media (prefers-color-scheme: dark){.dsh-upd[data-theme="
|
|
701
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-prog{height:8px;background:var(--d5-line);border-radius:4px;overflow:hidden;margin:10px 0 4px}',
|
|
702
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-prog-bar{display:block;height:100%;background:var(--d5-accent);transition:width .3s}',
|
|
703
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-progtxt{font-size:12.5px;color:var(--d5-muted)}',
|
|
704
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-skipline{font-size:13px;margin-top:8px}',
|
|
705
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-tag{display:inline-block;border:1px dashed var(--d5-line-strong);border-radius:3px;padding:1px 8px;margin-right:8px;font-family:var(--d5-mono);font-size:12px}',
|
|
706
|
+
'.dsh-upd[data-theme="archive"] .dsh-upd-err{font-size:13px;margin:0 0 6px;color:var(--d5-muted)}',
|
|
707
|
+
'@media (max-width:640px){.dsh-upd[data-theme="archive"]{padding:10px 12px}.dsh-upd[data-theme="archive"]::before{top:12px;right:12px;width:56px;height:56px;font-size:15px;box-shadow:inset 0 0 0 4px var(--d5-card),inset 0 0 0 5px currentColor}.dsh-upd[data-theme="archive"] .dsh-upd-masthead-title{font-size:19px}.dsh-upd[data-theme="archive"] .dsh-upd-banner{padding-right:16px}.dsh-upd[data-theme="archive"] .dsh-upd-banner::before{width:24px;height:24px;font-size:14px;line-height:20px;flex:none}.dsh-upd[data-theme="archive"] .dsh-upd-banner>div:first-child{white-space:normal}}',
|
|
708
|
+
'@media (prefers-color-scheme: dark){.dsh-upd[data-theme="archive"]{--d5-bg:#141210;--d5-card:#1e1a15;--d5-ink:#ece5d3;--d5-muted:#a89c83;',
|
|
708
709
|
"--d5-line:#3a3226;--d5-line-strong:#5c4e3b;--d5-accent:#e0684e;--d5-accent-deep:#f0866b;",
|
|
709
710
|
"--d5-ok:#8fd6a4;--d5-ok-bg:rgba(80,180,120,.12);--d5-warn:#e8c15a;--d5-warn-bg:rgba(232,193,90,.12);",
|
|
710
711
|
"--d5-bad:#ef8a7d;--d5-bad-bg:rgba(239,138,125,.12);--d5-shadow:0 1px 2px rgba(0,0,0,.4),0 12px 32px rgba(0,0,0,.45)}}",
|
|
711
|
-
'@media (prefers-color-scheme: dark){.dsh-upd[data-theme="
|
|
712
|
-
'@media (prefers-color-scheme: dark){.dsh-upd[data-theme="
|
|
713
|
-
'@media (forced-colors: active){.dsh-upd[data-theme="
|
|
714
|
-
'@media (prefers-reduced-motion: reduce){.dsh-upd[data-theme="
|
|
712
|
+
'@media (prefers-color-scheme: dark){.dsh-upd[data-theme="archive"] button[data-primary="1"]{color:#141210}.dsh-upd[data-theme="archive"] button:focus-visible{outline-color:var(--d5-accent-deep)}}',
|
|
713
|
+
'@media (prefers-color-scheme: dark){.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="restart"]>div:first-child::before{background-image:url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2720%27 height=%2720%27 viewBox=%270 0 24 24%27 fill=%27none%27 stroke=%27%23e8c15a%27 stroke-width=%272%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27%3E%3Cpath d=%27M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z%27/%3E%3Cline x1=%2712%27 y1=%279%27 x2=%2712%27 y2=%2713%27/%3E%3Cline x1=%2712%27 y1=%2717%27 x2=%2712.01%27 y2=%2717%27/%3E%3C/svg%3E")}}',
|
|
714
|
+
'@media (forced-colors: active){.dsh-upd[data-theme="archive"]{box-shadow:none}.dsh-upd[data-theme="archive"]::before{background:none;box-shadow:none;border-color:CanvasText;color:CanvasText}.dsh-upd[data-theme="archive"] .dsh-upd-banner{border:1px solid CanvasText}.dsh-upd[data-theme="archive"] .dsh-upd-banner::before{border-color:CanvasText;color:CanvasText;background:Canvas}.dsh-upd[data-theme="archive"] button{border:1px solid ButtonText}.dsh-upd[data-theme="archive"] button[data-primary="1"]{background:ButtonFace;color:ButtonText;border-color:ButtonText}.dsh-upd[data-theme="archive"] .dsh-upd-banner[data-kind="restart"]>div:first-child::before{background-image:none;content:"\u26A0"}}',
|
|
715
|
+
'@media (prefers-reduced-motion: reduce){.dsh-upd[data-theme="archive"] *{transition:none !important;animation:none !important}}'
|
|
715
716
|
].join("\n");
|
|
716
717
|
function escapeHtml(text) {
|
|
717
718
|
return String(text).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
718
719
|
}
|
|
719
720
|
const CHAPTER_TITLES = ["\u68C0\u67E5\u4E0E\u5B89\u88C5", "\u66F4\u65B0\u65E5\u5FD7", "\u66F4\u65B0\u961F\u5217", "\u9519\u8BEF\u4FE1\u606F", "\u624B\u5DE5\u547D\u4EE4"];
|
|
721
|
+
function formatLatchTime(atMs) {
|
|
722
|
+
if (typeof atMs !== "number" || !Number.isFinite(atMs) || atMs <= 0) return null;
|
|
723
|
+
try {
|
|
724
|
+
const s = new Date(atMs).toLocaleTimeString("zh-CN", { hour12: false });
|
|
725
|
+
return s ? s : null;
|
|
726
|
+
} catch {
|
|
727
|
+
return null;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
720
730
|
function chapterOf(index, inner, note = "") {
|
|
721
731
|
const no = String(index).padStart(2, "0");
|
|
722
732
|
const title = CHAPTER_TITLES[index - 1];
|
|
@@ -781,6 +791,9 @@ function renderUpdatePanelKernel(input, view) {
|
|
|
781
791
|
if (b.kind === "restart") {
|
|
782
792
|
actions.push(`<button type="button" data-action="restart-hint" data-primary="1" title="\u5BBF\u4E3B\u6CA1\u6709\u81EA\u91CD\u542F\u7535\u8BDD\uFF1A\u8BF7\u624B\u52A8\u91CD\u542F\u5BBF\u4E3B">\u91CD\u542F\u5BBF\u4E3B</button>`);
|
|
783
793
|
}
|
|
794
|
+
if (b.kind === "failed") {
|
|
795
|
+
actions.push(`<button type="button" data-action="dismiss-failure" title="\u786E\u8BA4\u5DF2\u77E5\u6653\u8BE5\u5931\u8D25\uFF1A\u56DE\u5230\u53EF\u88C5\u9875\uFF0C\u4E0B\u6B21\u67E5/\u88C5\u5C06\u91CD\u65B0\u8BC4\u4F30">\u77E5\u9053\u4E86</button>`);
|
|
796
|
+
}
|
|
784
797
|
if (snapshot) {
|
|
785
798
|
actions.push(`<button type="button" data-action="copy-diag" title="\u590D\u5236\u5DF2\u8131\u654F\u8BCA\u65AD\uFF0C\u76F4\u63A5\u7C98\u7ED9\u63D2\u4EF6\u4F5C\u8005">\u590D\u5236\u8BCA\u65AD</button>`);
|
|
786
799
|
}
|
|
@@ -877,14 +890,45 @@ ${changelogHTML}
|
|
|
877
890
|
}
|
|
878
891
|
{
|
|
879
892
|
const errLines = [];
|
|
880
|
-
|
|
893
|
+
const failedNow = b.kind === "failed" || b.kind === "blocked";
|
|
894
|
+
const failRef = input.failure;
|
|
895
|
+
if (failedNow) {
|
|
896
|
+
const shownCode = b.kind === "blocked" ? snapshot?.blockedReason ?? b.kind : typeof input.lastError === "string" && input.lastError.trim() ? input.lastError : "install-failed";
|
|
881
897
|
errLines.push(
|
|
882
|
-
`<div class="dsh-upd-err">\u7A33\u5B9A\u7801 <code>${escapeHtml(String(
|
|
898
|
+
`<div class="dsh-upd-err">\u7A33\u5B9A\u7801 <code>${escapeHtml(String(shownCode))}</code>\uFF1A\u4E0A\u4E00\u6761\u4E2D\u6587\u8BF4\u660E\u5C31\u662F\u8981\u7528\u6237\u505A\u7684\u4E8B\uFF1B\u8981\u5F80\u4E0A\u6E38\u62A5\uFF0C\u7528\u300C\u590D\u5236\u8BCA\u65AD\u300D\u6574\u6BB5\u7C98\uFF08\u5DF2\u8131\u654F\uFF09\u3002</div>`
|
|
883
899
|
);
|
|
900
|
+
if (b.kind === "failed" && failRef) {
|
|
901
|
+
const keys = [];
|
|
902
|
+
if (typeof failRef.requestId === "string" && failRef.requestId) keys.push(`\u8BF7\u6C42 <code>${escapeHtml(failRef.requestId)}</code>`);
|
|
903
|
+
if (typeof failRef.checkId === "string" && failRef.checkId) keys.push(`\u68C0\u67E5 <code>${escapeHtml(failRef.checkId)}</code>`);
|
|
904
|
+
const at = formatLatchTime(failRef.atMs);
|
|
905
|
+
if (at) keys.push(`\u5931\u8D25\u4E8E ${escapeHtml(at)}`);
|
|
906
|
+
if (keys.length > 0) {
|
|
907
|
+
errLines.push(`<div class="dsh-upd-err">\u672C\u6B21\u67E5\u8BE2\u952E\uFF1A${keys.join(" \xB7 ")}\uFF08\u62FF\u7740\u5B83\u4EEC\u53BB\u65E5\u5FD7\u91CC\u5BF9\uFF09\u3002</div>`);
|
|
908
|
+
}
|
|
909
|
+
if (!failRef.volatile) {
|
|
910
|
+
errLines.push(
|
|
911
|
+
`<div class="dsh-upd-err">\u8BC1\u636E\u5DF2\u51BB\u7ED3\uFF1A\u590D\u5236\u8BCA\u65AD\u91CC\u7684\u7801\u3001\u7248\u672C\u3001\u7F16\u53F7\u90FD\u53D6\u81EA\u5931\u8D25\u65F6\u523B\uFF0C\u4E0D\u968F\u8F6E\u8BE2\u5237\u65B0\uFF1B\u4E0B\u4E00\u6B21\u67E5\u65B0\u7248\u6216\u5B89\u88C5\u4F1A\u66F4\u65B0\u5B83\u3002</div>`
|
|
912
|
+
);
|
|
913
|
+
} else {
|
|
914
|
+
errLines.push(
|
|
915
|
+
`<div class="dsh-upd-err">\u8BFB\u6570\u77AC\u6001\u5931\u8D25\uFF1A\u4E0B\u4E00\u6B21\u6210\u529F\u8BFB\u6570\u4F1A\u81EA\u52A8\u89E3\u9664\uFF1B\u4E00\u76F4\u51FA\u73B0\u518D\u6309\u7A33\u5B9A\u7801\u6392\u67E5\u3002</div>`
|
|
916
|
+
);
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
} else {
|
|
920
|
+
errLines.push(`<div class="dsh-upd-changelog-neutral">\u6682\u65E0\u5931\u8D25\uFF1A\u6B64\u65F6\u590D\u5236\u8BCA\u65AD\u7ED9\u51FA\u7684\u662F\u5F53\u524D\u72B6\u6001\u5FEB\u7167\u3002</div>`);
|
|
921
|
+
}
|
|
922
|
+
if (input.showLogHint !== false) {
|
|
923
|
+
errLines.push(
|
|
924
|
+
`<div class="dsh-upd-log">\u6DF1\u6316\u770B\u65E5\u5FD7\uFF1A\u6309\u63D2\u4EF6\u6807\u8BC6 <code>${escapeHtml(pluginId)}</code> \u8FC7\u6EE4 <code>${LOG_EVENT_CALL}</code>\u3001<code>${LOG_EVENT_CALL_FAIL}</code>\u3001<code>${LOG_EVENT_INSTALL_EXEC}</code> \u4E09\u4E2A\u4E8B\u4EF6\u3002</div>`
|
|
925
|
+
);
|
|
926
|
+
if (b.kind === "failed" && failRef && (failRef.requestId || failRef.checkId)) {
|
|
927
|
+
errLines.push(
|
|
928
|
+
`<div class="dsh-upd-log">\u51ED\u4E0A\u9762\u7684\u8BF7\u6C42\uFF0F\u68C0\u67E5\u7F16\u53F7\u5728 <code>${LOG_EVENT_CALL_FAIL}</code> \u91CC\u5BF9\u4E0A\uFF1B\u57FA\u7EBF\u8017\u65F6\u770B <code>${LOG_EVENT_CALL}</code>\uFF0C\u6267\u884C\u7ED3\u679C\u770B <code>${LOG_EVENT_INSTALL_EXEC}</code>\u3002</div>`
|
|
929
|
+
);
|
|
930
|
+
}
|
|
884
931
|
}
|
|
885
|
-
errLines.push(
|
|
886
|
-
`<div class="dsh-upd-log">\u6DF1\u6316\u770B\u65E5\u5FD7\uFF1A\u6309\u63D2\u4EF6\u6807\u8BC6 <code>${escapeHtml(pluginId)}</code> \u8FC7\u6EE4 <code>host.call</code>\u3001<code>host.call.fail</code>\u3001<code>update.install.exec</code> \u4E09\u4E2A\u4E8B\u4EF6\u3002</div>`
|
|
887
|
-
);
|
|
888
932
|
if (copyNotice) errLines.push(`<div class="dsh-upd-copy" role="status">${escapeHtml(copyNotice)}</div>`);
|
|
889
933
|
parts.push(chapterOf(4, errLines.join("")));
|
|
890
934
|
}
|
|
@@ -896,7 +940,7 @@ ${changelogHTML}
|
|
|
896
940
|
);
|
|
897
941
|
if (showActions && mode === "dialog") {
|
|
898
942
|
parts.push(
|
|
899
|
-
`<div class="dsh-upd-footer"><span class="dsh-upd-foot-note">\
|
|
943
|
+
`<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>`
|
|
900
944
|
);
|
|
901
945
|
}
|
|
902
946
|
return parts.join("\n");
|
|
@@ -904,8 +948,8 @@ ${changelogHTML}
|
|
|
904
948
|
function renderUpdatePanelHTML(input) {
|
|
905
949
|
const view = panelViewModel(input);
|
|
906
950
|
const kernel = renderUpdatePanelKernel(input, view);
|
|
907
|
-
const d5 = normalizePanelTheme(input.theme) === "
|
|
908
|
-
const attr = d5 ? ' data-theme="
|
|
951
|
+
const d5 = normalizePanelTheme(input.theme) === "archive";
|
|
952
|
+
const attr = d5 ? ' data-theme="archive"' : "";
|
|
909
953
|
const sealAttr = ` data-seal="${escapeHtml(view.seal.text)}" data-seal-tone="${view.seal.tone}"`;
|
|
910
954
|
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}>
|
|
911
955
|
${kernel}
|
|
@@ -917,6 +961,7 @@ ${UPDATE_PANEL_D5_CSS}` : UPDATE_PANEL_CSS;
|
|
|
917
961
|
return `<style>${css}</style>
|
|
918
962
|
${body}`;
|
|
919
963
|
}
|
|
964
|
+
const PANEL_FAILURE_LATCHES = /* @__PURE__ */ new Map();
|
|
920
965
|
function isObject(value) {
|
|
921
966
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
922
967
|
}
|
|
@@ -1019,6 +1064,7 @@ function mountUpdatePanel(container, options) {
|
|
|
1019
1064
|
const skipStore = options.skipStore ?? createBrowserSkipStore(pluginId);
|
|
1020
1065
|
const hostKind = typeof options.hostKind === "string" && options.hostKind ? options.hostKind : null;
|
|
1021
1066
|
const profileNameOption = typeof options.profileName === "string" && options.profileName ? options.profileName : null;
|
|
1067
|
+
const showLogHintOption = options.showLogHint !== false;
|
|
1022
1068
|
const diagCopyFormat = options.diagCopyFormat === "line" ? "line" : "block";
|
|
1023
1069
|
let changelogMarkdown = typeof options.changelogMarkdown === "string" ? options.changelogMarkdown : null;
|
|
1024
1070
|
let snapshot = null;
|
|
@@ -1026,9 +1072,139 @@ function mountUpdatePanel(container, options) {
|
|
|
1026
1072
|
let queue = null;
|
|
1027
1073
|
let receipt = null;
|
|
1028
1074
|
let requestId = null;
|
|
1029
|
-
|
|
1030
|
-
let
|
|
1031
|
-
|
|
1075
|
+
const latchKey = `${pluginId}${profileNameOption ?? ""}`;
|
|
1076
|
+
let latch = null;
|
|
1077
|
+
try {
|
|
1078
|
+
latch = PANEL_FAILURE_LATCHES.get(latchKey) ?? null;
|
|
1079
|
+
if (latch?.volatile) latch = null;
|
|
1080
|
+
} catch {
|
|
1081
|
+
latch = null;
|
|
1082
|
+
}
|
|
1083
|
+
function saveLatch() {
|
|
1084
|
+
try {
|
|
1085
|
+
if (latch && !latch.volatile) {
|
|
1086
|
+
if (!PANEL_FAILURE_LATCHES.has(latchKey) && PANEL_FAILURE_LATCHES.size >= 100) {
|
|
1087
|
+
const oldest = PANEL_FAILURE_LATCHES.keys().next();
|
|
1088
|
+
if (!oldest.done) PANEL_FAILURE_LATCHES.delete(oldest.value);
|
|
1089
|
+
}
|
|
1090
|
+
PANEL_FAILURE_LATCHES.set(latchKey, latch);
|
|
1091
|
+
} else PANEL_FAILURE_LATCHES.delete(latchKey);
|
|
1092
|
+
} catch {
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
function latchNow() {
|
|
1096
|
+
try {
|
|
1097
|
+
const n = Date.now();
|
|
1098
|
+
return Number.isFinite(n) ? n : null;
|
|
1099
|
+
} catch {
|
|
1100
|
+
return null;
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
function setLatch(next) {
|
|
1104
|
+
latch = next;
|
|
1105
|
+
saveLatch();
|
|
1106
|
+
}
|
|
1107
|
+
function clearLatch() {
|
|
1108
|
+
if (!latch) return;
|
|
1109
|
+
latch = null;
|
|
1110
|
+
saveLatch();
|
|
1111
|
+
}
|
|
1112
|
+
function currentEnvPair() {
|
|
1113
|
+
return { hostKind: hostKind ?? envHostKind, profileName: profileNameOption ?? envProfileName };
|
|
1114
|
+
}
|
|
1115
|
+
function latchFromReply(reply, source) {
|
|
1116
|
+
const env = currentEnvPair();
|
|
1117
|
+
return {
|
|
1118
|
+
code: failureCodeOf(
|
|
1119
|
+
{ error: reply["error"], errorKind: reply["errorKind"] },
|
|
1120
|
+
source === "install" ? "install-failed" : "check-failed"
|
|
1121
|
+
),
|
|
1122
|
+
kind: typeof reply["errorKind"] === "string" && reply["errorKind"].trim() ? reply["errorKind"].trim() : null,
|
|
1123
|
+
detail: null,
|
|
1124
|
+
diag: Object.prototype.hasOwnProperty.call(reply, "diag") ? reply["diag"] : null,
|
|
1125
|
+
requestId: source === "install" ? requestId : null,
|
|
1126
|
+
checkId: source === "install" ? receipt?.checkId ?? null : null,
|
|
1127
|
+
runningVersion: snapshot?.runningVersion ?? null,
|
|
1128
|
+
installedVersion: snapshot?.installedVersion ?? null,
|
|
1129
|
+
latestVersion: snapshot?.latestVersion ?? null,
|
|
1130
|
+
hostKind: env.hostKind,
|
|
1131
|
+
profileName: env.profileName,
|
|
1132
|
+
source,
|
|
1133
|
+
volatile: false,
|
|
1134
|
+
jobId: null,
|
|
1135
|
+
atMs: latchNow()
|
|
1136
|
+
};
|
|
1137
|
+
}
|
|
1138
|
+
function latchFromJob(job, snap) {
|
|
1139
|
+
const env = currentEnvPair();
|
|
1140
|
+
const message = typeof job.message === "string" ? job.message : null;
|
|
1141
|
+
const detail = message && message.includes(":") ? message.slice(message.indexOf(":") + 1).trim() || null : message;
|
|
1142
|
+
return {
|
|
1143
|
+
code: messageCodeOf(message) || "install-failed",
|
|
1144
|
+
kind: null,
|
|
1145
|
+
detail,
|
|
1146
|
+
// 快照里没有 diag:保留锁存里既有的(多半是致命安装回包自带的),没有即缺省说人话
|
|
1147
|
+
diag: latch?.diag ?? null,
|
|
1148
|
+
requestId: job.requestId ?? latch?.requestId ?? requestId,
|
|
1149
|
+
checkId: receipt?.checkId ?? latch?.checkId ?? null,
|
|
1150
|
+
runningVersion: snap?.runningVersion ?? snapshot?.runningVersion ?? null,
|
|
1151
|
+
installedVersion: snap?.installedVersion ?? snapshot?.installedVersion ?? null,
|
|
1152
|
+
latestVersion: snap?.latestVersion ?? snapshot?.latestVersion ?? null,
|
|
1153
|
+
hostKind: env.hostKind ?? latch?.hostKind ?? null,
|
|
1154
|
+
profileName: env.profileName ?? latch?.profileName ?? null,
|
|
1155
|
+
source: "install",
|
|
1156
|
+
volatile: false,
|
|
1157
|
+
jobId: typeof job.id === "string" ? job.id : null,
|
|
1158
|
+
atMs: latchNow()
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
function volatileLatch(source) {
|
|
1162
|
+
const env = currentEnvPair();
|
|
1163
|
+
return {
|
|
1164
|
+
code: "check-failed",
|
|
1165
|
+
kind: null,
|
|
1166
|
+
detail: null,
|
|
1167
|
+
diag: null,
|
|
1168
|
+
requestId,
|
|
1169
|
+
checkId: receipt?.checkId ?? null,
|
|
1170
|
+
runningVersion: snapshot?.runningVersion ?? null,
|
|
1171
|
+
installedVersion: snapshot?.installedVersion ?? null,
|
|
1172
|
+
latestVersion: snapshot?.latestVersion ?? null,
|
|
1173
|
+
hostKind: env.hostKind,
|
|
1174
|
+
profileName: env.profileName,
|
|
1175
|
+
source,
|
|
1176
|
+
volatile: true,
|
|
1177
|
+
jobId: null,
|
|
1178
|
+
atMs: latchNow()
|
|
1179
|
+
};
|
|
1180
|
+
}
|
|
1181
|
+
function backfillLatch(s) {
|
|
1182
|
+
if (!latch) return;
|
|
1183
|
+
let touched = false;
|
|
1184
|
+
if (latch.runningVersion === null && s.runningVersion) {
|
|
1185
|
+
latch.runningVersion = s.runningVersion;
|
|
1186
|
+
touched = true;
|
|
1187
|
+
}
|
|
1188
|
+
if (latch.installedVersion === null && s.installedVersion) {
|
|
1189
|
+
latch.installedVersion = s.installedVersion;
|
|
1190
|
+
touched = true;
|
|
1191
|
+
}
|
|
1192
|
+
if (latch.latestVersion === null && s.latestVersion) {
|
|
1193
|
+
latch.latestVersion = s.latestVersion;
|
|
1194
|
+
touched = true;
|
|
1195
|
+
}
|
|
1196
|
+
if (touched) saveLatch();
|
|
1197
|
+
}
|
|
1198
|
+
function tripleChanged(s) {
|
|
1199
|
+
if (!latch) return false;
|
|
1200
|
+
backfillLatch(s);
|
|
1201
|
+
const pairs = [
|
|
1202
|
+
[latch.runningVersion, s.runningVersion ?? null],
|
|
1203
|
+
[latch.installedVersion, s.installedVersion ?? null],
|
|
1204
|
+
[latch.latestVersion, s.latestVersion ?? null]
|
|
1205
|
+
];
|
|
1206
|
+
return pairs.some(([a, b]) => a !== null && b !== null && a !== b);
|
|
1207
|
+
}
|
|
1032
1208
|
let copyNotice = null;
|
|
1033
1209
|
let noticeExpiresAt = 0;
|
|
1034
1210
|
let busyAct = null;
|
|
@@ -1061,8 +1237,10 @@ function mountUpdatePanel(container, options) {
|
|
|
1061
1237
|
queue,
|
|
1062
1238
|
busyAct,
|
|
1063
1239
|
skippedLatest,
|
|
1064
|
-
lastError,
|
|
1065
|
-
errorKind:
|
|
1240
|
+
lastError: latch?.code ?? null,
|
|
1241
|
+
errorKind: latch?.kind ?? null,
|
|
1242
|
+
failure: latch ? { requestId: latch.requestId, checkId: latch.checkId, atMs: latch.atMs, source: latch.source, volatile: latch.volatile } : null,
|
|
1243
|
+
showLogHint: showLogHintOption,
|
|
1066
1244
|
changelogMarkdown,
|
|
1067
1245
|
mode,
|
|
1068
1246
|
showOthers,
|
|
@@ -1078,7 +1256,7 @@ function mountUpdatePanel(container, options) {
|
|
|
1078
1256
|
container.innerHTML = nextHTML;
|
|
1079
1257
|
}
|
|
1080
1258
|
}
|
|
1081
|
-
function applyStatusReply(reply) {
|
|
1259
|
+
function applyStatusReply(reply, via) {
|
|
1082
1260
|
if (!isObject(reply)) return;
|
|
1083
1261
|
if (reply["ok"] === true) {
|
|
1084
1262
|
const s = asSnapshot(reply["snapshot"]);
|
|
@@ -1096,13 +1274,29 @@ function mountUpdatePanel(container, options) {
|
|
|
1096
1274
|
if (typeof pn === "string" && pn.trim()) envProfileName = pn.trim();
|
|
1097
1275
|
if (typeof hk === "string" && hk.trim()) envHostKind = hk.trim();
|
|
1098
1276
|
}
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1277
|
+
const jobState = snapshot?.job?.state ?? null;
|
|
1278
|
+
if (jobState === "installing" || jobState === "verifying") {
|
|
1279
|
+
clearLatch();
|
|
1280
|
+
} else if (jobState === "completed" || jobState === "restart-required") {
|
|
1281
|
+
clearLatch();
|
|
1282
|
+
} else if ((jobState === "failed" || jobState === "interrupted") && snapshot?.job) {
|
|
1283
|
+
setLatch(latchFromJob(snapshot.job, snapshot));
|
|
1284
|
+
} else if (via === "install") {
|
|
1285
|
+
clearLatch();
|
|
1286
|
+
} else if (snapshot && tripleChanged(snapshot)) {
|
|
1287
|
+
clearLatch();
|
|
1288
|
+
} else if (via === "check" && latch?.source === "check") {
|
|
1289
|
+
clearLatch();
|
|
1290
|
+
} else if (latch?.volatile) {
|
|
1291
|
+
clearLatch();
|
|
1292
|
+
}
|
|
1102
1293
|
} else {
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1294
|
+
const errText = typeof reply["error"] === "string" ? reply["error"].trim() : "";
|
|
1295
|
+
const kindText = typeof reply["errorKind"] === "string" ? reply["errorKind"].trim() : "";
|
|
1296
|
+
if ((kindText || errText) === "update-busy") {
|
|
1297
|
+
} else {
|
|
1298
|
+
setLatch(latchFromReply(reply, via === "install" ? "install" : "check"));
|
|
1299
|
+
}
|
|
1106
1300
|
try {
|
|
1107
1301
|
const env = reply["env"];
|
|
1108
1302
|
if (isObject(env)) {
|
|
@@ -1188,12 +1382,10 @@ function mountUpdatePanel(container, options) {
|
|
|
1188
1382
|
try {
|
|
1189
1383
|
const reply = await call(phoneNames.updateStatus, queueArgs());
|
|
1190
1384
|
if (!mounted) return;
|
|
1191
|
-
applyStatusReply(reply);
|
|
1385
|
+
applyStatusReply(reply, "refresh");
|
|
1192
1386
|
} catch {
|
|
1193
1387
|
if (!mounted) return;
|
|
1194
|
-
|
|
1195
|
-
lastErrorKind = null;
|
|
1196
|
-
lastDiag = null;
|
|
1388
|
+
setLatch(volatileLatch("check"));
|
|
1197
1389
|
}
|
|
1198
1390
|
render();
|
|
1199
1391
|
maybeAutoChangelog();
|
|
@@ -1210,13 +1402,11 @@ function mountUpdatePanel(container, options) {
|
|
|
1210
1402
|
try {
|
|
1211
1403
|
const reply = await call(phoneNames.updateCheck, queueArgs());
|
|
1212
1404
|
if (!mounted) return;
|
|
1213
|
-
applyStatusReply(reply);
|
|
1405
|
+
applyStatusReply(reply, "check");
|
|
1214
1406
|
changelogFailedAt.clear();
|
|
1215
1407
|
} catch {
|
|
1216
1408
|
if (!mounted) return;
|
|
1217
|
-
|
|
1218
|
-
lastErrorKind = null;
|
|
1219
|
-
lastDiag = null;
|
|
1409
|
+
setLatch(volatileLatch("check"));
|
|
1220
1410
|
} finally {
|
|
1221
1411
|
busyAct = null;
|
|
1222
1412
|
if (copyNotice === "\u6B63\u5728\u67E5\u65B0\u7248\u2026") copyNotice = null;
|
|
@@ -1234,13 +1424,28 @@ function mountUpdatePanel(container, options) {
|
|
|
1234
1424
|
if (!receipt) {
|
|
1235
1425
|
const checked = await call(phoneNames.updateCheck, queueArgs());
|
|
1236
1426
|
if (!mounted) return;
|
|
1237
|
-
applyStatusReply(checked);
|
|
1427
|
+
applyStatusReply(checked, "check");
|
|
1238
1428
|
}
|
|
1239
1429
|
if (!mounted) return;
|
|
1240
1430
|
if (!receipt) {
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1431
|
+
const expiredEnv = currentEnvPair();
|
|
1432
|
+
setLatch({
|
|
1433
|
+
code: "check-expired",
|
|
1434
|
+
kind: "check-expired",
|
|
1435
|
+
detail: null,
|
|
1436
|
+
diag: null,
|
|
1437
|
+
requestId,
|
|
1438
|
+
checkId: null,
|
|
1439
|
+
runningVersion: snapshot?.runningVersion ?? null,
|
|
1440
|
+
installedVersion: snapshot?.installedVersion ?? null,
|
|
1441
|
+
latestVersion: snapshot?.latestVersion ?? null,
|
|
1442
|
+
hostKind: expiredEnv.hostKind,
|
|
1443
|
+
profileName: expiredEnv.profileName,
|
|
1444
|
+
source: "install",
|
|
1445
|
+
volatile: false,
|
|
1446
|
+
jobId: null,
|
|
1447
|
+
atMs: latchNow()
|
|
1448
|
+
});
|
|
1244
1449
|
render();
|
|
1245
1450
|
return;
|
|
1246
1451
|
}
|
|
@@ -1251,12 +1456,10 @@ function mountUpdatePanel(container, options) {
|
|
|
1251
1456
|
...queueArgs()
|
|
1252
1457
|
});
|
|
1253
1458
|
if (!mounted) return;
|
|
1254
|
-
applyStatusReply(reply);
|
|
1459
|
+
applyStatusReply(reply, "install");
|
|
1255
1460
|
} catch {
|
|
1256
1461
|
if (!mounted) return;
|
|
1257
|
-
|
|
1258
|
-
lastErrorKind = null;
|
|
1259
|
-
lastDiag = null;
|
|
1462
|
+
setLatch(volatileLatch("install"));
|
|
1260
1463
|
} finally {
|
|
1261
1464
|
busyAct = null;
|
|
1262
1465
|
if (copyNotice === "\u6B63\u5728\u5B89\u88C5\u2026") copyNotice = null;
|
|
@@ -1298,25 +1501,49 @@ function mountUpdatePanel(container, options) {
|
|
|
1298
1501
|
}
|
|
1299
1502
|
case "copy-diag": {
|
|
1300
1503
|
const jobCode = snapshot?.job?.state === "failed" ? messageCodeOf(snapshot.job.message) || "install-failed" : "";
|
|
1301
|
-
|
|
1302
|
-
{
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1504
|
+
if (!latch && !jobCode && !snapshot?.blockedReason && snapshot) {
|
|
1505
|
+
const stateLine = snapshot.canInstall && snapshot.latestVersion ? `\u6709\u65B0\u7248 ${snapshot.latestVersion} \u53EF\u88C5\uFF08\u5F53\u524D ${snapshot.runningVersion}\uFF09` : "\u5DF2\u662F\u6700\u65B0\uFF0C\u65E0\u9700\u66F4\u65B0\u3002";
|
|
1506
|
+
const queueName = queueTextOf(queue?.position ?? null);
|
|
1507
|
+
const envBits = [
|
|
1508
|
+
`\u63D2\u4EF6=${pluginId}`,
|
|
1509
|
+
`\u5BBF\u4E3B=${hostKind ?? envHostKind ?? "\u672A\u77E5"}`,
|
|
1510
|
+
`\u4F7F\u7528\u8303\u56F4=${profileNameOption ?? envProfileName ?? "\u672A\u77E5"}`,
|
|
1511
|
+
`\u961F\u5217=${queueName}`
|
|
1512
|
+
];
|
|
1513
|
+
if (requestId) envBits.push(`\u8BF7\u6C42=${requestId}`);
|
|
1514
|
+
if (receipt?.checkId) envBits.push(`\u68C0\u67E5=${receipt.checkId}`);
|
|
1515
|
+
const segs = [
|
|
1516
|
+
`[update-diag] \u5F53\u524D\u65E0\u5931\u8D25\uFF1A${stateLine}`,
|
|
1517
|
+
`\u7248\u672C\uFF1A\u8FD0\u884C ${snapshot.runningVersion}\uFF0F\u78C1\u76D8 ${snapshot.installedVersion ?? "\u672A\u77E5"}\uFF0F\u8FDC\u7AEF ${snapshot.latestVersion ?? "\u672A\u67E5\u8FC7"}`,
|
|
1518
|
+
`\u6765\u6E90\uFF1A${envBits.join(" \xB7 ")}`
|
|
1519
|
+
].map((s) => redactForCopy(s));
|
|
1520
|
+
const text2 = diagCopyFormat === "line" ? segs.join(" \xB7 ") : segs.join("\n");
|
|
1521
|
+
try {
|
|
1522
|
+
await copyText(text2);
|
|
1523
|
+
sayCopy("\u5DF2\u590D\u5236\u5F53\u524D\u72B6\u6001\uFF08\u65E0\u5931\u8D25\uFF09\uFF0C\u76F4\u63A5\u7C98\u7ED9\u63D2\u4EF6\u4F5C\u8005\u5373\u53EF\uFF08\u5DF2\u8131\u654F\uFF09\u3002");
|
|
1524
|
+
} catch {
|
|
1525
|
+
sayCopy("\u590D\u5236\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u9009\u4E2D\u4E0A\u9762\u7684\u4FE1\u606F\u3002");
|
|
1526
|
+
}
|
|
1527
|
+
render();
|
|
1528
|
+
return;
|
|
1529
|
+
}
|
|
1530
|
+
const frozen = latch;
|
|
1531
|
+
const code = frozen?.code ?? failureCodeOf({ error: null, errorKind: null }, jobCode || snapshot?.blockedReason || "check-failed");
|
|
1532
|
+
const detail = frozen?.detail ?? (snapshot?.job?.message && snapshot.job.message.includes(":") ? snapshot.job.message.slice(snapshot.job.message.indexOf(":") + 1) : snapshot?.job?.message);
|
|
1306
1533
|
const text = buildUpdateDiagCopy({
|
|
1307
1534
|
pluginId,
|
|
1308
1535
|
code,
|
|
1309
1536
|
detail,
|
|
1310
|
-
runningVersion: snapshot?.runningVersion ?? null,
|
|
1311
|
-
installedVersion: snapshot?.installedVersion ?? null,
|
|
1312
|
-
latestVersion: snapshot?.latestVersion ?? null,
|
|
1313
|
-
hostKind: hostKind ?? envHostKind,
|
|
1314
|
-
profileName: profileNameOption ?? envProfileName,
|
|
1537
|
+
runningVersion: frozen?.runningVersion ?? snapshot?.runningVersion ?? null,
|
|
1538
|
+
installedVersion: frozen?.installedVersion ?? snapshot?.installedVersion ?? null,
|
|
1539
|
+
latestVersion: frozen?.latestVersion ?? snapshot?.latestVersion ?? null,
|
|
1540
|
+
hostKind: frozen?.hostKind ?? hostKind ?? envHostKind,
|
|
1541
|
+
profileName: frozen?.profileName ?? profileNameOption ?? envProfileName,
|
|
1315
1542
|
queuePosition: queue?.position ?? null,
|
|
1316
|
-
requestId,
|
|
1317
|
-
checkId: receipt?.checkId ?? null,
|
|
1543
|
+
requestId: frozen?.requestId ?? requestId,
|
|
1544
|
+
checkId: frozen?.checkId ?? receipt?.checkId ?? null,
|
|
1318
1545
|
route: null,
|
|
1319
|
-
diag:
|
|
1546
|
+
diag: frozen?.diag ?? null,
|
|
1320
1547
|
manual,
|
|
1321
1548
|
format: diagCopyFormat
|
|
1322
1549
|
});
|
|
@@ -1329,6 +1556,12 @@ function mountUpdatePanel(container, options) {
|
|
|
1329
1556
|
render();
|
|
1330
1557
|
return;
|
|
1331
1558
|
}
|
|
1559
|
+
case "dismiss-failure": {
|
|
1560
|
+
clearLatch();
|
|
1561
|
+
sayCopy("\u5DF2\u786E\u8BA4\u8BE5\u5931\u8D25\u63D0\u793A\uFF1B\u4E0B\u6B21\u67E5\u65B0\u7248\u6216\u5B89\u88C5\u5C06\u91CD\u65B0\u8BC4\u4F30\u3002");
|
|
1562
|
+
render();
|
|
1563
|
+
return;
|
|
1564
|
+
}
|
|
1332
1565
|
case "toggle-queue": {
|
|
1333
1566
|
showOthers = !showOthers;
|
|
1334
1567
|
await refresh();
|
|
@@ -1514,6 +1747,7 @@ export {
|
|
|
1514
1747
|
emptyQueueState,
|
|
1515
1748
|
failureCodeOf,
|
|
1516
1749
|
failureCopy,
|
|
1750
|
+
formatLatchTime,
|
|
1517
1751
|
hasVisibleSections,
|
|
1518
1752
|
isHeadOfQueue,
|
|
1519
1753
|
isKnownFailureCode,
|
package/dist/store.js
CHANGED
|
@@ -8,6 +8,7 @@ import { emptyBatchSession, normalizeBatchSession } from "./batch.js";
|
|
|
8
8
|
import { normalizeQueueState } from "./queue.js";
|
|
9
9
|
import { validReleaseVersion } from "./service.js";
|
|
10
10
|
import { sanitizeDetail } from "./redaction.js";
|
|
11
|
+
import { LOG_EVENT_INSTALL_EXEC } from "./log-events.js";
|
|
11
12
|
const LEGACY_SEGMENT = "updates";
|
|
12
13
|
const LEGACY_DIR_PLUGIN = LEGACY_PLUGIN_ID;
|
|
13
14
|
const JOB_STATES = ["installing", "verifying", "restart-required", "completed", "failed", "interrupted"];
|
|
@@ -496,7 +497,7 @@ function emitInstall(parts, recipe, ok, exitCode, durationMs) {
|
|
|
496
497
|
if (typeof log !== "function") return;
|
|
497
498
|
try {
|
|
498
499
|
const pluginId = part(parts.pluginId ?? null);
|
|
499
|
-
log("info",
|
|
500
|
+
log("info", LOG_EVENT_INSTALL_EXEC, {
|
|
500
501
|
route: recipe ? recipe.route : "none",
|
|
501
502
|
ok: ok === true,
|
|
502
503
|
exitCode: typeof exitCode === "number" ? exitCode : null,
|