@huaqiu/dsh-tool-plugin-update 0.6.10 → 0.6.12
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/lib/client.js +63 -109
- package/lib/client.js.map +1 -1
- package/package.json +3 -3
- package/src/client/api.ts +11 -2
- package/src/client/copy.ts +2 -30
- package/src/client/index.ts +5 -35
- package/src/client/update-view.ts +48 -80
- package/src/client/version.ts +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@huaqiu/dsh-tool-plugin-update",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./lib/index.mjs",
|
|
6
6
|
"types": "./lib/index.d.mts",
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
"@deepseek-ai/dsh-host-webserver": "^0.2.1-rc.2"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@huaqiu/dsh-plugin-log": "0.6.
|
|
35
|
-
"@huaqiu/dsh-sidebar-entry": "0.6.
|
|
34
|
+
"@huaqiu/dsh-plugin-log": "0.6.12",
|
|
35
|
+
"@huaqiu/dsh-sidebar-entry": "0.6.12"
|
|
36
36
|
},
|
|
37
37
|
"files": [
|
|
38
38
|
"lib",
|
package/src/client/api.ts
CHANGED
|
@@ -420,13 +420,22 @@ export interface InstallResult {
|
|
|
420
420
|
}
|
|
421
421
|
|
|
422
422
|
/** POST the install transaction for the confirmed set version. */
|
|
423
|
-
export function installSet(setVersion: string): Promise<InstallResult> {
|
|
423
|
+
export function installSet(setVersion: string, pluginNames?: string[]): Promise<InstallResult> {
|
|
424
424
|
return request<InstallResult>('/install', {
|
|
425
425
|
method: 'POST',
|
|
426
|
-
body: JSON.stringify({ setVersion }),
|
|
426
|
+
body: JSON.stringify({ setVersion, ...(pluginNames ? { pluginNames } : {}) }),
|
|
427
427
|
})
|
|
428
428
|
}
|
|
429
429
|
|
|
430
|
+
/** Persist a per-plugin decision for this release set. */
|
|
431
|
+
export function ignorePlugin(setVersion: string, name: string): Promise<unknown> {
|
|
432
|
+
return request<unknown>('/plugin-ignore', { method: 'POST', body: JSON.stringify({ setVersion, name }) })
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
export function unignorePlugin(setVersion: string, name: string): Promise<unknown> {
|
|
436
|
+
return request<unknown>('/plugin-unignore', { method: 'POST', body: JSON.stringify({ setVersion, name }) })
|
|
437
|
+
}
|
|
438
|
+
|
|
430
439
|
/** POST ignore for the current offer (set-scoped, persists across restarts). */
|
|
431
440
|
export function ignoreOffer(setVersion: string): Promise<unknown> {
|
|
432
441
|
return request<unknown>('/ignore', { method: 'POST', body: JSON.stringify({ setVersion }) })
|
package/src/client/copy.ts
CHANGED
|
@@ -14,7 +14,7 @@ const zh = {
|
|
|
14
14
|
entryTooltip: '查看可用的插件更新',
|
|
15
15
|
viewTitle: '插件更新',
|
|
16
16
|
viewIntro: '检查并安装可用的插件更新',
|
|
17
|
-
viewInfo: '
|
|
17
|
+
viewInfo: '在这里查看插件集的更新;已是最新的插件无需操作。',
|
|
18
18
|
refresh: '刷新',
|
|
19
19
|
close: '关闭',
|
|
20
20
|
loading: '正在读取插件更新…',
|
|
@@ -37,22 +37,9 @@ const zh = {
|
|
|
37
37
|
groupCount: (count: number) => `${count} 个插件`,
|
|
38
38
|
group: {
|
|
39
39
|
actionable: '待处理',
|
|
40
|
-
blocked: '无法安装',
|
|
41
40
|
ignored: '已忽略',
|
|
42
41
|
current: '已是最新',
|
|
43
42
|
},
|
|
44
|
-
state: {
|
|
45
|
-
current: '当前',
|
|
46
|
-
update: '可更新',
|
|
47
|
-
install: '待安装',
|
|
48
|
-
ignored: '已忽略',
|
|
49
|
-
blocked: '被阻止',
|
|
50
|
-
},
|
|
51
|
-
blockedReason: {
|
|
52
|
-
host: '不适用于当前 EDA 主机',
|
|
53
|
-
'hq-edge-version': '当前 HQ Edge 版本不兼容',
|
|
54
|
-
'runtime-unknown': 'HQ Edge 版本未知,无法校验',
|
|
55
|
-
},
|
|
56
43
|
runtime: {
|
|
57
44
|
title: 'HQ Edge 运行时',
|
|
58
45
|
current: '当前版本',
|
|
@@ -197,7 +184,6 @@ const zh = {
|
|
|
197
184
|
allChangelog: '展开全部更新日志',
|
|
198
185
|
changelogBtn: '日志',
|
|
199
186
|
changelogTitle: '更新内容:',
|
|
200
|
-
blockedBadge: '无法安装',
|
|
201
187
|
// Footer
|
|
202
188
|
footerCount: (n: number) => `准备安装 ${n} 项兼容插件`,
|
|
203
189
|
footerSet: (v: string) => `版本集: v${v}`,
|
|
@@ -222,7 +208,7 @@ const en: UpdateCopy = {
|
|
|
222
208
|
entryTooltip: 'Review available plugin updates',
|
|
223
209
|
viewTitle: 'Plugin Updates',
|
|
224
210
|
viewIntro: 'Check for and install available plugin updates',
|
|
225
|
-
viewInfo: 'Review the plugin set updates here.
|
|
211
|
+
viewInfo: 'Review the plugin set updates here. Plugins that are up to date need no action.',
|
|
226
212
|
refresh: 'Refresh',
|
|
227
213
|
close: 'Close',
|
|
228
214
|
loading: 'Reading plugin updates…',
|
|
@@ -243,22 +229,9 @@ const en: UpdateCopy = {
|
|
|
243
229
|
groupCount: (count: number) => `${count} plugin${count === 1 ? '' : 's'}`,
|
|
244
230
|
group: {
|
|
245
231
|
actionable: 'Actionable',
|
|
246
|
-
blocked: 'Blocked',
|
|
247
232
|
ignored: 'Ignored',
|
|
248
233
|
current: 'Up to date',
|
|
249
234
|
},
|
|
250
|
-
state: {
|
|
251
|
-
current: 'Current',
|
|
252
|
-
update: 'Update',
|
|
253
|
-
install: 'Install',
|
|
254
|
-
ignored: 'Ignored',
|
|
255
|
-
blocked: 'Blocked',
|
|
256
|
-
},
|
|
257
|
-
blockedReason: {
|
|
258
|
-
host: 'Not applicable to the current EDA host',
|
|
259
|
-
'hq-edge-version': 'Incompatible with the current HQ Edge version',
|
|
260
|
-
'runtime-unknown': 'HQ Edge version unknown — cannot verify',
|
|
261
|
-
},
|
|
262
235
|
runtime: {
|
|
263
236
|
title: 'HQ Edge Runtime',
|
|
264
237
|
current: 'Current version',
|
|
@@ -397,7 +370,6 @@ const en: UpdateCopy = {
|
|
|
397
370
|
allChangelog: 'Expand all changelogs',
|
|
398
371
|
changelogBtn: 'Log',
|
|
399
372
|
changelogTitle: 'What changed:',
|
|
400
|
-
blockedBadge: 'Cannot install',
|
|
401
373
|
footerCount: (n: number) => `Ready to install ${n} compatible plugin${n === 1 ? '' : 's'}`,
|
|
402
374
|
footerSet: (v: string) => `Set: v${v}`,
|
|
403
375
|
installAllSet: (v: string) => `Update all plugins (v${v})`,
|
package/src/client/index.ts
CHANGED
|
@@ -1466,40 +1466,6 @@ const VIEW_CSS_TEXT = `
|
|
|
1466
1466
|
line-height: 16px;
|
|
1467
1467
|
}
|
|
1468
1468
|
|
|
1469
|
-
/* Blocked / incompatible card: dimmed, rose badge, no install button */
|
|
1470
|
-
.hqpu-card--blocked {
|
|
1471
|
-
opacity: 0.8;
|
|
1472
|
-
background: color-mix(in srgb, var(--hq-card) 60%, transparent);
|
|
1473
|
-
border-color: var(--hq-border);
|
|
1474
|
-
}
|
|
1475
|
-
.hqpu-card--blocked .hqpu-avatar {
|
|
1476
|
-
background: color-mix(in srgb, var(--hq-card) 50%, transparent);
|
|
1477
|
-
border-color: var(--hq-border);
|
|
1478
|
-
color: var(--hq-faint);
|
|
1479
|
-
}
|
|
1480
|
-
.hqpu-card--blocked .hqpu-card-title { color: var(--hq-muted); }
|
|
1481
|
-
.hqpu-blocked-badge {
|
|
1482
|
-
font-size: 9px;
|
|
1483
|
-
padding: 2px 6px;
|
|
1484
|
-
border-radius: 4px;
|
|
1485
|
-
color: var(--hq-rose);
|
|
1486
|
-
background: color-mix(in srgb, var(--hq-rose) 18%, transparent);
|
|
1487
|
-
border: 0.5px solid color-mix(in srgb, var(--hq-rose) 40%, transparent);
|
|
1488
|
-
}
|
|
1489
|
-
.hqpu-reason-line {
|
|
1490
|
-
display: flex;
|
|
1491
|
-
align-items: flex-start;
|
|
1492
|
-
gap: 4px;
|
|
1493
|
-
margin: 8px 0 0;
|
|
1494
|
-
padding: 6px;
|
|
1495
|
-
border-radius: 5px;
|
|
1496
|
-
border: 0.5px solid color-mix(in srgb, var(--hq-rose) 20%, transparent);
|
|
1497
|
-
background: color-mix(in srgb, var(--hq-rose) 8%, transparent);
|
|
1498
|
-
font-size: 10px;
|
|
1499
|
-
color: var(--hq-rose);
|
|
1500
|
-
}
|
|
1501
|
-
.hqpu-reason-line svg { width: 12px; height: 12px; flex: none; margin-top: 1px; }
|
|
1502
|
-
|
|
1503
1469
|
/* Sticky footer */
|
|
1504
1470
|
.hqpu-footer {
|
|
1505
1471
|
flex: none;
|
|
@@ -1755,7 +1721,11 @@ class UpdateStore implements UpdateStoreLike {
|
|
|
1755
1721
|
if (report.runtime?.updateAvailable) count += 1
|
|
1756
1722
|
if (count === 0) {
|
|
1757
1723
|
const latest = this.recent[0]
|
|
1758
|
-
|
|
1724
|
+
// Keep a visible attention dot for every terminal outcome, including
|
|
1725
|
+
// success. Without this, a background update could finish while the
|
|
1726
|
+
// panel was closed and the user would have no indication that a result
|
|
1727
|
+
// is waiting in the update centre.
|
|
1728
|
+
if (latest !== undefined && TERMINAL_PHASES.has(latest.phase)) {
|
|
1759
1729
|
count = 1
|
|
1760
1730
|
}
|
|
1761
1731
|
}
|
|
@@ -20,12 +20,14 @@
|
|
|
20
20
|
import {
|
|
21
21
|
checkUpdates,
|
|
22
22
|
ignoreOffer,
|
|
23
|
+
ignorePlugin,
|
|
23
24
|
installSet,
|
|
24
25
|
requestRuntimeUpdate,
|
|
25
26
|
runtimeLatestVersion,
|
|
26
27
|
setPluginUpdateSource,
|
|
27
28
|
setRuntimeUpdateSource,
|
|
28
29
|
unignoreOffer,
|
|
30
|
+
unignorePlugin,
|
|
29
31
|
TERMINAL_PHASES,
|
|
30
32
|
type PluginUpdateItem,
|
|
31
33
|
type PluginUpdateReport,
|
|
@@ -101,44 +103,6 @@ export function shortName(name: string): string {
|
|
|
101
103
|
return unscoped.replace(/^dsh-(?:host-|client-)?/, '')
|
|
102
104
|
}
|
|
103
105
|
|
|
104
|
-
/** Normalize the server's block reason to a list (it may arrive as a string). */
|
|
105
|
-
function blockedByList(item: PluginUpdateItem): string[] {
|
|
106
|
-
const by = item.blockedBy
|
|
107
|
-
if (Array.isArray(by)) return by.filter((entry): entry is string => typeof entry === 'string')
|
|
108
|
-
return typeof by === 'string' && by.length > 0 ? [by] : []
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/** The tag tone a state reads as, matching the shell's Tag palette. */
|
|
112
|
-
function stateTone(item: PluginUpdateItem): 'success' | 'info' | 'warning' | 'danger' | 'neutral' {
|
|
113
|
-
switch (item.state) {
|
|
114
|
-
case 'update':
|
|
115
|
-
return 'info'
|
|
116
|
-
case 'install':
|
|
117
|
-
return 'warning'
|
|
118
|
-
case 'blocked':
|
|
119
|
-
return 'danger'
|
|
120
|
-
case 'ignored':
|
|
121
|
-
return 'neutral'
|
|
122
|
-
case 'current':
|
|
123
|
-
return 'success'
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function stateLabel(item: PluginUpdateItem, copy: UpdateCopy): string {
|
|
128
|
-
switch (item.state) {
|
|
129
|
-
case 'current':
|
|
130
|
-
return copy.state.current
|
|
131
|
-
case 'update':
|
|
132
|
-
return copy.state.update
|
|
133
|
-
case 'install':
|
|
134
|
-
return copy.state.install
|
|
135
|
-
case 'ignored':
|
|
136
|
-
return copy.state.ignored
|
|
137
|
-
case 'blocked':
|
|
138
|
-
return copy.state.blocked
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
106
|
/** The one-line version transition under a card's title. */
|
|
143
107
|
function versionLine(item: PluginUpdateItem): string {
|
|
144
108
|
switch (item.state) {
|
|
@@ -154,14 +118,6 @@ function versionLine(item: PluginUpdateItem): string {
|
|
|
154
118
|
}
|
|
155
119
|
}
|
|
156
120
|
|
|
157
|
-
function blockedReason(item: PluginUpdateItem, copy: UpdateCopy): string | undefined {
|
|
158
|
-
const by = blockedByList(item)
|
|
159
|
-
if (by.includes('host')) return copy.blockedReason.host
|
|
160
|
-
if (by.includes('hq-edge-version')) return copy.blockedReason['hq-edge-version']
|
|
161
|
-
if (by.includes('runtime-unknown')) return copy.blockedReason['runtime-unknown']
|
|
162
|
-
return by.length > 0 ? by.join(', ') : undefined
|
|
163
|
-
}
|
|
164
|
-
|
|
165
121
|
/** Two-letter avatar initials for a card (CJK-safe). */
|
|
166
122
|
function avatarInitials(item: PluginUpdateItem): string {
|
|
167
123
|
const src =
|
|
@@ -189,16 +145,16 @@ function changelogDrawer(item: PluginUpdateItem, copy: UpdateCopy, open: boolean
|
|
|
189
145
|
|
|
190
146
|
/** One package card per the mockup: avatar initials, display name + package
|
|
191
147
|
* subtitle, old→new version row (new in emerald), a 日志 toggle expanding the
|
|
192
|
-
* inline changelog.
|
|
193
|
-
* 无法安装 badge and a reason line, and carry NO install button. */
|
|
148
|
+
* inline changelog. */
|
|
194
149
|
function packageCard(
|
|
195
150
|
item: PluginUpdateItem,
|
|
196
151
|
copy: UpdateCopy,
|
|
197
152
|
open: boolean,
|
|
198
153
|
onToggle: () => void,
|
|
154
|
+
onInstall: () => void,
|
|
155
|
+
onIgnore: () => void,
|
|
199
156
|
): HTMLLIElement {
|
|
200
|
-
const
|
|
201
|
-
const card = el('li', blocked ? 'hqpu-card hqpu-card--blocked' : 'hqpu-card')
|
|
157
|
+
const card = el('li', 'hqpu-card')
|
|
202
158
|
card.dataset.pluginName = item.name
|
|
203
159
|
card.dataset.state = item.state
|
|
204
160
|
|
|
@@ -229,9 +185,22 @@ function packageCard(
|
|
|
229
185
|
left.append(avatar, main)
|
|
230
186
|
|
|
231
187
|
const right = el('div', 'hqpu-card-end')
|
|
232
|
-
if (
|
|
233
|
-
|
|
234
|
-
|
|
188
|
+
if (item.state === 'update' || item.state === 'install') {
|
|
189
|
+
const install = el('button', 'hqpu-log-toggle', copy.installOne)
|
|
190
|
+
install.type = 'button'
|
|
191
|
+
install.title = copy.installOneTip
|
|
192
|
+
install.addEventListener('click', onInstall)
|
|
193
|
+
const ignore = el('button', 'hqpu-log-toggle', copy.ignore)
|
|
194
|
+
ignore.type = 'button'
|
|
195
|
+
ignore.addEventListener('click', onIgnore)
|
|
196
|
+
right.append(install, ignore)
|
|
197
|
+
} else if (item.state === 'ignored') {
|
|
198
|
+
const show = el('button', 'hqpu-log-toggle', copy.unignore)
|
|
199
|
+
show.type = 'button'
|
|
200
|
+
show.addEventListener('click', onIgnore)
|
|
201
|
+
right.append(show)
|
|
202
|
+
}
|
|
203
|
+
if (Array.isArray(item.changes) && item.changes.length > 0) {
|
|
235
204
|
const toggle = el('button', 'hqpu-log-toggle')
|
|
236
205
|
toggle.type = 'button'
|
|
237
206
|
toggle.dataset.open = open ? 'true' : 'false'
|
|
@@ -243,26 +212,9 @@ function packageCard(
|
|
|
243
212
|
card.append(head)
|
|
244
213
|
|
|
245
214
|
// Inline changelog drawer (actionable items only).
|
|
246
|
-
if (
|
|
215
|
+
if (Array.isArray(item.changes) && item.changes.length > 0) {
|
|
247
216
|
card.append(changelogDrawer(item, copy, open))
|
|
248
217
|
}
|
|
249
|
-
|
|
250
|
-
// Blocked reason line(s): mapped copy plus the server's full reasons/notes.
|
|
251
|
-
if (blocked) {
|
|
252
|
-
const reason = blockedReason(item, copy)
|
|
253
|
-
const entries = [
|
|
254
|
-
...(reason !== undefined ? [reason] : []),
|
|
255
|
-
...[...(item.reasons ?? []), ...(item.notes ?? [])].filter(
|
|
256
|
-
(e): e is string => typeof e === 'string' && e.length > 0,
|
|
257
|
-
),
|
|
258
|
-
]
|
|
259
|
-
for (const entry of entries) {
|
|
260
|
-
const line = el('p', 'hqpu-reason-line')
|
|
261
|
-
line.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 5V8.5M8 11.25h.01M14.5 8a6.5 6.5 0 11-13 0 6.5 6.5 0 0113 0Z"/></svg>`
|
|
262
|
-
line.append(document.createTextNode(entry))
|
|
263
|
-
card.append(line)
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
218
|
return card
|
|
267
219
|
}
|
|
268
220
|
|
|
@@ -275,6 +227,8 @@ function group(
|
|
|
275
227
|
allOpen: boolean,
|
|
276
228
|
onToggle: (name: string) => void,
|
|
277
229
|
onToggleAll: () => void,
|
|
230
|
+
onInstall: (name: string) => void,
|
|
231
|
+
onIgnore: (item: PluginUpdateItem) => void,
|
|
278
232
|
): HTMLElement | null {
|
|
279
233
|
if (items.length === 0) return null
|
|
280
234
|
const section = el('section', 'hqpu-group')
|
|
@@ -286,7 +240,7 @@ function group(
|
|
|
286
240
|
titleWrap.append(el('span', 'hqpu-count-pill', String(items.length)))
|
|
287
241
|
head.append(titleWrap)
|
|
288
242
|
// Expand-all only makes sense over actionable, changelog-bearing cards.
|
|
289
|
-
const actionable = items.some((i) =>
|
|
243
|
+
const actionable = items.some((i) => Array.isArray(i.changes) && i.changes.length > 0)
|
|
290
244
|
if (actionable) {
|
|
291
245
|
const all = el('button', 'hqpu-all-changelog', copy.ui.allChangelog)
|
|
292
246
|
all.type = 'button'
|
|
@@ -296,7 +250,7 @@ function group(
|
|
|
296
250
|
|
|
297
251
|
const list = el('ul', 'hqpu-cards')
|
|
298
252
|
for (const item of items) {
|
|
299
|
-
list.append(packageCard(item, copy, allOpen || changelogOpen.has(item.name), () => onToggle(item.name)))
|
|
253
|
+
list.append(packageCard(item, copy, allOpen || changelogOpen.has(item.name), () => onToggle(item.name), () => onInstall(item.name), () => onIgnore(item)))
|
|
300
254
|
}
|
|
301
255
|
|
|
302
256
|
section.append(head, list)
|
|
@@ -1119,6 +1073,8 @@ function renderList(
|
|
|
1119
1073
|
onToggleCard: (name: string) => void,
|
|
1120
1074
|
onToggleAll: () => void,
|
|
1121
1075
|
onRetryCheck: () => void,
|
|
1076
|
+
onInstall: (name: string) => void,
|
|
1077
|
+
onIgnore: (item: PluginUpdateItem) => void,
|
|
1122
1078
|
): void {
|
|
1123
1079
|
section.textContent = ''
|
|
1124
1080
|
if (report === null) {
|
|
@@ -1144,16 +1100,17 @@ function renderList(
|
|
|
1144
1100
|
const byState = (state: PluginUpdateItem['state']): PluginUpdateItem[] =>
|
|
1145
1101
|
report.items.filter((item) => item.state === state)
|
|
1146
1102
|
|
|
1147
|
-
// Most important first: what can be acted on, then what
|
|
1148
|
-
//
|
|
1103
|
+
// Most important first: what can be acted on, then what was muted, then what
|
|
1104
|
+
// is already settled. Blocked plugins are intentionally skipped — the server
|
|
1105
|
+
// still reports them, but they carry no user-actionable offer, so surfacing
|
|
1106
|
+
// them only adds noise without a path forward.
|
|
1149
1107
|
const order: Array<[string, PluginUpdateItem[]]> = [
|
|
1150
1108
|
[copy.group.actionable, [...byState('update'), ...byState('install')]],
|
|
1151
|
-
[copy.group.blocked, byState('blocked')],
|
|
1152
1109
|
[copy.group.ignored, byState('ignored')],
|
|
1153
1110
|
[copy.group.current, byState('current')],
|
|
1154
1111
|
]
|
|
1155
1112
|
for (const [heading, items] of order) {
|
|
1156
|
-
const node = group(heading, items, copy, changelogOpen, allOpen, onToggleCard, onToggleAll)
|
|
1113
|
+
const node = group(heading, items, copy, changelogOpen, allOpen, onToggleCard, onToggleAll, onInstall, onIgnore)
|
|
1157
1114
|
if (node !== null) section.append(node)
|
|
1158
1115
|
}
|
|
1159
1116
|
}
|
|
@@ -1190,7 +1147,7 @@ function renderPluginSource(
|
|
|
1190
1147
|
}
|
|
1191
1148
|
|
|
1192
1149
|
/** Run the install transaction (a new durable job), then let the store poll it. */
|
|
1193
|
-
async function runInstall(store: UpdateStoreLike, copy: UpdateCopy): Promise<void> {
|
|
1150
|
+
async function runInstall(store: UpdateStoreLike, copy: UpdateCopy, pluginNames?: string[]): Promise<void> {
|
|
1194
1151
|
const report = store.report()
|
|
1195
1152
|
if (report === null) return
|
|
1196
1153
|
// Without an evaluated set there is nothing to install: hq-edge has not read
|
|
@@ -1204,7 +1161,7 @@ async function runInstall(store: UpdateStoreLike, copy: UpdateCopy): Promise<voi
|
|
|
1204
1161
|
// dead code — runInstall never called it).
|
|
1205
1162
|
store.setInstalling(true)
|
|
1206
1163
|
try {
|
|
1207
|
-
const result = await installSet(report.manifestVersion)
|
|
1164
|
+
const result = await installSet(report.manifestVersion, pluginNames)
|
|
1208
1165
|
if (result.error !== undefined) {
|
|
1209
1166
|
store.setInstalling(false)
|
|
1210
1167
|
showToast(`${copy.installError}: ${result.error}`, 'error')
|
|
@@ -1287,7 +1244,7 @@ function renderSettingsPanel(
|
|
|
1287
1244
|
* The sticky footer: left meta ("准备安装 N 项兼容插件" / "版本集: vX"), a
|
|
1288
1245
|
* full-width primary "一键更新全套插件 (vX)" (install = one durable job),
|
|
1289
1246
|
* a secondary 忽略 wired to the existing ignore/unignore affordance, and the
|
|
1290
|
-
* footnote. The count EXCLUDES
|
|
1247
|
+
* footnote. The count EXCLUDES ignored / already-current entries.
|
|
1291
1248
|
*/
|
|
1292
1249
|
function renderFooter(
|
|
1293
1250
|
footer: HTMLElement,
|
|
@@ -1685,6 +1642,17 @@ export function openUpdateView(store: UpdateStoreLike, copy: UpdateCopy): void {
|
|
|
1685
1642
|
() => {
|
|
1686
1643
|
void store.check()
|
|
1687
1644
|
},
|
|
1645
|
+
(name) => {
|
|
1646
|
+
void runInstall(store, copy, [name])
|
|
1647
|
+
},
|
|
1648
|
+
(item) => {
|
|
1649
|
+
const report = store.report()
|
|
1650
|
+
if (report?.manifestVersion === null || report?.manifestVersion === undefined) return
|
|
1651
|
+
const action = item.state === 'ignored'
|
|
1652
|
+
? unignorePlugin(report.manifestVersion, item.name)
|
|
1653
|
+
: ignorePlugin(report.manifestVersion, item.name)
|
|
1654
|
+
void action.then(() => store.check()).catch(() => undefined)
|
|
1655
|
+
},
|
|
1688
1656
|
)
|
|
1689
1657
|
}
|
|
1690
1658
|
renderRuntime(runtimeSection, report, copy, runtimeActions, dismissedAlert, () => {
|
package/src/client/version.ts
CHANGED
|
@@ -120,8 +120,8 @@ export function isVersionUpgrade(currentVersion: string | null, availableVersion
|
|
|
120
120
|
* to `current`: the plugin is at (or ahead of) the offered release, there is
|
|
121
121
|
* nothing to install, and no install control may be offered for it. Every
|
|
122
122
|
* other state passes through untouched — `install` (plugin absent locally)
|
|
123
|
-
* has no local version to compare, and `
|
|
124
|
-
*
|
|
123
|
+
* has no local version to compare, and `ignored`/`current` are already
|
|
124
|
+
* non-actionable. Unknown server fields are preserved verbatim: the
|
|
125
125
|
* client stays a thin reader of the hq-edge report shape.
|
|
126
126
|
*/
|
|
127
127
|
export function normalizePluginUpdateItem<T extends PluginUpdateItem>(item: T): T {
|