dsh-plugin-update 0.7.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +25 -24
- package/dist/entry.d.ts +4 -0
- package/dist/entry.js +32 -8
- package/dist/panel-batch.d.ts +4 -1
- package/dist/panel-batch.js +31 -7
- package/dist/panel.js +10 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
格式为 Keep-a-Changelog 子集:`Added/Fixed/Changed` 必写,`Deprecated/Removed/Security` 有则透传,`Unreleased` 面板忽略,禁止 git-log 直倒。面板缺日志时中性提示,不挡安装。
|
|
4
4
|
|
|
5
|
+
## [0.8.0] - 2026-10-07
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- 入口件无新版摆法 `upToDateDisplay`(`note` / `button`(默认) / `tooltip`):默认按钮本身即`已是最新 X.Y.Z`(单按钮不占宽,可点再查,hover 回`检查更新`暗示动作;无新版走中性弱边、hover 才走主色,复用既有 token,不新增文案与 token);`tooltip` 按钮仍是`检查更新`、版本收进悬停;`note` 回右侧小字旧样子(显式传回)。入口 `label()` 与按钮正文同口径,`badge` 形态不受影响;README 2.5 节补自由度与参数说明,单测覆盖三摆法与非法校验。
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- 查新版瞬时抖动补强(面板):忙闲两帧同高(动作区按钮 nowrap + 最小宽预留、转圈槽常驻)+ 横幅/复制区最小高占位,查新版与轮询回包帧不再跳动。
|
|
12
|
+
- 入口件深色默认主题补 `text-muted`(`#9ca3af`),按钮直显弱边在深底可读。
|
|
13
|
+
- 批量 prefs-only 存偏好回归测试补齐(#74):偏好保存不闪空行、失败保留行(测试覆盖,行为零变更)。
|
|
14
|
+
|
|
5
15
|
## [0.7.1] - 2026-10-06
|
|
6
16
|
|
|
7
17
|
### Fixed
|
package/README.md
CHANGED
|
@@ -144,28 +144,28 @@ mountUpdatePanel(slot, { pluginId: 'my-notes-plugin', prefix: 'notes', call: hos
|
|
|
144
144
|
|
|
145
145
|
可选档案卷主题(纸面浅色案卷风,不替换默认):挂载时加 `theme: 'archive'` 即换肤(右上大印章「待查/可装/安装中/待重启/受阻/已最新」+ 横幅小印章一字 + profile 牌 + 待重启衬线横幅配手绘 SVG 标 + 窄屏印章固定 + 省略号逐字折叠 + 浅深双主题跟随系统),内核 DOM 顺序不动、复制诊断常在;不传即最小可用默认深色样式。运行时用 `panel.setTheme('archive' | 'default')` 可切。入口件与批量面板是同一个 `theme` 参数,取值同一套。
|
|
146
146
|
|
|
147
|
-
主题变量覆盖(第三方换肤):四个 UI 口(单面板、批量面板、入口件、批量入口件)都收 `themeTokens`,键为语义名,一次传入即换肤,不传即零回归。入口件打开的 dialog 面板同步透传;运行时用 `setThemeTokens({...})` 即时换肤,传 `undefined` 即清掉覆盖:
|
|
148
|
-
|
|
149
|
-
```js
|
|
150
|
-
mountUpdatePanel(el, { pluginId: 'p', prefix: 'notes', call,
|
|
151
|
-
theme: 'archive',
|
|
152
|
-
themeTokens: { primary: '#c8402a', bg: '#fffdf6', radiusButton: '3px' },
|
|
153
|
-
})
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
键与样式表里的 `--dsh-update-*` 变量一一对应(括号里是变量名),手写 CSS 变量等价生效:
|
|
157
|
-
|
|
158
|
-
| 组 | 键(变量) |
|
|
159
|
-
|---|---|
|
|
160
|
-
| 文字背景边线 | `text`、`textMuted`、`bg`、`bgSoft`、`border`、`borderStrong`、`buttonBg` |
|
|
161
|
-
| 主色焦点 | `primary`、`primaryDeep`、`focus` |
|
|
162
|
-
| 状态四组 | `okBg / okBorder / okText`、`warnBg / warnBorder / warnText`、`badBg / badBorder / badText`、`busyBg / busyBorder / busyText` |
|
|
163
|
-
| 字体阴影 | `fontSans`、`fontSerif`、`fontMono`、`shadow` |
|
|
164
|
-
| 圆角 | `radiusPanel`、`radiusButton`、`radiusBadge` |
|
|
165
|
-
| 入口件尺寸 | `entryFontSize`、`entryPadding`、`entryBorderRadius`、`entryScale`(数字,大于 0) |
|
|
166
|
-
|
|
167
|
-
三条约束:颜色只收 hex(`#rgb`/`#rrggbb`/`#rrggbbaa`)或英文名单词(含 `transparent`),`rgb()`/`color-mix()` 请自行换算成 hex;其余值走注入检查(分号引号括号等不许过,见 `sizing` 同口径);未知键与非法值挂载即抛错,不静默吞掉。被覆盖的 token 不再跟随深色模式(内联值固定,调用方自己保证深浅可读)。
|
|
168
|
-
|
|
147
|
+
主题变量覆盖(第三方换肤):四个 UI 口(单面板、批量面板、入口件、批量入口件)都收 `themeTokens`,键为语义名,一次传入即换肤,不传即零回归。入口件打开的 dialog 面板同步透传;运行时用 `setThemeTokens({...})` 即时换肤,传 `undefined` 即清掉覆盖:
|
|
148
|
+
|
|
149
|
+
```js
|
|
150
|
+
mountUpdatePanel(el, { pluginId: 'p', prefix: 'notes', call,
|
|
151
|
+
theme: 'archive',
|
|
152
|
+
themeTokens: { primary: '#c8402a', bg: '#fffdf6', radiusButton: '3px' },
|
|
153
|
+
})
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
键与样式表里的 `--dsh-update-*` 变量一一对应(括号里是变量名),手写 CSS 变量等价生效:
|
|
157
|
+
|
|
158
|
+
| 组 | 键(变量) |
|
|
159
|
+
|---|---|
|
|
160
|
+
| 文字背景边线 | `text`、`textMuted`、`bg`、`bgSoft`、`border`、`borderStrong`、`buttonBg` |
|
|
161
|
+
| 主色焦点 | `primary`、`primaryDeep`、`focus` |
|
|
162
|
+
| 状态四组 | `okBg / okBorder / okText`、`warnBg / warnBorder / warnText`、`badBg / badBorder / badText`、`busyBg / busyBorder / busyText` |
|
|
163
|
+
| 字体阴影 | `fontSans`、`fontSerif`、`fontMono`、`shadow` |
|
|
164
|
+
| 圆角 | `radiusPanel`、`radiusButton`、`radiusBadge` |
|
|
165
|
+
| 入口件尺寸 | `entryFontSize`、`entryPadding`、`entryBorderRadius`、`entryScale`(数字,大于 0) |
|
|
166
|
+
|
|
167
|
+
三条约束:颜色只收 hex(`#rgb`/`#rrggbb`/`#rrggbbaa`)或英文名单词(含 `transparent`),`rgb()`/`color-mix()` 请自行换算成 hex;其余值走注入检查(分号引号括号等不许过,见 `sizing` 同口径);未知键与非法值挂载即抛错,不静默吞掉。被覆盖的 token 不再跟随深色模式(内联值固定,调用方自己保证深浅可读)。
|
|
168
|
+
|
|
169
169
|
类型定义随包分发(`dsh-plugin-update/panel` 的 `.d.ts`),不用自编译;面板离线可读,与包版本绑定。
|
|
170
170
|
|
|
171
171
|
整组件 HTTP 版(无 `host.call` 环境即跑:浏览器面板经 HTTP POST 直达宿主网关,只传三样):
|
|
@@ -204,12 +204,13 @@ const entry = mountUpdateEntry(document.getElementById('upd-entry'), {
|
|
|
204
204
|
| 摆什么 | `button` / `badge` / `inline` | `button` | 按钮;只给一个状态点;面板本体直接嵌进来 |
|
|
205
205
|
| 什么时候查 | `mount` / `never` | `mount` | 进页面静默查一次(**只调 `.updateStatus`,只读**);`never` 则只在点击时查 |
|
|
206
206
|
| 点了做什么 | `has-update` / `always` / `manual` / `direct` | `has-update` | 有新版才开面板;检查完总是开;交给 `onActivate` 自己跳;点开即弹窗、不预查 |
|
|
207
|
+
| 无新版摆哪 | `button` / `tooltip` / `note` | `button` | 版本即按钮本身;收进悬停;回右侧小字旧样子 |
|
|
207
208
|
|
|
208
|
-
|
|
209
|
+
按钮文案随状态自己变(按宿主语言单语渲染:中文界面纯中文、英文界面纯英文,一次只出现一种语言):`检查更新`(还没查到)/ `已是最新 X.Y.Z`(无新版,默认即按钮本身)/ `有新版 1.1.0` / `正在安装…` / `待重启` / `更新失败,点此查看`(英文见集中字典同 key)。
|
|
209
210
|
|
|
210
211
|
语言跟随(单语):入口件默认跟随 `document.documentElement.lang` > `navigator.languages` > 中文兜底;显式覆盖传 `locale: 'zh' | 'en' | { getActive(): string; subscribe?(cb): () => void }`(对象形态可接宿主语言服务,切换即时重绘,`unmount` 后停订;无 DOM 环境不抛,落中文)。
|
|
211
212
|
|
|
212
|
-
|
|
213
|
+
无新版时版本号摆哪(`upToDateDisplay`,缺省 `'button'`):`button` 即按钮本身就是`已是最新 X.Y.Z`(单按钮不占宽,可点再查,hover 回`检查更新`暗示动作;无新版中性弱边,hover 才走主色);`tooltip` 按钮还是`检查更新`、版本收进悬停;`note` 回旧样子(按钮旁右侧小字,只在 `openOn: 'has-update'` 下点击确认无新版后出现)。旧版默认即 `note` 的样子,要回去就显式传 `upToDateDisplay: 'note'`。想连按钮都不留痕,就用 `openOn: 'always'`(检查完总是开弹窗,无新版在弹窗里看“已是最新”)或 `openOn: 'direct'`(点开即弹窗,连预查都省了,面板挂载即自查;徽标形态仍走回调口径)。
|
|
213
214
|
|
|
214
215
|
按钮尺寸(跟头行其他控件同高/整体缩放):传 `sizing` 只改按钮本体,不碰面板;不传即默认外观。`scale` 是整体缩放(默认 `1`,如 `1.2` 放大、`0.9` 缩小),原来写在容器上的 `zoom` 可整体搬进来:
|
|
215
216
|
|
package/dist/entry.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export type EntryAutoCheck = 'mount' | 'never';
|
|
|
12
12
|
* `manual` 交给 onActivate;`direct` 点开即弹窗、不预查(面板挂载即自查;badge 仍走回调口径)。
|
|
13
13
|
*/
|
|
14
14
|
export type EntryOpenOn = 'has-update' | 'always' | 'manual' | 'direct';
|
|
15
|
+
/** 无新版时版本号摆哪:`note` 右侧小字(旧默认);`button` 按钮本身即版本(默认);`tooltip` 收进悬停。 */
|
|
16
|
+
export type UpToDateDisplay = 'note' | 'button' | 'tooltip';
|
|
15
17
|
/** 入口件主题(与面板同一套取值,切换即换肤)。 */
|
|
16
18
|
export type EntryTheme = UpdatePanelTheme;
|
|
17
19
|
/** 入口件的状态档:只用来算文案与 data-state,不另立状态机。 */
|
|
@@ -52,6 +54,8 @@ export interface UpdateEntryOptions {
|
|
|
52
54
|
autoCheck?: EntryAutoCheck;
|
|
53
55
|
/** 缺省 'has-update':有新版才开面板,没有就只在原地给一句提示。 */
|
|
54
56
|
openOn?: EntryOpenOn;
|
|
57
|
+
/** 无新版时版本号摆哪:缺省 'button'(按钮本身即版本,单按钮不占宽);'note' 回旧样子(右侧小字);'tooltip' 收进悬停。 */
|
|
58
|
+
upToDateDisplay?: UpToDateDisplay;
|
|
55
59
|
/** 覆盖默认按钮文案(不传就用状态联动文案)。 */
|
|
56
60
|
label?: string;
|
|
57
61
|
/** 按钮尺寸覆盖(#69):见 EntrySizing;不传即零回归,badge/inline 不受影响。 */
|
package/dist/entry.js
CHANGED
|
@@ -8,6 +8,11 @@ import {
|
|
|
8
8
|
} from "./panel.js";
|
|
9
9
|
import { BILINGUAL_CSS, copyHTML, copyText } from "./bilingual.js";
|
|
10
10
|
import { resolveLang, subscribeLang } from "./lang.js";
|
|
11
|
+
function upToDateVersionOf(snapshot, error) {
|
|
12
|
+
if (error || !snapshot || hasUpdateOf(snapshot)) return null;
|
|
13
|
+
const v = snapshot.runningVersion;
|
|
14
|
+
return typeof v === "string" && v.trim() ? v.trim() : null;
|
|
15
|
+
}
|
|
11
16
|
function hasUpdateOf(snapshot) {
|
|
12
17
|
if (!snapshot) return false;
|
|
13
18
|
const latest = snapshot.latestVersion;
|
|
@@ -123,6 +128,9 @@ const UPDATE_ENTRY_CSS = [
|
|
|
123
128
|
'.dsh-upd-entry[data-state="update"] .dsh-upd-entry-dot{background:var(--dsh-update-ok-border,#059669)}',
|
|
124
129
|
'.dsh-upd-entry[data-state="busy"] .dsh-upd-entry-dot,.dsh-upd-entry[data-state="restart"] .dsh-upd-entry-dot{background:var(--dsh-update-warn-border,#d97706)}',
|
|
125
130
|
'.dsh-upd-entry[data-state="failed"] .dsh-upd-entry-dot{background:var(--dsh-update-bad-border,#dc2626)}',
|
|
131
|
+
// C 直显(upToDateDisplay='button'):无新版时按钮本身即版本,走中性弱边(不抢有新版的红/绿),hover 才走 primary 暗示可再查;复用既有 token,不加新键。
|
|
132
|
+
'.dsh-upd-entry[data-known="uptodate"] .dsh-upd-entry-btn{border-color:var(--dsh-update-border,#d1d5db);color:var(--dsh-update-text-muted,#6b7280)}',
|
|
133
|
+
'.dsh-upd-entry[data-known="uptodate"] .dsh-upd-entry-btn:hover{border-color:var(--dsh-update-primary,#2563eb);color:var(--dsh-update-primary,#2563eb)}',
|
|
126
134
|
// 小字自带底(深色宿主 + 浅色变量时也读得出;浅底宿主上只是多一圈细线,不抢戏)。
|
|
127
135
|
".dsh-upd-entry-note{font-size:12.5px;opacity:.9;background:var(--dsh-update-bg,#ffffff);border:1px solid var(--dsh-update-border,#e5e7eb);border-radius:var(--dsh-update-radius-badge,4px);padding:1px 8px}",
|
|
128
136
|
'.dsh-upd-entry[data-theme="archive"] .dsh-upd-entry-note{background:var(--dsh-update-bg);border-color:var(--dsh-update-border-strong);color:var(--dsh-update-text)}',
|
|
@@ -133,7 +141,7 @@ const UPDATE_ENTRY_CSS = [
|
|
|
133
141
|
'.dsh-upd-entry[data-theme="archive"] .dsh-upd-entry-btn{border-color:var(--dsh-update-border-strong);background:var(--dsh-update-bg);color:var(--dsh-update-text);border-radius:var(--dsh-update-entry-border-radius,3px)}',
|
|
134
142
|
'.dsh-upd-entry[data-theme="archive"] .dsh-upd-entry-btn:hover{border-color:var(--dsh-update-primary);color:var(--dsh-update-primary)}',
|
|
135
143
|
'@media (prefers-color-scheme: dark){.dsh-upd-entry[data-theme="archive"]{--dsh-update-text:#ece5d3;--dsh-update-text-muted:#a89c83;--dsh-update-border-strong:#5c4e3b;--dsh-update-primary:#e0684e;--dsh-update-bg:#1e1a15}}',
|
|
136
|
-
"@media (prefers-color-scheme: dark){.dsh-upd-entry{color:#e5e7eb}",
|
|
144
|
+
"@media (prefers-color-scheme: dark){.dsh-upd-entry{color:#e5e7eb;--dsh-update-text-muted:#9ca3af}",
|
|
137
145
|
".dsh-upd-entry-btn{--dsh-update-button-bg:#1f2937;--dsh-update-border:#374151}}"
|
|
138
146
|
].join("\n");
|
|
139
147
|
const ENTRY_ATTR = "data-dsh-upd-entry";
|
|
@@ -179,6 +187,10 @@ function mountUpdateEntry(container, options) {
|
|
|
179
187
|
if (openOn !== "has-update" && openOn !== "always" && openOn !== "manual" && openOn !== "direct") {
|
|
180
188
|
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`);
|
|
181
189
|
}
|
|
190
|
+
const upToDateDisplay = options.upToDateDisplay ?? "button";
|
|
191
|
+
if (upToDateDisplay !== "note" && upToDateDisplay !== "button" && upToDateDisplay !== "tooltip") {
|
|
192
|
+
throw new Error(`[dsh-plugin-update] invalid upToDateDisplay: expected note / button / tooltip (got ${JSON.stringify(options.upToDateDisplay)})`);
|
|
193
|
+
}
|
|
182
194
|
if (options.theme !== void 0 && options.theme !== "default" && options.theme !== "archive") {
|
|
183
195
|
throw new Error(`[dsh-plugin-update] \u4E3B\u9898\u975E\u6CD5\uFF1A\u53EA\u6536 default \u6216 archive\uFF08\u6536\u5230 ${JSON.stringify(options.theme)}\uFF09`);
|
|
184
196
|
}
|
|
@@ -237,7 +249,13 @@ function mountUpdateEntry(container, options) {
|
|
|
237
249
|
return { snapshot, error };
|
|
238
250
|
}
|
|
239
251
|
function currentLabel() {
|
|
240
|
-
|
|
252
|
+
if (labelOverride) return labelOverride;
|
|
253
|
+
const lang = currentLang();
|
|
254
|
+
if (!activating && upToDateDisplay === "button" && entryStateKind(stateOf()) === "idle") {
|
|
255
|
+
const v = upToDateVersionOf(snapshot, error);
|
|
256
|
+
if (v) return copyText("entry.note.up-to-date", lang, { version: v });
|
|
257
|
+
}
|
|
258
|
+
return entryLabelFor(stateOf(), lang);
|
|
241
259
|
}
|
|
242
260
|
function hasUpdate() {
|
|
243
261
|
return hasUpdateOf(snapshot);
|
|
@@ -248,18 +266,24 @@ function mountUpdateEntry(container, options) {
|
|
|
248
266
|
function entryHTML() {
|
|
249
267
|
const kind = entryStateKind(stateOf());
|
|
250
268
|
const lang = currentLang();
|
|
251
|
-
const
|
|
252
|
-
const
|
|
269
|
+
const knownUpToDate = !activating && kind === "idle" ? upToDateVersionOf(snapshot, error) : null;
|
|
270
|
+
const buttonMode = !labelOverride && upToDateDisplay === "button" && knownUpToDate;
|
|
271
|
+
const tooltipMode = !labelOverride && upToDateDisplay === "tooltip" && knownUpToDate;
|
|
272
|
+
const labelHTML = labelOverride ? escapeHtml(labelOverride) : activating ? copyHTML("entry.action.checking", lang) : buttonMode && knownUpToDate ? copyHTML("entry.note.up-to-date", lang, { version: knownUpToDate }) : copyHTML(entryBilingualKeyFor(stateOf()), lang, entryBilingualValuesFor(stateOf()));
|
|
273
|
+
const labelText = labelOverride ?? (activating ? copyText("entry.action.checking", lang) : buttonMode && knownUpToDate ? copyText("entry.note.up-to-date", lang, { version: knownUpToDate }) : copyText(entryBilingualKeyFor(stateOf()), lang, entryBilingualValuesFor(stateOf())));
|
|
274
|
+
const titleText = labelOverride ? labelOverride : activating ? copyText("entry.action.checking", lang) : buttonMode && knownUpToDate ? copyText(entryBilingualKeyFor(stateOf()), lang, entryBilingualValuesFor(stateOf())) : tooltipMode && knownUpToDate ? copyText("entry.note.up-to-date", lang, { version: knownUpToDate }) : labelText;
|
|
253
275
|
const busyAttr = activating ? ' disabled aria-busy="true"' : "";
|
|
254
276
|
const themeAttr = theme === "archive" ? ' data-theme="archive"' : "";
|
|
255
277
|
const tokensStyle = themeTokensStyleFor(themeTokens ?? void 0);
|
|
256
278
|
const styleBody = [sizingStyle, tokensStyle].filter((part) => part).join(";");
|
|
257
279
|
const styleAttr = styleBody ? ` style="${styleBody}"` : "";
|
|
258
|
-
const
|
|
259
|
-
const
|
|
280
|
+
const knownAttr = buttonMode ? ' data-known="uptodate"' : "";
|
|
281
|
+
const displayAttr = ` data-uptodate="${upToDateDisplay}"`;
|
|
282
|
+
const control = variant === "badge" ? `<button type="button" class="dsh-upd-entry-dot" ${ENTRY_ATTR}="activate" title="${escapeHtml(titleText)}" aria-label="${escapeHtml(labelText)}"${busyAttr}></button>` : `<button type="button" class="dsh-upd-entry-btn" ${ENTRY_ATTR}="activate" title="${escapeHtml(titleText)}" aria-label="${escapeHtml(labelText)}"${busyAttr}>${labelHTML}</button>`;
|
|
283
|
+
const noteHTML = upToDateDisplay === "note" && noteVersion ? `<span class="dsh-upd-entry-note" data-dsh-upd-note="1">${copyHTML("entry.note.up-to-date", lang, { version: noteVersion })}</span>` : "";
|
|
260
284
|
return `<style>${UPDATE_ENTRY_CSS}
|
|
261
285
|
${BILINGUAL_CSS}</style>
|
|
262
|
-
<span class="dsh-upd-entry" data-variant="${variant}" data-state="${kind}"${themeAttr}${styleAttr}>${control}${noteHTML}</span>`;
|
|
286
|
+
<span class="dsh-upd-entry" data-variant="${variant}" data-state="${kind}"${displayAttr}${knownAttr}${themeAttr}${styleAttr}>${control}${noteHTML}</span>`;
|
|
263
287
|
}
|
|
264
288
|
function render() {
|
|
265
289
|
if (!mounted) return;
|
|
@@ -361,7 +385,7 @@ ${BILINGUAL_CSS}</style>
|
|
|
361
385
|
openDialog();
|
|
362
386
|
return;
|
|
363
387
|
}
|
|
364
|
-
noteVersion = snapshot.runningVersion;
|
|
388
|
+
if (upToDateDisplay === "note") noteVersion = snapshot.runningVersion;
|
|
365
389
|
render();
|
|
366
390
|
}
|
|
367
391
|
function setTheme(next) {
|
package/dist/panel-batch.d.ts
CHANGED
|
@@ -127,8 +127,11 @@ export interface BatchLedgerCounts {
|
|
|
127
127
|
* 失败优先(失败行永远算失败,不算待重启);其余按相位落档;不用重启的终态才算「已最新」。
|
|
128
128
|
* 例外一处:「忙失败占位」(phase=failed 但 error=update-busy)其实是**排队**不是失败——
|
|
129
129
|
* 宿主忙时先写占位再回 update-busy,面板把它翻回「可更新」,免得总账把它报成失败。
|
|
130
|
+
* 无轮次 pending 行看知识(#73:与行展示同口径,四态镜像 batchRowKnowledgeText):
|
|
131
|
+
* 有新版进可更新、已是最新进已最新、查失败进失败、无知识仍待查;
|
|
132
|
+
* checking 行永远走执行态(待查),不吃知识。不传 inventory 即无知识(旧语义)。
|
|
130
133
|
*/
|
|
131
|
-
export declare function batchLedgerCounts(rows: readonly BatchRowView[]): BatchLedgerCounts;
|
|
134
|
+
export declare function batchLedgerCounts(rows: readonly BatchRowView[], inventory?: unknown): BatchLedgerCounts;
|
|
132
135
|
/** 分类计数 → 一句话总账(只出现非零档,顺序固定:可更新 · 安装中 · 待查 · 待重启 · 失败 · 已跳过 · 已最新;#64 单语:缺省 zh 零回归)。 */
|
|
133
136
|
export declare function batchLedgerText(counts: BatchLedgerCounts, lang?: unknown): string;
|
|
134
137
|
/**
|
package/dist/panel-batch.js
CHANGED
|
@@ -35,7 +35,7 @@ function buildBatchPhoneNames(prefix) {
|
|
|
35
35
|
prefsSave: p + ".batchPrefsSave"
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
|
-
function batchLedgerCounts(rows) {
|
|
38
|
+
function batchLedgerCounts(rows, inventory) {
|
|
39
39
|
const counts = {
|
|
40
40
|
updatable: 0,
|
|
41
41
|
installing: 0,
|
|
@@ -51,13 +51,22 @@ function batchLedgerCounts(rows) {
|
|
|
51
51
|
else if (phase === "failed") counts.failed += 1;
|
|
52
52
|
else if (phase === "installing") counts.installing += 1;
|
|
53
53
|
else if (phase === "ready") counts.updatable += 1;
|
|
54
|
-
else if (phase === "pending"
|
|
54
|
+
else if (phase === "pending") counts[ledgerKnowledgeBucket(knowledgeEntryOf(inventory, row.key))] += 1;
|
|
55
|
+
else if (phase === "checking") counts.pending += 1;
|
|
55
56
|
else if (phase === "skipped") counts.skipped += 1;
|
|
56
57
|
else if (row.restartRequired === true) counts.restart += 1;
|
|
57
58
|
else counts.settled += 1;
|
|
58
59
|
}
|
|
59
60
|
return counts;
|
|
60
61
|
}
|
|
62
|
+
function ledgerKnowledgeBucket(entry) {
|
|
63
|
+
if (!entry) return "pending";
|
|
64
|
+
if (entry.error) return "failed";
|
|
65
|
+
if (entry.latestVersion && entry.installedVersion) return entry.latestVersion !== entry.installedVersion ? "updatable" : "settled";
|
|
66
|
+
if (entry.latestVersion && !entry.installedVersion) return "updatable";
|
|
67
|
+
if (entry.lastCheckedAt > 0) return "settled";
|
|
68
|
+
return "pending";
|
|
69
|
+
}
|
|
61
70
|
function batchLedgerText(counts, lang) {
|
|
62
71
|
const l = langOf(lang);
|
|
63
72
|
const parts = [];
|
|
@@ -216,7 +225,7 @@ function renderBatchPanelHTML(input) {
|
|
|
216
225
|
const expandedKey = typeof input.expandedKey === "string" && input.expandedKey ? input.expandedKey : null;
|
|
217
226
|
const lastError = typeof input.lastError === "string" && input.lastError ? input.lastError : null;
|
|
218
227
|
const loaded = input.loaded === void 0 ? rows.length > 0 : input.loaded === true;
|
|
219
|
-
const counts = batchLedgerCounts(rows);
|
|
228
|
+
const counts = batchLedgerCounts(rows, input.inventory);
|
|
220
229
|
const installing = counts.installing > 0;
|
|
221
230
|
const macroBusy = input.inFlight === true || installing;
|
|
222
231
|
const total = rows.length;
|
|
@@ -1072,10 +1081,25 @@ function mountUpdateBatchPanel(container, options) {
|
|
|
1072
1081
|
return;
|
|
1073
1082
|
}
|
|
1074
1083
|
if (reply["ok"] === true) {
|
|
1075
|
-
const
|
|
1076
|
-
const
|
|
1077
|
-
|
|
1078
|
-
|
|
1084
|
+
const hasSession = "session" in reply && reply["session"] !== void 0;
|
|
1085
|
+
const hasRows = "rows" in reply && reply["rows"] !== void 0;
|
|
1086
|
+
if (!hasSession && !hasRows) {
|
|
1087
|
+
if (isObject(reply["prefs"])) lastPrefs = reply["prefs"];
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
if (hasSession) {
|
|
1091
|
+
const session = normalizeBatchSession(reply["session"]);
|
|
1092
|
+
lastSession = session;
|
|
1093
|
+
if (hasRows) {
|
|
1094
|
+
const parsed = readRows(reply["rows"]);
|
|
1095
|
+
rows = parsed.length > 0 ? parsed : rowsFromSession(session);
|
|
1096
|
+
} else {
|
|
1097
|
+
rows = rowsFromSession(session);
|
|
1098
|
+
}
|
|
1099
|
+
} else {
|
|
1100
|
+
const parsed = readRows(reply["rows"]);
|
|
1101
|
+
if (parsed.length > 0) rows = parsed;
|
|
1102
|
+
}
|
|
1079
1103
|
if (isObject(reply["inventory"])) {
|
|
1080
1104
|
lastInventory = reply["inventory"];
|
|
1081
1105
|
const at = reply["inventory"]["updatedAt"];
|
package/dist/panel.js
CHANGED
|
@@ -696,6 +696,15 @@ const UPDATE_PANEL_CSS = [
|
|
|
696
696
|
'.dsh-upd-actions button:first-child:not([aria-busy="true"])::after{content:"";display:inline-block;width:11px;height:11px;margin-left:8px;visibility:hidden}',
|
|
697
697
|
".dsh-upd-banner{min-height:1.2em}",
|
|
698
698
|
".dsh-upd-strip{min-height:48px}",
|
|
699
|
+
// —— 查新版瞬时抖动补强:忙闲两帧同高 + 横幅不动(只追加覆盖,不改上面既有串)——
|
|
700
|
+
".dsh-upd-actions{align-content:flex-start}",
|
|
701
|
+
".dsh-upd-actions button{white-space:nowrap}",
|
|
702
|
+
".dsh-upd-actions button:first-child{min-width:10em}",
|
|
703
|
+
'.dsh-upd-actions button[data-primary="1"]{min-width:9em}',
|
|
704
|
+
".dsh-upd-banner{min-height:3.4em;display:flex;flex-direction:column;justify-content:center}",
|
|
705
|
+
".dsh-upd-banner{animation:none}",
|
|
706
|
+
".dsh-upd-copy{min-height:1.75em}",
|
|
707
|
+
".dsh-upd-copy--empty{visibility:hidden}",
|
|
699
708
|
"@media (prefers-color-scheme: dark){.dsh-upd{--dsh-update-text:#e5e7eb;--dsh-update-bg:#111827;--dsh-update-border:#374151;",
|
|
700
709
|
"--dsh-update-button-bg:#1f2937;--dsh-update-bg-soft:#1f2937;--dsh-update-primary:#3b82f6;--dsh-update-focus:#93c5fd;",
|
|
701
710
|
// 横幅深色覆盖:底色用低透明度同色系(不是浅色原值),边线提亮,保证「深底浅字」可读。
|
|
@@ -1084,6 +1093,7 @@ ${changelogHTML}
|
|
|
1084
1093
|
}
|
|
1085
1094
|
}
|
|
1086
1095
|
if (copyNotice) errLines.push(`<div class="dsh-upd-copy" role="status">${escapeHtml(copyNotice)}</div>`);
|
|
1096
|
+
else errLines.push('<div class="dsh-upd-copy dsh-upd-copy--empty" aria-hidden="true"></div>');
|
|
1087
1097
|
chapters.push(chapterOf(4, errLines.join(""), "", l));
|
|
1088
1098
|
}
|
|
1089
1099
|
chapters.push(
|