dsh-plugin-update 0.5.0 → 0.5.1
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 +8 -0
- package/README.md +5 -3
- package/dist/changelog-io.d.ts +5 -0
- package/dist/changelog.d.ts +66 -3
- package/dist/changelog.js +412 -35
- package/dist/entry.d.ts +5 -1
- package/dist/entry.js +15 -7
- package/dist/host.d.ts +2 -2
- package/dist/host.js +57 -7
- package/dist/panel-batch.js +34 -5
- package/dist/panel.d.ts +2 -2
- package/dist/panel.js +61 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
格式为 Keep-a-Changelog 子集:`Added/Fixed/Changed` 必写,`Deprecated/Removed/Security` 有则透传,`Unreleased` 面板忽略,禁止 git-log 直倒。面板缺日志时中性提示,不挡安装。
|
|
4
4
|
|
|
5
|
+
## [0.5.1] - 2026-10-06
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- 入口件 `openOn: 'direct'`:点开即弹窗、不预查(面板挂载即自查;徽标形态仍走回调口径)。
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- 入口件小字对比度:原地提示与档案卷按钮脸自带底,深色宿主下也读得出。
|
|
12
|
+
|
|
5
13
|
## [0.5.0] - 2026-10-05
|
|
6
14
|
|
|
7
15
|
### Added
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
给 DSH 插件加「检查更新 / 安装更新」能力的 npm 包。宿主侧一段接线,面板侧挂一个现成组件,装不上时给用户一条可复制的手工命令。
|
|
4
4
|
|
|
5
|
-
要求 Node 22 或更高,零运行时依赖。当前版本 `0.5.
|
|
5
|
+
要求 Node 22 或更高,零运行时依赖。当前版本 `0.5.1`。
|
|
6
6
|
|
|
7
7
|
装上它你会拿到六样东西:
|
|
8
8
|
|
|
@@ -181,10 +181,12 @@ const entry = mountUpdateEntry(document.getElementById('upd-entry'), {
|
|
|
181
181
|
|---|---|---|---|
|
|
182
182
|
| 摆什么 | `button` / `badge` / `inline` | `button` | 按钮;只给一个状态点;面板本体直接嵌进来 |
|
|
183
183
|
| 什么时候查 | `mount` / `never` | `mount` | 进页面静默查一次(**只调 `.updateStatus`,只读**);`never` 则只在点击时查 |
|
|
184
|
-
| 点了做什么 | `has-update` / `always` / `manual` | `has-update` |
|
|
184
|
+
| 点了做什么 | `has-update` / `always` / `manual` / `direct` | `has-update` | 有新版才开面板;检查完总是开;交给 `onActivate` 自己跳;点开即弹窗、不预查 |
|
|
185
185
|
|
|
186
186
|
按钮文案随状态自己变:`检查更新` / `有新版 1.1.0` / `正在安装…` / `待重启` / `更新失败,点此查看`。
|
|
187
187
|
|
|
188
|
+
无新版时原地那句小字(`已是最新 X.Y.Z`)只在 `has-update` 下出现:不想看它就用 `openOn: 'always'`(检查完总是开弹窗,无新版在弹窗里看“已是最新”)或 `openOn: 'direct'`(点开即弹窗,连预查都省了,面板挂载即自查;徽标形态仍走回调口径)。
|
|
189
|
+
|
|
188
190
|
**一条铁律:检查是只读、安装是写入,两者不许合并成一个动作。** 入口件永远只做「查 + 打开面板」,
|
|
189
191
|
任何路径都不自动安装;用户必须在面板里明确点「安装」。想让点击交给自己(例如你已有自己的更新页):
|
|
190
192
|
|
|
@@ -302,7 +304,7 @@ const panel = mountUpdatePanel(document.getElementById('update-slot'), {
|
|
|
302
304
|
|
|
303
305
|
宿主种类与安装出口都由本包自己探测和选择,**升级依赖即可,宿主侧与面板侧都不用改代码**:
|
|
304
306
|
|
|
305
|
-
1. 依赖版本提到 `^0.5.
|
|
307
|
+
1. 依赖版本提到 `^0.5.1`。
|
|
306
308
|
2. 重新装 / 发一版你自己的插件,让新依赖进当前使用范围(运行时用的是 `node_modules` 里那份)。
|
|
307
309
|
3. 如果你自己接过一版宿主安装出口,把它删掉——它会挡在本包的路由前面。
|
|
308
310
|
|
package/dist/changelog-io.d.ts
CHANGED
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
* - 已装版离线读:`node_modules/<目标包>/CHANGELOG.md`(与版本 pin 绑定,零新增联网);
|
|
10
10
|
* - 新版按需取:新版 tarball 内同名文件,复用官方源 + integrity 校验;
|
|
11
11
|
* 取不到一律回 null(调用方渲染中性提示,不挡安装、不写 blockedReason)。
|
|
12
|
+
*
|
|
13
|
+
* 记住策略(#41 终裁,三墙不动,只澄清注释):成功与取不到(null)按版本永久记
|
|
14
|
+
* (面板内存会话级 + 宿主进程级,不落盘,显式文本永不覆盖);传输失败不进缓存,
|
|
15
|
+
* 手动查新版/换版/重开面板立即重问,轮询按退避问(三处共用 shouldFetchChangelog,各存各的)。
|
|
16
|
+
* 三墙(8MB 压缩态/32MB 解压后/64K 日志文本)一字不动,超限回 null 由调用方按取不到记住。
|
|
12
17
|
*/
|
|
13
18
|
/** tarball 下载上限(字节,压缩态):超限直接回落中性提示,不解包、不OOM。 */
|
|
14
19
|
export declare const CHANGELOG_TARBALL_MAX_BYTES: number;
|
package/dist/changelog.d.ts
CHANGED
|
@@ -9,14 +9,16 @@
|
|
|
9
9
|
* 解析只做字符串切分,不读盘、不联网;版本语义与核心同口径
|
|
10
10
|
* (validReleaseVersion / compareReleaseVersions,stable 纯三段、prerelease 显式)。
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
* - `Added/Fixed/Changed`
|
|
12
|
+
* 面板约定(#23 原文 + #41 四处优化终裁):
|
|
13
|
+
* - `Added/Fixed/Changed/Security` 必显展开;`Deprecated/Removed` 透传折叠;
|
|
14
14
|
* - `Unreleased` 与空节忽略;
|
|
15
15
|
* - 缺日志中性提示,不挡安装、不写 blockedReason、不动快照六字段
|
|
16
16
|
* (本模块不碰快照,面板只增一节 HTML,安装门控只跟快照);
|
|
17
17
|
* - 已装版离线读、新版按需取 tarball 同名文件复用官方源 integrity
|
|
18
18
|
* (I/O 在 `src/changelog-io.ts` Node 侧,取不到回落中性提示);
|
|
19
|
-
* -
|
|
19
|
+
* - 纯函数解析器(仅复用版本语义,不碰 Node 专属;新增 validate 与策略函数零新增导入)。
|
|
20
|
+
* - 截断数值不动(100 节/每类 200 条/单条 500 字/全文 64K),解析附计数元数据只记数字;
|
|
21
|
+
* 超限类标计数小字,Security 超限进内折叠;BREAKING 前缀识别 + 行首徽标;yanked 横幅只看 to 版。
|
|
20
22
|
*/
|
|
21
23
|
/** 包内日志文件名(包根,与 tarball 内同名文件同一名字)。 */
|
|
22
24
|
export declare const CHANGELOG_FILENAME = "CHANGELOG.md";
|
|
@@ -54,6 +56,8 @@ export interface ChangelogEntry {
|
|
|
54
56
|
yanked: boolean;
|
|
55
57
|
/** 六类各自的条目(空类为空数组,空节忽略由渲染层执行)。 */
|
|
56
58
|
sections: Record<ChangelogCategory, string[]>;
|
|
59
|
+
/** 每类原始条数(截断前计数,只记数字不留文本;缺省视为与 sections 等长)。 */
|
|
60
|
+
counts?: Record<ChangelogCategory, number>;
|
|
57
61
|
}
|
|
58
62
|
/** 是否 Unreleased(大小写不敏感,前后空白忽略)。 */
|
|
59
63
|
export declare function isUnreleasedVersion(v: unknown): boolean;
|
|
@@ -62,6 +66,41 @@ export declare function isUnreleasedVersion(v: unknown): boolean;
|
|
|
62
66
|
* 非字符串/空串回 [];超长截断后解析;未知版本标题与其下条目一律丢弃;
|
|
63
67
|
* 全空节版本直接丢弃(空节忽略);Unreleased 保留(渲染与区间时忽略)。
|
|
64
68
|
*/
|
|
69
|
+
/** 共享扫描仪(parse 与 validate 同一套,不许第二套正则)。 */
|
|
70
|
+
export declare const RE_VERSION_HEADING: RegExp;
|
|
71
|
+
export declare const RE_CATEGORY_HEADING: RegExp;
|
|
72
|
+
export declare const RE_BULLET: RegExp;
|
|
73
|
+
export declare const RE_FENCE_TOGGLE: RegExp;
|
|
74
|
+
export declare const RE_VERSION_NUM: RegExp;
|
|
75
|
+
export declare const RE_VERSION_DATE: RegExp;
|
|
76
|
+
export declare const RE_YANKED: RegExp;
|
|
77
|
+
export declare const RE_UNRELEASED: RegExp;
|
|
78
|
+
export declare function matchVersionHeading(line: unknown): string | null;
|
|
79
|
+
export declare function matchCategoryHeading(line: unknown): string | null;
|
|
80
|
+
export declare function matchBulletBody(line: unknown): string | null;
|
|
81
|
+
export declare function isFenceToggle(line: unknown): boolean;
|
|
82
|
+
/** 破坏标记徽标文案(中文 UI 一致,原文前缀另行加粗保留)。 */
|
|
83
|
+
export declare const BREAKING_BADGE_TEXT = "\u4E0D\u517C\u5BB9";
|
|
84
|
+
/** 剥行首 markdown 装饰(加粗/引用/前后空白),只为识别,渲染保留原文一字不动。 */
|
|
85
|
+
export declare function stripBreakingDecorations(s: unknown): string;
|
|
86
|
+
/** 条目是否为破坏标记(去饰后开头匹配,大小写不敏感,中英文冒号皆可)。 */
|
|
87
|
+
export declare function isBreakingChangelogItem(item: unknown): boolean;
|
|
88
|
+
/** 拆出破坏前缀(保留原文拼写与冒号),命中才回,否则 null。 */
|
|
89
|
+
export declare function splitBreakingPrefix(item: string): {
|
|
90
|
+
head: string;
|
|
91
|
+
prefix: string;
|
|
92
|
+
rest: string;
|
|
93
|
+
} | null;
|
|
94
|
+
/** 轮询退避(毫秒):传输失败后轮询按此退避,手动/换版/重开立即重问。 */
|
|
95
|
+
export declare const CHANGELOG_POLL_BACKOFF_MS: number;
|
|
96
|
+
/** 三处共用(宿主电话/单面板/批量按行,各存各的):成功与取不到已记住即不再问。 */
|
|
97
|
+
export declare function shouldFetchChangelog(opts: {
|
|
98
|
+
hasCache: boolean;
|
|
99
|
+
failedAt: number | null;
|
|
100
|
+
now: number;
|
|
101
|
+
isManual: boolean;
|
|
102
|
+
backoffMs?: number;
|
|
103
|
+
}): boolean;
|
|
65
104
|
export declare function parseChangelog(markdown: unknown): ChangelogEntry[];
|
|
66
105
|
/** 该节是否有可见内容(六类任一非空)。 */
|
|
67
106
|
export declare function hasVisibleSections(entry: ChangelogEntry | null | undefined): boolean;
|
|
@@ -81,6 +120,14 @@ export declare function selectChangelogEntries(entries: unknown, fromExclusive:
|
|
|
81
120
|
* 任一缺失/非法即回 [](调用方渲染中性提示,不挡安装)。
|
|
82
121
|
*/
|
|
83
122
|
export declare function changelogForUpdate(entries: unknown, runningVersion: unknown, latestVersion: unknown, installedVersion?: unknown): ChangelogEntry[];
|
|
123
|
+
/** 计数元数据取值(缺省视为与 sections 等长,只记数字)。 */
|
|
124
|
+
export declare function countsOf(entry: ChangelogEntry | null | undefined): Record<ChangelogCategory, number>;
|
|
125
|
+
/** 超限小字(诚实截断):共 M 条,仅显示前 N 条。 */
|
|
126
|
+
export declare function truncatedNoteHTML(total: number, shown: number): string;
|
|
127
|
+
/** 撤回横幅(只看 to 版,纯展示,不挡安装,文案冻结)。 */
|
|
128
|
+
export declare function yankedBannerHTML(version: unknown): string;
|
|
129
|
+
/** 单条目 HTML(含破坏标记徽标与前缀加粗,正文一字不动)。 */
|
|
130
|
+
export declare function renderChangelogItem(item: string): string;
|
|
84
131
|
/** 单节 HTML(调用方保证已过滤 Unreleased 与空节,本函数再兜底一次)。 */
|
|
85
132
|
export declare function renderChangelogSection(entry: ChangelogEntry): string;
|
|
86
133
|
/** 中性提示 HTML(缺日志、取不到、无区间内容时统一用它,不挡安装)。 */
|
|
@@ -94,3 +141,19 @@ export declare function renderChangelogHTML(entries: unknown, opts?: {
|
|
|
94
141
|
from?: string | null;
|
|
95
142
|
to?: string | null;
|
|
96
143
|
}): string;
|
|
144
|
+
/** 校验诊断(一行一码,行号为原文件 1-based,诊断按行号排序)。 */
|
|
145
|
+
export interface ChangelogDiagnostic {
|
|
146
|
+
line: number;
|
|
147
|
+
code: string;
|
|
148
|
+
hint: string;
|
|
149
|
+
}
|
|
150
|
+
export interface ChangelogValidation {
|
|
151
|
+
ok: boolean;
|
|
152
|
+
diagnostics: ChangelogDiagnostic[];
|
|
153
|
+
}
|
|
154
|
+
/** 校验提示文(与终裁码表一字对应,仅 CI/发布前用,运行时永不调用)。 */
|
|
155
|
+
export declare const CHANGELOG_VALIDATE_HINTS: Record<string, string>;
|
|
156
|
+
/** 疑似破坏误写(仅行首可疑,不报正文中间散文)。 */
|
|
157
|
+
export declare function isMaybeBreakingMisuse(bulletBody: unknown): boolean;
|
|
158
|
+
/** 纯校验(零新增导入,与 parse 共享扫描仪,永不抛错,仅 CI/发布前用)。 */
|
|
159
|
+
export declare function validateChangelog(markdown: unknown): ChangelogValidation;
|
package/dist/changelog.js
CHANGED
|
@@ -15,8 +15,8 @@ const CHANGELOG_ALL_CATEGORIES = [
|
|
|
15
15
|
"Removed",
|
|
16
16
|
"Security"
|
|
17
17
|
];
|
|
18
|
-
const CHANGELOG_MUST_SHOW = ["Added", "Fixed", "Changed"];
|
|
19
|
-
const CHANGELOG_FOLDED = ["Deprecated", "Removed"
|
|
18
|
+
const CHANGELOG_MUST_SHOW = ["Added", "Fixed", "Changed", "Security"];
|
|
19
|
+
const CHANGELOG_FOLDED = ["Deprecated", "Removed"];
|
|
20
20
|
const CHANGELOG_CATEGORY_ZH = {
|
|
21
21
|
Added: "\u65B0\u589E",
|
|
22
22
|
Fixed: "\u4FEE\u590D",
|
|
@@ -28,6 +28,9 @@ const CHANGELOG_CATEGORY_ZH = {
|
|
|
28
28
|
function emptySections() {
|
|
29
29
|
return { Added: [], Fixed: [], Changed: [], Deprecated: [], Removed: [], Security: [] };
|
|
30
30
|
}
|
|
31
|
+
function emptyCounts() {
|
|
32
|
+
return { Added: 0, Fixed: 0, Changed: 0, Deprecated: 0, Removed: 0, Security: 0 };
|
|
33
|
+
}
|
|
31
34
|
function isCategoryName(v) {
|
|
32
35
|
return CHANGELOG_ALL_CATEGORIES.includes(v);
|
|
33
36
|
}
|
|
@@ -60,6 +63,119 @@ function truncateBullet(text) {
|
|
|
60
63
|
if (t.length <= CHANGELOG_MAX_BULLET_CHARS) return t;
|
|
61
64
|
return `${t.slice(0, CHANGELOG_MAX_BULLET_CHARS - 1)}\u2026`;
|
|
62
65
|
}
|
|
66
|
+
const RE_VERSION_HEADING = /^##(?!#)\s*(.+?)\s*$/;
|
|
67
|
+
const RE_CATEGORY_HEADING = /^###\s*(.+?)\s*$/;
|
|
68
|
+
const RE_BULLET = /^\s*[-*+]\s+(.+?)\s*$/;
|
|
69
|
+
const RE_FENCE_TOGGLE = new RegExp("^\\s*(" + String.fromCharCode(96).repeat(3) + "|~~~)");
|
|
70
|
+
const RE_VERSION_NUM = /(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)/;
|
|
71
|
+
const RE_VERSION_DATE = /(\d{4}-\d{2}-\d{2})/;
|
|
72
|
+
const RE_YANKED = /\[YANKED\]/i;
|
|
73
|
+
const RE_UNRELEASED = /unreleased/i;
|
|
74
|
+
function matchVersionHeading(line) {
|
|
75
|
+
try {
|
|
76
|
+
const s = String(line !== null && line !== void 0 ? line : "");
|
|
77
|
+
const m = s.match(RE_VERSION_HEADING);
|
|
78
|
+
if (!m) return null;
|
|
79
|
+
return String(m[1] !== void 0 && m[1] !== null ? m[1] : "").trim();
|
|
80
|
+
} catch {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function matchCategoryHeading(line) {
|
|
85
|
+
try {
|
|
86
|
+
const s = String(line !== null && line !== void 0 ? line : "");
|
|
87
|
+
const m = s.match(RE_CATEGORY_HEADING);
|
|
88
|
+
if (!m) return null;
|
|
89
|
+
return String(m[1] !== void 0 && m[1] !== null ? m[1] : "").trim();
|
|
90
|
+
} catch {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function matchBulletBody(line) {
|
|
95
|
+
try {
|
|
96
|
+
const s = String(line !== null && line !== void 0 ? line : "");
|
|
97
|
+
const m = s.match(RE_BULLET);
|
|
98
|
+
if (!m) return null;
|
|
99
|
+
return String(m[1] !== void 0 && m[1] !== null ? m[1] : "");
|
|
100
|
+
} catch {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function isFenceToggle(line) {
|
|
105
|
+
try {
|
|
106
|
+
return RE_FENCE_TOGGLE.test(String(line !== null && line !== void 0 ? line : ""));
|
|
107
|
+
} catch {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const BREAKING_BADGE_TEXT = "\u4E0D\u517C\u5BB9";
|
|
112
|
+
function stripBreakingDecorations(s) {
|
|
113
|
+
try {
|
|
114
|
+
let t = String(s !== null && s !== void 0 ? s : "").trim();
|
|
115
|
+
let changed = true;
|
|
116
|
+
while (changed) {
|
|
117
|
+
changed = false;
|
|
118
|
+
if (t.charAt(0) === ">") {
|
|
119
|
+
t = t.slice(1).trim();
|
|
120
|
+
changed = true;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (t.slice(0, 2) === "**" || t.slice(0, 2) === "__") {
|
|
124
|
+
t = t.slice(2).trim();
|
|
125
|
+
changed = true;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (t.charAt(0) === "*" || t.charAt(0) === "_") {
|
|
129
|
+
t = t.slice(1).trim();
|
|
130
|
+
changed = true;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return t;
|
|
135
|
+
} catch {
|
|
136
|
+
return "";
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function isBreakingChangelogItem(item) {
|
|
140
|
+
try {
|
|
141
|
+
if (typeof item !== "string") return false;
|
|
142
|
+
const t = stripBreakingDecorations(item);
|
|
143
|
+
if (!t) return false;
|
|
144
|
+
return /^(breaking|不兼容)\s*[::]/i.test(t);
|
|
145
|
+
} catch {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function splitBreakingPrefix(item) {
|
|
150
|
+
try {
|
|
151
|
+
const s = String(item !== null && item !== void 0 ? item : "");
|
|
152
|
+
const m = s.match(/^(\s*(?:>\s*|\*\*\s*|__\s*|\*\s*|_\s*)*)(breaking|不兼容)(\s*[::])/i);
|
|
153
|
+
if (!m) return null;
|
|
154
|
+
const head = String(m[1] !== void 0 && m[1] !== null ? m[1] : "");
|
|
155
|
+
const core = String(m[2] !== void 0 && m[2] !== null ? m[2] : "");
|
|
156
|
+
const colon = String(m[3] !== void 0 && m[3] !== null ? m[3] : "");
|
|
157
|
+
const prefix = core + colon;
|
|
158
|
+
const rest = s.slice(m[0].length);
|
|
159
|
+
return { head, prefix, rest };
|
|
160
|
+
} catch {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const CHANGELOG_POLL_BACKOFF_MS = 30 * 1e3;
|
|
165
|
+
function shouldFetchChangelog(opts) {
|
|
166
|
+
try {
|
|
167
|
+
const o = opts;
|
|
168
|
+
if (o.hasCache === true) return false;
|
|
169
|
+
if (o.isManual === true) return true;
|
|
170
|
+
const now = typeof o.now === "number" && Number.isFinite(o.now) ? o.now : Date.now();
|
|
171
|
+
const failedAt = typeof o.failedAt === "number" && Number.isFinite(o.failedAt) ? o.failedAt : null;
|
|
172
|
+
if (failedAt === null) return true;
|
|
173
|
+
const backoff = typeof o.backoffMs === "number" && Number.isFinite(o.backoffMs) && o.backoffMs > 0 ? Math.floor(o.backoffMs) : CHANGELOG_POLL_BACKOFF_MS;
|
|
174
|
+
return now - failedAt >= backoff;
|
|
175
|
+
} catch {
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
63
179
|
function parseChangelog(markdown) {
|
|
64
180
|
try {
|
|
65
181
|
if (typeof markdown !== "string" || !markdown.trim()) return [];
|
|
@@ -86,32 +202,38 @@ function parseChangelog(markdown) {
|
|
|
86
202
|
};
|
|
87
203
|
for (const rawLine of lines) {
|
|
88
204
|
const line = String(rawLine ?? "");
|
|
89
|
-
if (
|
|
205
|
+
if (isFenceToggle(line)) {
|
|
90
206
|
inFence = !inFence;
|
|
91
207
|
continue;
|
|
92
208
|
}
|
|
93
209
|
if (inFence) continue;
|
|
94
|
-
const
|
|
95
|
-
if (
|
|
210
|
+
const versionTitle = matchVersionHeading(line);
|
|
211
|
+
if (versionTitle !== null) {
|
|
96
212
|
pushCurrent();
|
|
97
213
|
cur = null;
|
|
98
214
|
curCat = null;
|
|
99
|
-
const title =
|
|
215
|
+
const title = versionTitle;
|
|
100
216
|
const { version, date, yanked } = extractVersionDateAndYanked(title);
|
|
101
217
|
if (!version) continue;
|
|
102
|
-
cur = { version, date, yanked, sections: emptySections() };
|
|
218
|
+
cur = { version, date, yanked, sections: emptySections(), counts: emptyCounts() };
|
|
103
219
|
continue;
|
|
104
220
|
}
|
|
105
|
-
const
|
|
106
|
-
if (
|
|
107
|
-
const cat = normalizeCategory(
|
|
221
|
+
const catTitle = matchCategoryHeading(line);
|
|
222
|
+
if (catTitle !== null) {
|
|
223
|
+
const cat = normalizeCategory(catTitle);
|
|
108
224
|
curCat = cur && cat && isCategoryName(cat) ? cat : null;
|
|
109
225
|
continue;
|
|
110
226
|
}
|
|
111
|
-
const
|
|
112
|
-
if (
|
|
113
|
-
const item = truncateBullet(
|
|
114
|
-
if (item)
|
|
227
|
+
const bulletBody = matchBulletBody(line);
|
|
228
|
+
if (bulletBody !== null && cur && curCat) {
|
|
229
|
+
const item = truncateBullet(bulletBody);
|
|
230
|
+
if (item) {
|
|
231
|
+
cur.sections[curCat].push(item);
|
|
232
|
+
try {
|
|
233
|
+
if (cur.counts) cur.counts[curCat] += 1;
|
|
234
|
+
} catch {
|
|
235
|
+
}
|
|
236
|
+
}
|
|
115
237
|
continue;
|
|
116
238
|
}
|
|
117
239
|
const trimmed = line.trim();
|
|
@@ -189,46 +311,119 @@ function changelogForUpdate(entries, runningVersion, latestVersion, installedVer
|
|
|
189
311
|
function escapeChangelogHtml(text) {
|
|
190
312
|
return String(text ?? "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
191
313
|
}
|
|
314
|
+
function countsOf(entry) {
|
|
315
|
+
try {
|
|
316
|
+
const out = { Added: 0, Fixed: 0, Changed: 0, Deprecated: 0, Removed: 0, Security: 0 };
|
|
317
|
+
if (!entry || typeof entry !== "object") return out;
|
|
318
|
+
for (const c of CHANGELOG_ALL_CATEGORIES) {
|
|
319
|
+
const n = entry.counts;
|
|
320
|
+
const v = n && typeof n[c] === "number" && Number.isFinite(n[c]) ? Math.floor(n[c]) : null;
|
|
321
|
+
if (v !== null && v >= 0) {
|
|
322
|
+
out[c] = v;
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
const list = entry.sections;
|
|
326
|
+
const arr = list ? list[c] : null;
|
|
327
|
+
out[c] = Array.isArray(arr) ? arr.length : 0;
|
|
328
|
+
}
|
|
329
|
+
return out;
|
|
330
|
+
} catch {
|
|
331
|
+
return { Added: 0, Fixed: 0, Changed: 0, Deprecated: 0, Removed: 0, Security: 0 };
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
function truncatedNoteHTML(total, shown) {
|
|
335
|
+
try {
|
|
336
|
+
const m = Math.floor(total);
|
|
337
|
+
const n = Math.floor(shown);
|
|
338
|
+
return '<div class="dsh-upd-changelog-count">\u5171 ' + String(m) + " \u6761\uFF0C\u4EC5\u663E\u793A\u524D " + String(n) + " \u6761</div>";
|
|
339
|
+
} catch {
|
|
340
|
+
return "";
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
function yankedBannerHTML(version) {
|
|
344
|
+
try {
|
|
345
|
+
const v = typeof version === "string" ? version.trim() : "";
|
|
346
|
+
if (!v) return "";
|
|
347
|
+
return '<div class="dsh-upd-changelog-yanked" role="alert">\u76EE\u6807\u7248\u672C ' + escapeChangelogHtml(v) + " \u5DF2\u88AB\u4F5C\u8005\u64A4\u56DE\uFF08yanked\uFF09\uFF0C\u5B89\u88C5\u4E0D\u53D7\u5F71\u54CD\uFF0C\u7EE7\u7EED\u524D\u8BF7\u786E\u8BA4\u3002</div>";
|
|
348
|
+
} catch {
|
|
349
|
+
return "";
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
function renderChangelogItem(item) {
|
|
353
|
+
try {
|
|
354
|
+
const s = String(item !== null && item !== void 0 ? item : "");
|
|
355
|
+
if (!s) return "";
|
|
356
|
+
const split = splitBreakingPrefix(s);
|
|
357
|
+
if (!split) return "<li>" + escapeChangelogHtml(s) + "</li>";
|
|
358
|
+
const head = escapeChangelogHtml(split.head);
|
|
359
|
+
const prefix = escapeChangelogHtml(split.prefix);
|
|
360
|
+
const rest = escapeChangelogHtml(split.rest);
|
|
361
|
+
const badge = '<span class="dsh-upd-breaking-badge" role="img" aria-label="\u7834\u574F\u6027\u53D8\u66F4">' + BREAKING_BADGE_TEXT + "</span> ";
|
|
362
|
+
return "<li>" + head + badge + "<strong>" + prefix + "</strong>" + rest + "</li>";
|
|
363
|
+
} catch {
|
|
364
|
+
return "";
|
|
365
|
+
}
|
|
366
|
+
}
|
|
192
367
|
function renderChangelogSection(entry) {
|
|
193
368
|
try {
|
|
194
369
|
if (!entry || typeof entry !== "object") return "";
|
|
195
|
-
const version = String(entry.version
|
|
370
|
+
const version = String(entry.version !== void 0 && entry.version !== null ? String(entry.version) : "").trim();
|
|
196
371
|
if (!version || isUnreleasedVersion(version)) return "";
|
|
197
372
|
if (!hasVisibleSections(entry)) return "";
|
|
198
|
-
const
|
|
199
|
-
const
|
|
200
|
-
const
|
|
373
|
+
const dateRaw = entry.date;
|
|
374
|
+
const date = typeof dateRaw === "string" ? dateRaw : "";
|
|
375
|
+
const yankedFlag = entry.yanked === true;
|
|
376
|
+
const yankedSuffix = yankedFlag ? " \xB7 \u5DF2\u64A4\u56DE" : "";
|
|
377
|
+
const title = (date ? version + " \xB7 " + date : version) + yankedSuffix;
|
|
378
|
+
const counts = countsOf(entry);
|
|
201
379
|
const parts = [];
|
|
202
|
-
parts.push(
|
|
203
|
-
parts.push(
|
|
380
|
+
parts.push('<div class="dsh-upd-changelog-version" data-version="' + escapeChangelogHtml(version) + '">');
|
|
381
|
+
parts.push('<div class="dsh-upd-changelog-title">' + escapeChangelogHtml(title) + "</div>");
|
|
204
382
|
for (const cat of CHANGELOG_MUST_SHOW) {
|
|
205
|
-
const items = Array.isArray(entry.sections
|
|
206
|
-
if (items.length === 0) continue;
|
|
207
|
-
const label =
|
|
208
|
-
parts.push(
|
|
209
|
-
parts.push(
|
|
383
|
+
const items = Array.isArray(entry.sections ? entry.sections[cat] : null) ? entry.sections[cat] : [];
|
|
384
|
+
if (!items || items.length === 0) continue;
|
|
385
|
+
const label = cat + " \xB7 " + CHANGELOG_CATEGORY_ZH[cat];
|
|
386
|
+
parts.push('<div class="dsh-upd-changelog-cat" data-cat="' + cat + '">');
|
|
387
|
+
parts.push('<div class="dsh-upd-changelog-catname">' + escapeChangelogHtml(label) + "</div>");
|
|
388
|
+
const total = counts[cat];
|
|
389
|
+
const shown = items.length;
|
|
390
|
+
if (total > shown) {
|
|
391
|
+
parts.push(truncatedNoteHTML(total, shown));
|
|
392
|
+
}
|
|
210
393
|
parts.push("<ul>");
|
|
211
394
|
for (const item of items) {
|
|
212
395
|
if (!item) continue;
|
|
213
|
-
|
|
396
|
+
const li = renderChangelogItem(item);
|
|
397
|
+
if (li) parts.push(li);
|
|
398
|
+
}
|
|
399
|
+
parts.push("</ul>");
|
|
400
|
+
if (cat === "Security" && total > shown) {
|
|
401
|
+
const restCount = total - shown;
|
|
402
|
+
parts.push('<details class="dsh-upd-changelog-security-more"><summary>\u5176\u4F59 ' + String(restCount) + ' \u6761</summary><div class="dsh-upd-changelog-more-note">\u4E3A\u4FDD\u6301\u9762\u677F\u6027\u80FD\uFF0C\u5176\u4F59\u6761\u76EE\u5DF2\u6298\u53E0\uFF0C\u53EF\u67E5\u770B\u539F\u6587\u3002</div></details>');
|
|
214
403
|
}
|
|
215
|
-
parts.push("</
|
|
404
|
+
parts.push("</div>");
|
|
216
405
|
}
|
|
217
406
|
for (const cat of CHANGELOG_FOLDED) {
|
|
218
|
-
const items = Array.isArray(entry.sections
|
|
219
|
-
if (items.length === 0) continue;
|
|
220
|
-
const label =
|
|
221
|
-
parts.push(
|
|
222
|
-
parts.push(
|
|
407
|
+
const items = Array.isArray(entry.sections ? entry.sections[cat] : null) ? entry.sections[cat] : [];
|
|
408
|
+
if (!items || items.length === 0) continue;
|
|
409
|
+
const label = cat + " \xB7 " + CHANGELOG_CATEGORY_ZH[cat] + "\uFF08" + String(items.length) + "\uFF09";
|
|
410
|
+
parts.push('<details class="dsh-upd-changelog-fold" data-cat="' + cat + '">');
|
|
411
|
+
parts.push("<summary>" + escapeChangelogHtml(label) + "</summary>");
|
|
412
|
+
const total = counts[cat];
|
|
413
|
+
const shown = items.length;
|
|
414
|
+
if (total > shown) {
|
|
415
|
+
parts.push(truncatedNoteHTML(total, shown));
|
|
416
|
+
}
|
|
223
417
|
parts.push("<ul>");
|
|
224
418
|
for (const item of items) {
|
|
225
419
|
if (!item) continue;
|
|
226
|
-
|
|
420
|
+
const li = renderChangelogItem(item);
|
|
421
|
+
if (li) parts.push(li);
|
|
227
422
|
}
|
|
228
423
|
parts.push("</ul></details>");
|
|
229
424
|
}
|
|
230
425
|
parts.push("</div>");
|
|
231
|
-
return parts.join(
|
|
426
|
+
return parts.join(String.fromCharCode(10));
|
|
232
427
|
} catch {
|
|
233
428
|
return "";
|
|
234
429
|
}
|
|
@@ -264,7 +459,165 @@ ${blocks.join("\n")}
|
|
|
264
459
|
return renderChangelogNeutral();
|
|
265
460
|
}
|
|
266
461
|
}
|
|
462
|
+
const CHANGELOG_VALIDATE_HINTS = {
|
|
463
|
+
E_VERSION_TITLE: "\u8BE5\u6807\u9898\u4E0D\u662F\u5408\u6CD5\u7248\u672C\u53F7\uFF0C\u5176\u4E0B\u6761\u76EE\u5DF2\u88AB\u5FFD\u7565\uFF0C\u8BF7\u6539\u6210 ## [x.y.z] \u5F62\u6001",
|
|
464
|
+
E_CATEGORY: "\u672A\u77E5\u5206\u7C7B\uFF0C\u5176\u4E0B\u6761\u76EE\u5DF2\u88AB\u5FFD\u7565\uFF0C\u4EC5\u6536 Added/Fixed/Changed/Deprecated/Removed/Security",
|
|
465
|
+
E_BULLET_ORPHAN: "\u8BE5\u6761\u76EE\u4E0D\u5728\u7248\u672C\u8282\u4E0E\u5206\u7C7B\u4E0B\uFF0C\u5DF2\u88AB\u5FFD\u7565",
|
|
466
|
+
W_BREAKING_MAYBE: "\u7591\u4F3C\u7834\u574F\u6807\u8BB0\u8BEF\u5199\uFF08\u7F3A\u5192\u53F7/\u62FC\u5199\u63A5\u8FD1/\u4F4D\u7F6E\u7591\u4F3C\u653E\u9519\uFF09\uFF0C\u8BF7\u68C0\u67E5\u662F\u5426\u60F3\u5199 BREAKING:/\u4E0D\u517C\u5BB9:",
|
|
467
|
+
W_YANKED: "\u8BE5\u7248\u672C\u5DF2\u88AB\u6807\u8BB0\u64A4\u56DE\uFF08yanked\uFF09\uFF0C\u5B89\u88C5\u4E0D\u53D7\u5F71\u54CD\uFF0C\u7EE7\u7EED\u524D\u8BF7\u786E\u8BA4",
|
|
468
|
+
W_TRUNCATED: "\u8BE5\u7C7B\u5171 M \u6761\uFF0C\u4EC5\u663E\u793A\u524D N \u6761",
|
|
469
|
+
W_TRUNCATED_FILE: "\u6587\u4EF6\u8D85 64K\uFF0C\u4EC5\u6821\u9A8C\u524D 64K",
|
|
470
|
+
W_EMPTY: "\u672A\u89E3\u6790\u5230\u4EFB\u4F55\u7248\u672C\u8282\uFF0C\u9762\u677F\u5C06\u663E\u793A\u4E2D\u6027\u63D0\u793A"
|
|
471
|
+
};
|
|
472
|
+
function isMaybeBreakingMisuse(bulletBody) {
|
|
473
|
+
try {
|
|
474
|
+
if (typeof bulletBody !== "string") return false;
|
|
475
|
+
const t = stripBreakingDecorations(bulletBody);
|
|
476
|
+
if (!t) return false;
|
|
477
|
+
if (isBreakingChangelogItem(bulletBody)) return false;
|
|
478
|
+
if (/^breaking\s*$/i.test(t)) return true;
|
|
479
|
+
if (/^breaking\s+[^::]/i.test(t)) return true;
|
|
480
|
+
if (/^breaking[^::\s\w]/i.test(t)) return true;
|
|
481
|
+
if (/^不兼容\s*$/i.test(t)) return true;
|
|
482
|
+
if (/^不兼容\s*[^::]/i.test(t)) return true;
|
|
483
|
+
if (/^(braking|breking|breakign|brekaing|braeking|breakingg)\b/i.test(t)) return true;
|
|
484
|
+
if (/^break[^i::\s]/i.test(t)) return true;
|
|
485
|
+
return false;
|
|
486
|
+
} catch {
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
function validateChangelog(markdown) {
|
|
491
|
+
try {
|
|
492
|
+
if (typeof markdown !== "string" || !markdown.trim()) {
|
|
493
|
+
return { ok: true, diagnostics: [{ line: 1, code: "W_EMPTY", hint: CHANGELOG_VALIDATE_HINTS.W_EMPTY }] };
|
|
494
|
+
}
|
|
495
|
+
let raw = String(markdown).replace(/\r\n/g, String.fromCharCode(10));
|
|
496
|
+
let fileTruncated = false;
|
|
497
|
+
if (raw.length > CHANGELOG_MAX_CHARS) {
|
|
498
|
+
fileTruncated = true;
|
|
499
|
+
raw = raw.slice(0, CHANGELOG_MAX_CHARS);
|
|
500
|
+
}
|
|
501
|
+
const lines = raw.split(String.fromCharCode(10));
|
|
502
|
+
const diags = [];
|
|
503
|
+
let inFence = false;
|
|
504
|
+
let curValid = false;
|
|
505
|
+
let curCat = null;
|
|
506
|
+
let catLineByCat = {};
|
|
507
|
+
let countByCat = {};
|
|
508
|
+
let hasVisibleVersion = false;
|
|
509
|
+
const flushCounts = function() {
|
|
510
|
+
try {
|
|
511
|
+
for (const k of Object.keys(countByCat)) {
|
|
512
|
+
const n = countByCat[k];
|
|
513
|
+
if (n > CHANGELOG_MAX_BULLETS_PER_SECTION) {
|
|
514
|
+
const lineNo = catLineByCat[k] ? catLineByCat[k] : lines.length;
|
|
515
|
+
const hint = "\u5206\u7C7B " + k + " \u5171 " + String(n) + " \u6761\uFF0C\u4EC5\u663E\u793A\u524D " + String(CHANGELOG_MAX_BULLETS_PER_SECTION) + " \u6761";
|
|
516
|
+
diags.push({ line: lineNo, code: "W_TRUNCATED", hint });
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
} catch {
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
for (let i = 0; i < lines.length; i++) {
|
|
523
|
+
const lineNo = i + 1;
|
|
524
|
+
const line = String(lines[i] !== void 0 && lines[i] !== null ? lines[i] : "");
|
|
525
|
+
if (isFenceToggle(line)) {
|
|
526
|
+
inFence = !inFence;
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
if (inFence) continue;
|
|
530
|
+
const vTitle = matchVersionHeading(line);
|
|
531
|
+
if (vTitle !== null) {
|
|
532
|
+
flushCounts();
|
|
533
|
+
catLineByCat = {};
|
|
534
|
+
countByCat = {};
|
|
535
|
+
curValid = false;
|
|
536
|
+
curCat = null;
|
|
537
|
+
const info = extractVersionDateAndYanked(vTitle);
|
|
538
|
+
if (!info.version) {
|
|
539
|
+
diags.push({ line: lineNo, code: "E_VERSION_TITLE", hint: CHANGELOG_VALIDATE_HINTS.E_VERSION_TITLE });
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
if (isUnreleasedVersion(info.version)) {
|
|
543
|
+
curValid = false;
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
curValid = true;
|
|
547
|
+
hasVisibleVersion = true;
|
|
548
|
+
if (info.yanked) {
|
|
549
|
+
diags.push({ line: lineNo, code: "W_YANKED", hint: CHANGELOG_VALIDATE_HINTS.W_YANKED });
|
|
550
|
+
}
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
553
|
+
const cTitle = matchCategoryHeading(line);
|
|
554
|
+
if (cTitle !== null) {
|
|
555
|
+
const cat = normalizeCategory(cTitle);
|
|
556
|
+
if (!cat) {
|
|
557
|
+
curCat = null;
|
|
558
|
+
diags.push({ line: lineNo, code: "E_CATEGORY", hint: CHANGELOG_VALIDATE_HINTS.E_CATEGORY });
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
if (!curValid) {
|
|
562
|
+
curCat = null;
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
curCat = cat;
|
|
566
|
+
catLineByCat[cat] = lineNo;
|
|
567
|
+
if (countByCat[cat] === void 0) countByCat[cat] = 0;
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
const body = matchBulletBody(line);
|
|
571
|
+
if (body !== null) {
|
|
572
|
+
if (!curValid || !curCat) {
|
|
573
|
+
diags.push({ line: lineNo, code: "E_BULLET_ORPHAN", hint: CHANGELOG_VALIDATE_HINTS.E_BULLET_ORPHAN });
|
|
574
|
+
continue;
|
|
575
|
+
}
|
|
576
|
+
countByCat[curCat] = (countByCat[curCat] ? countByCat[curCat] : 0) + 1;
|
|
577
|
+
if (isMaybeBreakingMisuse(body)) {
|
|
578
|
+
diags.push({ line: lineNo, code: "W_BREAKING_MAYBE", hint: CHANGELOG_VALIDATE_HINTS.W_BREAKING_MAYBE });
|
|
579
|
+
}
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
flushCounts();
|
|
584
|
+
if (fileTruncated) {
|
|
585
|
+
diags.push({ line: lines.length, code: "W_TRUNCATED_FILE", hint: CHANGELOG_VALIDATE_HINTS.W_TRUNCATED_FILE });
|
|
586
|
+
}
|
|
587
|
+
if (!hasVisibleVersion) {
|
|
588
|
+
let anyVisible = false;
|
|
589
|
+
try {
|
|
590
|
+
const parsed = parseChangelog(markdown);
|
|
591
|
+
for (const e of parsed) {
|
|
592
|
+
if (e && !isUnreleasedVersion(e.version) && hasVisibleSections(e)) {
|
|
593
|
+
anyVisible = true;
|
|
594
|
+
break;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
} catch {
|
|
598
|
+
anyVisible = false;
|
|
599
|
+
}
|
|
600
|
+
if (!anyVisible) {
|
|
601
|
+
diags.push({ line: lines.length, code: "W_EMPTY", hint: CHANGELOG_VALIDATE_HINTS.W_EMPTY });
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
diags.sort(function(a, b) {
|
|
605
|
+
return a.line - b.line;
|
|
606
|
+
});
|
|
607
|
+
let ok = true;
|
|
608
|
+
for (const d of diags) {
|
|
609
|
+
if (d.code.charAt(0) === "E") {
|
|
610
|
+
ok = false;
|
|
611
|
+
break;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return { ok, diagnostics: diags };
|
|
615
|
+
} catch {
|
|
616
|
+
return { ok: false, diagnostics: [] };
|
|
617
|
+
}
|
|
618
|
+
}
|
|
267
619
|
export {
|
|
620
|
+
BREAKING_BADGE_TEXT,
|
|
268
621
|
CHANGELOG_ALL_CATEGORIES,
|
|
269
622
|
CHANGELOG_CATEGORY_ZH,
|
|
270
623
|
CHANGELOG_FILENAME,
|
|
@@ -276,12 +629,36 @@ export {
|
|
|
276
629
|
CHANGELOG_MUST_SHOW,
|
|
277
630
|
CHANGELOG_NEUTRAL_HINT,
|
|
278
631
|
CHANGELOG_NEUTRAL_LINE,
|
|
632
|
+
CHANGELOG_POLL_BACKOFF_MS,
|
|
633
|
+
CHANGELOG_VALIDATE_HINTS,
|
|
634
|
+
RE_BULLET,
|
|
635
|
+
RE_CATEGORY_HEADING,
|
|
636
|
+
RE_FENCE_TOGGLE,
|
|
637
|
+
RE_UNRELEASED,
|
|
638
|
+
RE_VERSION_DATE,
|
|
639
|
+
RE_VERSION_HEADING,
|
|
640
|
+
RE_VERSION_NUM,
|
|
641
|
+
RE_YANKED,
|
|
279
642
|
changelogForUpdate,
|
|
643
|
+
countsOf,
|
|
280
644
|
hasVisibleSections,
|
|
645
|
+
isBreakingChangelogItem,
|
|
646
|
+
isFenceToggle,
|
|
647
|
+
isMaybeBreakingMisuse,
|
|
281
648
|
isUnreleasedVersion,
|
|
649
|
+
matchBulletBody,
|
|
650
|
+
matchCategoryHeading,
|
|
651
|
+
matchVersionHeading,
|
|
282
652
|
parseChangelog,
|
|
283
653
|
renderChangelogHTML,
|
|
654
|
+
renderChangelogItem,
|
|
284
655
|
renderChangelogNeutral,
|
|
285
656
|
renderChangelogSection,
|
|
286
|
-
selectChangelogEntries
|
|
657
|
+
selectChangelogEntries,
|
|
658
|
+
shouldFetchChangelog,
|
|
659
|
+
splitBreakingPrefix,
|
|
660
|
+
stripBreakingDecorations,
|
|
661
|
+
truncatedNoteHTML,
|
|
662
|
+
validateChangelog,
|
|
663
|
+
yankedBannerHTML
|
|
287
664
|
};
|
package/dist/entry.d.ts
CHANGED
|
@@ -5,7 +5,11 @@ export type EntryVariant = 'button' | 'badge' | 'inline';
|
|
|
5
5
|
/** 什么时候主动查一次(只读,联网一次)。 */
|
|
6
6
|
export type EntryAutoCheck = 'mount' | 'never';
|
|
7
7
|
/** 用户点下去之后做什么。 */
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* 点下去之后做什么:`has-update` 有新版才开面板(无事只给一句小字);`always` 检查完总是开;
|
|
10
|
+
* `manual` 交给 onActivate;`direct` 点开即弹窗、不预查(面板挂载即自查;badge 仍走回调口径)。
|
|
11
|
+
*/
|
|
12
|
+
export type EntryOpenOn = 'has-update' | 'always' | 'manual' | 'direct';
|
|
9
13
|
/** 入口件主题(与面板同一套取值,切换即换肤)。 */
|
|
10
14
|
export type EntryTheme = UpdatePanelTheme;
|
|
11
15
|
/** 入口件的状态档:只用来算文案与 data-state,不另立状态机。 */
|
package/dist/entry.js
CHANGED
|
@@ -64,10 +64,15 @@ const UPDATE_ENTRY_CSS = [
|
|
|
64
64
|
'.dsh-upd-entry[data-state="update"] .dsh-upd-entry-dot{background:var(--dsh-upd-ok-line,#059669)}',
|
|
65
65
|
'.dsh-upd-entry[data-state="busy"] .dsh-upd-entry-dot,.dsh-upd-entry[data-state="restart"] .dsh-upd-entry-dot{background:var(--dsh-upd-warn-line,#d97706)}',
|
|
66
66
|
'.dsh-upd-entry[data-state="failed"] .dsh-upd-entry-dot{background:var(--dsh-upd-bad-line,#dc2626)}',
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
'.dsh-upd-entry[data-theme="d5-paper"] .dsh-upd-entry-
|
|
70
|
-
'.dsh-upd-entry[data-theme="d5-paper"]
|
|
67
|
+
// 小字自带底(深色宿主 + 浅色变量时也读得出;浅底宿主上只是多一圈细线,不抢戏)。
|
|
68
|
+
".dsh-upd-entry-note{font-size:12.5px;opacity:.9;background:var(--dsh-upd-bg,#ffffff);border:1px solid var(--dsh-upd-line,#e5e7eb);border-radius:4px;padding:1px 8px}",
|
|
69
|
+
'.dsh-upd-entry[data-theme="d5-paper"] .dsh-upd-entry-note{background:var(--d5-card);border-color:var(--d5-line-strong);color:var(--d5-ink)}',
|
|
70
|
+
'.dsh-upd-entry[data-theme="d5-paper"]{--d5-ink:#1a1a1a;--d5-muted:#6f675a;--d5-line-strong:#c4b896;--d5-accent:#c8402a;--d5-card:#fffdf6;',
|
|
71
|
+
'font-family:Georgia,"Songti SC","STSong","SimSun",serif;color:var(--d5-ink)}',
|
|
72
|
+
// 按钮脸自己不透明(深色宿主 + 浅色系统变量时也读得出;hover 红在深浅底上都可见)。
|
|
73
|
+
'.dsh-upd-entry[data-theme="d5-paper"] .dsh-upd-entry-btn{border-color:var(--d5-line-strong);background:var(--d5-card);color:var(--d5-ink);border-radius:3px}',
|
|
74
|
+
'.dsh-upd-entry[data-theme="d5-paper"] .dsh-upd-entry-btn:hover{border-color:var(--d5-accent);color:var(--d5-accent)}',
|
|
75
|
+
'@media (prefers-color-scheme: dark){.dsh-upd-entry[data-theme="d5-paper"]{--d5-ink:#ece5d3;--d5-muted:#a89c83;--d5-line-strong:#5c4e3b;--d5-accent:#e0684e;--d5-card:#1e1a15}}',
|
|
71
76
|
"@media (prefers-color-scheme: dark){.dsh-upd-entry{color:#e5e7eb}",
|
|
72
77
|
".dsh-upd-entry-btn{--dsh-upd-btn:#1f2937;--dsh-upd-line:#374151}}"
|
|
73
78
|
].join("\n");
|
|
@@ -111,8 +116,8 @@ function mountUpdateEntry(container, options) {
|
|
|
111
116
|
throw new Error(`[dsh-plugin-update] \u81EA\u52A8\u68C0\u67E5\u65F6\u673A\u975E\u6CD5\uFF1A\u53EA\u6536 mount \u6216 never\uFF08\u6536\u5230 ${JSON.stringify(options.autoCheck)}\uFF09`);
|
|
112
117
|
}
|
|
113
118
|
const openOn = options.openOn ?? "has-update";
|
|
114
|
-
if (openOn !== "has-update" && openOn !== "always" && openOn !== "manual") {
|
|
115
|
-
throw new Error(`[dsh-plugin-update] \u70B9\u51FB\u53BB\u5411\u975E\u6CD5\uFF1A\u53EA\u6536 has-update / always / manual\uFF08\u6536\u5230 ${JSON.stringify(options.openOn)}\uFF09`);
|
|
119
|
+
if (openOn !== "has-update" && openOn !== "always" && openOn !== "manual" && openOn !== "direct") {
|
|
120
|
+
throw new Error(`[dsh-plugin-update] \u70B9\u51FB\u53BB\u5411\u975E\u6CD5\uFF1A\u53EA\u6536 has-update / always / manual / direct\uFF08\u6536\u5230 ${JSON.stringify(options.openOn)}\uFF09`);
|
|
116
121
|
}
|
|
117
122
|
if (options.theme !== void 0 && options.theme !== "default" && options.theme !== "archive" && options.theme !== "d5-paper") {
|
|
118
123
|
throw new Error(`[dsh-plugin-update] \u4E3B\u9898\u975E\u6CD5\uFF1A\u53EA\u6536 default \u6216 archive\uFF08d5-paper \u4E3A\u65E7\u522B\u540D\u4ECD\u53EF\u7528\uFF09\uFF08\u6536\u5230 ${JSON.stringify(options.theme)}\uFF09`);
|
|
@@ -248,6 +253,10 @@ function mountUpdateEntry(container, options) {
|
|
|
248
253
|
}
|
|
249
254
|
async function activate() {
|
|
250
255
|
if (!mounted || panelMode !== null || activating) return;
|
|
256
|
+
if (openOn === "direct" && variant === "button") {
|
|
257
|
+
openDialog();
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
251
260
|
activating = true;
|
|
252
261
|
note = null;
|
|
253
262
|
render();
|
|
@@ -288,7 +297,6 @@ function mountUpdateEntry(container, options) {
|
|
|
288
297
|
void activate();
|
|
289
298
|
return;
|
|
290
299
|
}
|
|
291
|
-
if (panelMode === "dialog" && closest('[data-action="close-view"]')) close();
|
|
292
300
|
} catch {
|
|
293
301
|
}
|
|
294
302
|
}
|
package/dist/host.d.ts
CHANGED
|
@@ -10,8 +10,8 @@ export { COPY_BUDGET_CHARS, DETAIL_MAX_CHARS, DIAG_DROP_ORDER, DIAG_INLINE_BUDGE
|
|
|
10
10
|
export type { DiagDropKey, RedactionRuleName } from './redaction.js';
|
|
11
11
|
export { DIAG_ACTIONS, DIAG_KEYS, DIAG_KNOWN_TYPES, DIAG_STAGE_EXPECT, DIAG_STAGES, DIAG_VERSION, buildDiag, deriveAction, deriveRouteMethod, deriveStage, enforceBudget as enforceDiagBudgetForPhone, } from './diag.js';
|
|
12
12
|
export type { DiagAction, DiagInput, DiagObject, DiagStage } from './diag.js';
|
|
13
|
-
export { CHANGELOG_ALL_CATEGORIES, CHANGELOG_FILENAME, CHANGELOG_FOLDED, CHANGELOG_MAX_BULLETS_PER_SECTION, CHANGELOG_MAX_BULLET_CHARS, CHANGELOG_MAX_CHARS, CHANGELOG_MAX_ENTRIES, CHANGELOG_MUST_SHOW, CHANGELOG_NEUTRAL_HINT, CHANGELOG_NEUTRAL_LINE, changelogForUpdate, hasVisibleSections, isUnreleasedVersion, parseChangelog, renderChangelogHTML, renderChangelogNeutral, renderChangelogSection, selectChangelogEntries, } from './changelog.js';
|
|
14
|
-
export type { ChangelogCategory, ChangelogEntry } from './changelog.js';
|
|
13
|
+
export { CHANGELOG_ALL_CATEGORIES, CHANGELOG_FILENAME, CHANGELOG_FOLDED, CHANGELOG_MAX_BULLETS_PER_SECTION, CHANGELOG_MAX_BULLET_CHARS, CHANGELOG_MAX_CHARS, CHANGELOG_MAX_ENTRIES, CHANGELOG_MUST_SHOW, CHANGELOG_NEUTRAL_HINT, CHANGELOG_NEUTRAL_LINE, BREAKING_BADGE_TEXT, CHANGELOG_POLL_BACKOFF_MS, changelogForUpdate, countsOf, hasVisibleSections, isBreakingChangelogItem, isUnreleasedVersion, parseChangelog, renderChangelogHTML, renderChangelogItem, renderChangelogNeutral, renderChangelogSection, selectChangelogEntries, shouldFetchChangelog, splitBreakingPrefix, truncatedNoteHTML, validateChangelog, yankedBannerHTML, } from './changelog.js';
|
|
14
|
+
export type { ChangelogCategory, ChangelogDiagnostic, ChangelogEntry, ChangelogValidation } from './changelog.js';
|
|
15
15
|
export { CHANGELOG_DEFAULT_REGISTRY, CHANGELOG_FETCH_MAX_BYTES, CHANGELOG_TAR_MAX_BYTES, CHANGELOG_TARBALL_MAX_BYTES, extractChangelogFromTar, fetchReleaseChangelogText, readInstalledChangelogText, } from './changelog-io.js';
|
|
16
16
|
export type { ReleaseChangelogRef } from './changelog-io.js';
|
|
17
17
|
export { createUpdateDiskPorts, createUpdateExecutor, pathsForUpdate, resolveCliEntry } from './store.js';
|
package/dist/host.js
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from "./config.js";
|
|
12
12
|
import { manualCommand } from "./commands.js";
|
|
13
13
|
import { fetchReleaseChangelogText } from "./changelog-io.js";
|
|
14
|
+
import { shouldFetchChangelog } from "./changelog.js";
|
|
14
15
|
import { buildDiag } from "./diag.js";
|
|
15
16
|
import { fetchNpmVersionRelease, isVersionAllowedInChannel, updateError, validReleaseVersion, validRequestId } from "./service.js";
|
|
16
17
|
import { createUpdateDiskPorts, createUpdateExecutor, createUpdateQueuePorts } from "./store.js";
|
|
@@ -73,14 +74,24 @@ import {
|
|
|
73
74
|
CHANGELOG_MUST_SHOW,
|
|
74
75
|
CHANGELOG_NEUTRAL_HINT,
|
|
75
76
|
CHANGELOG_NEUTRAL_LINE,
|
|
77
|
+
BREAKING_BADGE_TEXT,
|
|
78
|
+
CHANGELOG_POLL_BACKOFF_MS,
|
|
76
79
|
changelogForUpdate,
|
|
80
|
+
countsOf,
|
|
77
81
|
hasVisibleSections,
|
|
82
|
+
isBreakingChangelogItem,
|
|
78
83
|
isUnreleasedVersion,
|
|
79
84
|
parseChangelog,
|
|
80
85
|
renderChangelogHTML,
|
|
86
|
+
renderChangelogItem,
|
|
81
87
|
renderChangelogNeutral,
|
|
82
88
|
renderChangelogSection,
|
|
83
|
-
selectChangelogEntries
|
|
89
|
+
selectChangelogEntries,
|
|
90
|
+
shouldFetchChangelog as shouldFetchChangelog2,
|
|
91
|
+
splitBreakingPrefix,
|
|
92
|
+
truncatedNoteHTML,
|
|
93
|
+
validateChangelog,
|
|
94
|
+
yankedBannerHTML
|
|
84
95
|
} from "./changelog.js";
|
|
85
96
|
import {
|
|
86
97
|
CHANGELOG_DEFAULT_REGISTRY,
|
|
@@ -578,12 +589,24 @@ function createHostUpdate(deps = {}, configInput) {
|
|
|
578
589
|
const readerOverrides = { ...deps.readerOverrides ?? {}, ctx, ...deps.pluginManager ? { pluginManager: deps.pluginManager } : {} };
|
|
579
590
|
const phoneNames = buildPhoneNames(config.prefix);
|
|
580
591
|
const changelogCache = /* @__PURE__ */ new Map();
|
|
592
|
+
const changelogFailedAt = /* @__PURE__ */ new Map();
|
|
581
593
|
async function readChangelog(args) {
|
|
582
594
|
const raw = args ? args["version"] : void 0;
|
|
583
595
|
if (!validReleaseVersion(raw)) throw updateError("invalid-release");
|
|
584
596
|
const version = raw;
|
|
585
597
|
if (!isVersionAllowedInChannel(version, config.releaseChannel)) throw updateError("invalid-release");
|
|
586
598
|
if (changelogCache.has(version)) return { version, markdown: changelogCache.get(version) ?? null };
|
|
599
|
+
try {
|
|
600
|
+
let nowMs = 0;
|
|
601
|
+
try {
|
|
602
|
+
nowMs = Date.now();
|
|
603
|
+
} catch {
|
|
604
|
+
nowMs = 0;
|
|
605
|
+
}
|
|
606
|
+
const failedAt = changelogFailedAt.has(version) ? changelogFailedAt.get(version) : null;
|
|
607
|
+
void shouldFetchChangelog({ hasCache: false, failedAt, now: nowMs, isManual: true });
|
|
608
|
+
} catch {
|
|
609
|
+
}
|
|
587
610
|
const fetchImpl = readerOverrides.fetchImpl ?? globalThis.fetch;
|
|
588
611
|
const release = await fetchNpmVersionRelease(fetchImpl, config.checkTimeoutMs, {
|
|
589
612
|
targetPackageName: config.targetPackageName,
|
|
@@ -591,12 +614,29 @@ function createHostUpdate(deps = {}, configInput) {
|
|
|
591
614
|
releaseChannel: config.releaseChannel,
|
|
592
615
|
version
|
|
593
616
|
});
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
617
|
+
let markdown;
|
|
618
|
+
try {
|
|
619
|
+
markdown = await fetchReleaseChangelogText(
|
|
620
|
+
fetchImpl,
|
|
621
|
+
{ tarball: release.tarball, integrity: release.integrity, version },
|
|
622
|
+
{ targetPackageName: config.targetPackageName, registryUrl: config.registryUrl, timeoutMs: config.checkTimeoutMs }
|
|
623
|
+
);
|
|
624
|
+
} catch (err) {
|
|
625
|
+
try {
|
|
626
|
+
changelogFailedAt.set(version, Date.now());
|
|
627
|
+
} catch {
|
|
628
|
+
try {
|
|
629
|
+
changelogFailedAt.set(version, 0);
|
|
630
|
+
} catch {
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
throw err;
|
|
634
|
+
}
|
|
599
635
|
changelogCache.set(version, markdown);
|
|
636
|
+
try {
|
|
637
|
+
changelogFailedAt.delete(version);
|
|
638
|
+
} catch {
|
|
639
|
+
}
|
|
600
640
|
return { version, markdown };
|
|
601
641
|
}
|
|
602
642
|
async function readStatus(args) {
|
|
@@ -713,6 +753,7 @@ function __resetSharedUpdateReaderForTests() {
|
|
|
713
753
|
sharedDesktopPnpm = null;
|
|
714
754
|
}
|
|
715
755
|
export {
|
|
756
|
+
BREAKING_BADGE_TEXT,
|
|
716
757
|
CHANGELOG_ALL_CATEGORIES,
|
|
717
758
|
CHANGELOG_DEFAULT_REGISTRY,
|
|
718
759
|
CHANGELOG_FETCH_MAX_BYTES,
|
|
@@ -725,6 +766,7 @@ export {
|
|
|
725
766
|
CHANGELOG_MUST_SHOW,
|
|
726
767
|
CHANGELOG_NEUTRAL_HINT,
|
|
727
768
|
CHANGELOG_NEUTRAL_LINE,
|
|
769
|
+
CHANGELOG_POLL_BACKOFF_MS,
|
|
728
770
|
CHANGELOG_TARBALL_MAX_BYTES,
|
|
729
771
|
CHANGELOG_TAR_MAX_BYTES,
|
|
730
772
|
COPY_BUDGET_CHARS,
|
|
@@ -764,6 +806,7 @@ export {
|
|
|
764
806
|
compareReleaseVersions,
|
|
765
807
|
compareVersions,
|
|
766
808
|
containingPackage,
|
|
809
|
+
countsOf,
|
|
767
810
|
createHostUpdate,
|
|
768
811
|
createSkipDiskPorts,
|
|
769
812
|
createUpdateDiskPorts2 as createUpdateDiskPorts,
|
|
@@ -782,6 +825,7 @@ export {
|
|
|
782
825
|
extractChangelogFromTar,
|
|
783
826
|
fetchReleaseChangelogText2 as fetchReleaseChangelogText,
|
|
784
827
|
hasVisibleSections,
|
|
828
|
+
isBreakingChangelogItem,
|
|
785
829
|
isHeadOfQueue2 as isHeadOfQueue,
|
|
786
830
|
isPrereleaseVersion,
|
|
787
831
|
isUnreleasedVersion,
|
|
@@ -800,6 +844,7 @@ export {
|
|
|
800
844
|
registrySpec,
|
|
801
845
|
releaseOwnerInQueue2 as releaseOwnerInQueue,
|
|
802
846
|
renderChangelogHTML,
|
|
847
|
+
renderChangelogItem,
|
|
803
848
|
renderChangelogNeutral,
|
|
804
849
|
renderChangelogSection,
|
|
805
850
|
resolveCliEntry,
|
|
@@ -810,9 +855,14 @@ export {
|
|
|
810
855
|
sanitizeForCopy,
|
|
811
856
|
selectChangelogEntries,
|
|
812
857
|
setOwnerIfFree2 as setOwnerIfFree,
|
|
858
|
+
shouldFetchChangelog2 as shouldFetchChangelog,
|
|
813
859
|
skipKey,
|
|
860
|
+
splitBreakingPrefix,
|
|
814
861
|
truncateToWordBoundary,
|
|
862
|
+
truncatedNoteHTML,
|
|
815
863
|
validReleaseVersion2 as validReleaseVersion,
|
|
816
864
|
validVersion,
|
|
817
|
-
|
|
865
|
+
validateChangelog,
|
|
866
|
+
visibleQueueFor2 as visibleQueueFor,
|
|
867
|
+
yankedBannerHTML
|
|
818
868
|
};
|
package/dist/panel-batch.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/panel-batch.ts
|
|
2
2
|
import { assertPrefix, MIN_PANEL_POLL_MS } from "./config.js";
|
|
3
3
|
import { validReleaseVersion } from "./service.js";
|
|
4
|
+
import { shouldFetchChangelog } from "./changelog.js";
|
|
4
5
|
import {
|
|
5
6
|
isTerminalPhase,
|
|
6
7
|
normalizeBatchSession
|
|
@@ -631,7 +632,7 @@ function mountUpdateBatchPanel(container, options) {
|
|
|
631
632
|
const autoChangelogEnabled = options.autoChangelog !== false;
|
|
632
633
|
const rowChangelog = /* @__PURE__ */ new Map();
|
|
633
634
|
const rowInflight = /* @__PURE__ */ new Set();
|
|
634
|
-
const
|
|
635
|
+
const rowFailedAt = /* @__PURE__ */ new Map();
|
|
635
636
|
let rowAutoSeq = 0;
|
|
636
637
|
let rows = [];
|
|
637
638
|
let expandedKey = null;
|
|
@@ -672,7 +673,19 @@ function mountUpdateBatchPanel(container, options) {
|
|
|
672
673
|
const cached = rowChangelog.get(key);
|
|
673
674
|
if (cached && cached.version === version) return;
|
|
674
675
|
if (rowInflight.has(key)) return;
|
|
675
|
-
|
|
676
|
+
try {
|
|
677
|
+
const failKey = key + "\0" + version;
|
|
678
|
+
const hasCache = false;
|
|
679
|
+
const failedAt = rowFailedAt.has(failKey) ? rowFailedAt.get(failKey) : null;
|
|
680
|
+
let nowMs = 0;
|
|
681
|
+
try {
|
|
682
|
+
nowMs = Date.now();
|
|
683
|
+
} catch {
|
|
684
|
+
nowMs = 0;
|
|
685
|
+
}
|
|
686
|
+
if (!shouldFetchChangelog({ hasCache, failedAt, now: nowMs, isManual: false })) return;
|
|
687
|
+
} catch {
|
|
688
|
+
}
|
|
676
689
|
const phone2 = changelogPhoneOf(row);
|
|
677
690
|
if (!phone2) return;
|
|
678
691
|
rowInflight.add(key);
|
|
@@ -682,7 +695,14 @@ function mountUpdateBatchPanel(container, options) {
|
|
|
682
695
|
rowInflight.delete(key);
|
|
683
696
|
if (!mounted || seq !== rowAutoSeq) return;
|
|
684
697
|
if (!isObject(reply) || reply["ok"] !== true) {
|
|
685
|
-
|
|
698
|
+
try {
|
|
699
|
+
rowFailedAt.set(key + "\0" + version, Date.now());
|
|
700
|
+
} catch {
|
|
701
|
+
try {
|
|
702
|
+
rowFailedAt.set(key + "\0" + version, 0);
|
|
703
|
+
} catch {
|
|
704
|
+
}
|
|
705
|
+
}
|
|
686
706
|
return;
|
|
687
707
|
}
|
|
688
708
|
const md = reply["markdown"];
|
|
@@ -691,7 +711,16 @@ function mountUpdateBatchPanel(container, options) {
|
|
|
691
711
|
},
|
|
692
712
|
() => {
|
|
693
713
|
rowInflight.delete(key);
|
|
694
|
-
if (mounted && seq === rowAutoSeq)
|
|
714
|
+
if (mounted && seq === rowAutoSeq) {
|
|
715
|
+
try {
|
|
716
|
+
rowFailedAt.set(key + "\0" + version, Date.now());
|
|
717
|
+
} catch {
|
|
718
|
+
try {
|
|
719
|
+
rowFailedAt.set(key + "\0" + version, 0);
|
|
720
|
+
} catch {
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
695
724
|
}
|
|
696
725
|
);
|
|
697
726
|
}
|
|
@@ -808,7 +837,7 @@ function mountUpdateBatchPanel(container, options) {
|
|
|
808
837
|
return;
|
|
809
838
|
}
|
|
810
839
|
applyReply(reply);
|
|
811
|
-
|
|
840
|
+
rowFailedAt.clear();
|
|
812
841
|
} catch {
|
|
813
842
|
if (!mounted) return;
|
|
814
843
|
lastError = "check-failed";
|
package/dist/panel.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { type VisibleQueue } from './queue.js';
|
|
|
2
2
|
import type { BlockedReason, UpdateSnapshot } from './ports.js';
|
|
3
3
|
/** 摆放形态:默认内嵌,切弹窗走同一参数。 */
|
|
4
4
|
export type UpdatePanelMode = 'embedded' | 'dialog';
|
|
5
|
-
/** 面板主题:默认最小可用样式;`d5-paper`
|
|
5
|
+
/** 面板主题:默认最小可用样式;`archive` 为档案卷纸面浅色(只换肤,不换 DOM 顺序;`d5-paper` 为旧别名)。 */
|
|
6
6
|
/**
|
|
7
7
|
* 面板主题:`default` 最小可用深色;`archive` 档案卷纸面浅色(原型敲定的案卷风格,只换肤)。
|
|
8
8
|
* `d5-paper` 是 archive 的旧别名(历史取值),仍可用,渲染逐字相同。
|
|
@@ -26,7 +26,7 @@ export interface UpdatePanelOptions {
|
|
|
26
26
|
prefix?: string;
|
|
27
27
|
/** 摆放形态:默认内嵌。 */
|
|
28
28
|
mode?: UpdatePanelMode;
|
|
29
|
-
/** 面板主题:默认 `default`(最小可用样式,一字不动);传 `d5-paper`
|
|
29
|
+
/** 面板主题:默认 `default`(最小可用样式,一字不动);传 `archive` 切档案卷(`d5-paper` 为旧别名)。 */
|
|
30
30
|
theme?: UpdatePanelTheme;
|
|
31
31
|
/** 是否看他人排队明细:默认只看自己的(他人仅露正忙占位,位置照给)。 */
|
|
32
32
|
showOthers?: boolean;
|
package/dist/panel.js
CHANGED
|
@@ -5,7 +5,9 @@ import { validReleaseVersion } from "./service.js";
|
|
|
5
5
|
import {
|
|
6
6
|
changelogForUpdate,
|
|
7
7
|
parseChangelog,
|
|
8
|
-
renderChangelogHTML
|
|
8
|
+
renderChangelogHTML,
|
|
9
|
+
shouldFetchChangelog,
|
|
10
|
+
yankedBannerHTML
|
|
9
11
|
} from "./changelog.js";
|
|
10
12
|
function normalizePanelTheme(value) {
|
|
11
13
|
return value === "d5-paper" || value === "archive" ? "d5-paper" : "default";
|
|
@@ -516,6 +518,12 @@ const UPDATE_PANEL_CSS = [
|
|
|
516
518
|
".dsh-upd-changelog li{margin:2px 0}",
|
|
517
519
|
".dsh-upd-changelog-fold{margin:4px 0}",
|
|
518
520
|
".dsh-upd-changelog-fold>summary{cursor:pointer}",
|
|
521
|
+
".dsh-upd-changelog-count{font-size:12px;opacity:.7;margin:2px 0 4px}",
|
|
522
|
+
".dsh-upd-changelog-yanked{border-left:4px solid var(--dsh-upd-warn-line,#d97706);background:var(--dsh-upd-warn-bg,#fffbeb);padding:6px 10px;margin:6px 0;font-size:13px}",
|
|
523
|
+
".dsh-upd-breaking-badge{display:inline-block;font-size:11px;font-weight:700;border:1px solid currentColor;border-radius:3px;padding:0 5px;margin-right:6px;vertical-align:baseline}",
|
|
524
|
+
".dsh-upd-changelog-security-more{margin:4px 0 6px}",
|
|
525
|
+
".dsh-upd-changelog-security-more>summary{cursor:pointer;font-size:12px;opacity:.8}",
|
|
526
|
+
".dsh-upd-changelog-more-note{font-size:12px;opacity:.7;margin:2px 0 4px}",
|
|
519
527
|
".dsh-upd-changelog-neutral{color:inherit;opacity:.8}",
|
|
520
528
|
".dsh-upd-overlay{position:fixed;inset:0;background:rgba(0,0,0,.35);display:flex;align-items:center;justify-content:center;z-index:9999}",
|
|
521
529
|
".dsh-upd-overlay .dsh-upd{background:var(--dsh-upd-bg,#ffffff);max-height:85vh;overflow:auto}",
|
|
@@ -593,7 +601,8 @@ const UPDATE_PANEL_D5_CSS = [
|
|
|
593
601
|
"--d5-shadow:0 1px 2px rgba(60,40,20,.08),0 12px 32px rgba(60,40,20,.10);",
|
|
594
602
|
"font-family:var(--d5-sans);color:var(--d5-ink);background:var(--d5-card);",
|
|
595
603
|
"border:1px solid var(--d5-line-strong);border-radius:4px;box-shadow:var(--d5-shadow)}",
|
|
596
|
-
|
|
604
|
+
// 按钮脸自己不透明(宿主底色未知时也读得出;卡片上渲染与 transparent 逐字同色)。
|
|
605
|
+
'.dsh-upd[data-theme="d5-paper"] button{border-color:var(--d5-line-strong);background:var(--d5-card);color:var(--d5-ink);border-radius:3px;font-family:var(--d5-sans)}',
|
|
597
606
|
'.dsh-upd[data-theme="d5-paper"] button:hover:not(:disabled){border-color:var(--d5-accent);color:var(--d5-accent)}',
|
|
598
607
|
'.dsh-upd[data-theme="d5-paper"] button[data-primary="1"]{background:var(--d5-accent);border-color:var(--d5-accent);color:#fff}',
|
|
599
608
|
'.dsh-upd[data-theme="d5-paper"] button[data-primary="1"]:hover:not(:disabled){background:var(--d5-accent-deep);color:#fff}',
|
|
@@ -604,6 +613,10 @@ const UPDATE_PANEL_D5_CSS = [
|
|
|
604
613
|
'.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner[data-kind="restart"]{border-color:var(--d5-warn);background:var(--d5-warn-bg);font-family:var(--d5-serif);border-width:2px}',
|
|
605
614
|
'.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner[data-kind="failed"],.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner[data-kind="blocked"]{border-color:var(--d5-bad);background:var(--d5-bad-bg)}',
|
|
606
615
|
'.dsh-upd[data-theme="d5-paper"] .dsh-upd-banner[data-kind="done"]{border-color:var(--d5-ok);background:var(--d5-ok-bg)}',
|
|
616
|
+
'.dsh-upd[data-theme="d5-paper"] .dsh-upd-changelog-yanked{border-color:var(--d5-warn);background:var(--d5-warn-bg);color:var(--d5-ink)}',
|
|
617
|
+
'.dsh-upd[data-theme="d5-paper"] .dsh-upd-breaking-badge{color:var(--d5-accent);border-color:var(--d5-accent)}',
|
|
618
|
+
'.dsh-upd[data-theme="d5-paper"] .dsh-upd-changelog-count{color:var(--d5-muted)}',
|
|
619
|
+
'.dsh-upd[data-theme="d5-paper"] .dsh-upd-changelog-more-note{color:var(--d5-muted)}',
|
|
607
620
|
// —— 大印章(原型 :206 `.seal`:右上 88px、旋转 -7°、双细框;内容与色调来自根属性,不加节点)——
|
|
608
621
|
'.dsh-upd[data-theme="d5-paper"]{position:relative;padding:22px 26px 20px}',
|
|
609
622
|
'.dsh-upd[data-theme="d5-paper"]::before{content:attr(data-seal);position:absolute;top:20px;right:24px;width:88px;height:88px;',
|
|
@@ -791,7 +804,22 @@ function renderUpdatePanelKernel(input, view) {
|
|
|
791
804
|
const fromText = String(snapshot.runningVersion ?? "");
|
|
792
805
|
const toText = String(snapshot.latestVersion ?? "");
|
|
793
806
|
const rangeTitle = fromText && toText ? `\u66F4\u65B0\u8BF4\u660E\uFF08${fromText} \u2192 ${toText}\uFF09\uFF1A` : "\u66F4\u65B0\u8BF4\u660E\uFF1A";
|
|
794
|
-
|
|
807
|
+
let yankedBanner = "";
|
|
808
|
+
try {
|
|
809
|
+
const toEntry = Array.isArray(ranged) ? ranged.find(function(e) {
|
|
810
|
+
try {
|
|
811
|
+
return e && e.version === toText;
|
|
812
|
+
} catch {
|
|
813
|
+
return false;
|
|
814
|
+
}
|
|
815
|
+
}) : null;
|
|
816
|
+
if (toEntry && toEntry.yanked === true && toText) {
|
|
817
|
+
yankedBanner = yankedBannerHTML(toText);
|
|
818
|
+
}
|
|
819
|
+
} catch {
|
|
820
|
+
yankedBanner = "";
|
|
821
|
+
}
|
|
822
|
+
inner = `<div class="dsh-upd-changelog-wrap"><div>${escapeHtml(rangeTitle)}</div>${yankedBanner}
|
|
795
823
|
${changelogHTML}
|
|
796
824
|
</div>`;
|
|
797
825
|
} catch {
|
|
@@ -951,7 +979,7 @@ function mountUpdatePanel(container, options) {
|
|
|
951
979
|
let manualChangelogOverride = typeof options.changelogMarkdown === "string";
|
|
952
980
|
const changelogCache = /* @__PURE__ */ new Map();
|
|
953
981
|
const changelogInflight = /* @__PURE__ */ new Set();
|
|
954
|
-
const
|
|
982
|
+
const changelogFailedAt = /* @__PURE__ */ new Map();
|
|
955
983
|
let autoSeq = 0;
|
|
956
984
|
const pollMs = options.pollMs === void 0 ? DEFAULT_PANEL_POLL_MS : options.pollMs;
|
|
957
985
|
if (typeof pollMs !== "number" || !Number.isFinite(pollMs) || pollMs < MIN_PANEL_POLL_MS) {
|
|
@@ -1069,7 +1097,16 @@ function mountUpdatePanel(container, options) {
|
|
|
1069
1097
|
if (skipStore.has(v)) return null;
|
|
1070
1098
|
} catch {
|
|
1071
1099
|
}
|
|
1072
|
-
if (
|
|
1100
|
+
if (changelogInflight.has(v)) return null;
|
|
1101
|
+
const hasCache = changelogCache.has(v);
|
|
1102
|
+
const failedAt = changelogFailedAt.has(v) ? changelogFailedAt.get(v) : null;
|
|
1103
|
+
let nowMs = 0;
|
|
1104
|
+
try {
|
|
1105
|
+
nowMs = Date.now();
|
|
1106
|
+
} catch {
|
|
1107
|
+
nowMs = 0;
|
|
1108
|
+
}
|
|
1109
|
+
if (!shouldFetchChangelog({ hasCache, failedAt, now: nowMs, isManual: false })) return null;
|
|
1073
1110
|
return v;
|
|
1074
1111
|
}
|
|
1075
1112
|
function maybeAutoChangelog() {
|
|
@@ -1082,7 +1119,14 @@ function mountUpdatePanel(container, options) {
|
|
|
1082
1119
|
changelogInflight.delete(v);
|
|
1083
1120
|
if (!mounted || seq !== autoSeq) return;
|
|
1084
1121
|
if (!reply || typeof reply !== "object" || reply["ok"] !== true) {
|
|
1085
|
-
|
|
1122
|
+
try {
|
|
1123
|
+
changelogFailedAt.set(v, Date.now());
|
|
1124
|
+
} catch {
|
|
1125
|
+
try {
|
|
1126
|
+
changelogFailedAt.set(v, 0);
|
|
1127
|
+
} catch {
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1086
1130
|
return;
|
|
1087
1131
|
}
|
|
1088
1132
|
const md = reply["markdown"];
|
|
@@ -1094,7 +1138,16 @@ function mountUpdatePanel(container, options) {
|
|
|
1094
1138
|
},
|
|
1095
1139
|
() => {
|
|
1096
1140
|
changelogInflight.delete(v);
|
|
1097
|
-
if (mounted && seq === autoSeq)
|
|
1141
|
+
if (mounted && seq === autoSeq) {
|
|
1142
|
+
try {
|
|
1143
|
+
changelogFailedAt.set(v, Date.now());
|
|
1144
|
+
} catch {
|
|
1145
|
+
try {
|
|
1146
|
+
changelogFailedAt.set(v, 0);
|
|
1147
|
+
} catch {
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1098
1151
|
}
|
|
1099
1152
|
);
|
|
1100
1153
|
}
|
|
@@ -1126,7 +1179,7 @@ function mountUpdatePanel(container, options) {
|
|
|
1126
1179
|
const reply = await call(phoneNames.updateCheck, queueArgs());
|
|
1127
1180
|
if (!mounted) return;
|
|
1128
1181
|
applyStatusReply(reply);
|
|
1129
|
-
|
|
1182
|
+
changelogFailedAt.clear();
|
|
1130
1183
|
} catch {
|
|
1131
1184
|
if (!mounted) return;
|
|
1132
1185
|
lastError = "check-failed";
|