@huaqiu/dsh-tool-plugin-update 0.5.21 → 0.6.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/lib/client.js +1144 -259
- package/lib/client.js.map +1 -1
- package/package.json +3 -3
- package/src/client/api.ts +12 -8
- package/src/client/copy.ts +46 -18
- package/src/client/icons.ts +91 -0
- package/src/client/index.ts +663 -121
- package/src/client/update-view.ts +478 -160
package/src/client/index.ts
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
} from '@huaqiu/dsh-sidebar-entry'
|
|
24
24
|
import { checkUpdates, fetchReport, type PluginUpdateReport } from './api.js'
|
|
25
25
|
import { COPY } from './copy.js'
|
|
26
|
+
import { PLUGIN_ICON } from './icons.js'
|
|
26
27
|
import { openUpdateView, type UpdateStoreLike } from './update-view.js'
|
|
27
28
|
|
|
28
29
|
/** Stable data attributes identifying the injected entry row. */
|
|
@@ -47,17 +48,15 @@ function entryCopy(lang: HostLocale | undefined): { label: string; tooltip: stri
|
|
|
47
48
|
: { label: COPY.zh.entryLabel, tooltip: COPY.zh.entryTooltip }
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
/**
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
<g fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round">
|
|
54
|
-
<path d="M2.5 8a5.5 5.5 0 1 1 1.61 3.89" />
|
|
55
|
-
<path d="M2.5 8V4.75M2.5 8h3.25" />
|
|
56
|
-
</g>
|
|
57
|
-
</svg>`
|
|
51
|
+
/** The sidebar glyph — the same pinwheel the shell's own Plugins entry draws
|
|
52
|
+
* (`IconPluginPinwheelOutlineRegular`), so both rows read as one feature. */
|
|
53
|
+
export const UPDATE_ICON = PLUGIN_ICON(16)
|
|
58
54
|
|
|
59
55
|
/** Sidebar entry styles — matched to the family rows (same inset/padding/gap
|
|
60
|
-
* as the shell's own Plugins entry
|
|
56
|
+
* as the shell's own Plugins entry, `SidebarRoot.module.css` `.panelRow`).
|
|
57
|
+
* Every colour rides a `--dsw-alias-*` token so the row follows the shell's
|
|
58
|
+
* light/dark theme; the badge foreground uses the toast label, the shell's
|
|
59
|
+
* "always light ink on a saturated surface" token. */
|
|
61
60
|
const ENTRY_CSS_TEXT = `
|
|
62
61
|
.hqpu-sidebar-entry {
|
|
63
62
|
box-sizing: border-box;
|
|
@@ -70,7 +69,7 @@ const ENTRY_CSS_TEXT = `
|
|
|
70
69
|
padding: 7px 8px;
|
|
71
70
|
background: transparent;
|
|
72
71
|
border: none;
|
|
73
|
-
border-radius:
|
|
72
|
+
border-radius: var(--dsw-radius-md);
|
|
74
73
|
color: var(--dsw-alias-label-primary);
|
|
75
74
|
cursor: pointer;
|
|
76
75
|
font: inherit;
|
|
@@ -85,6 +84,10 @@ const ENTRY_CSS_TEXT = `
|
|
|
85
84
|
background: var(--dsw-alias-interactive-bg-hover);
|
|
86
85
|
color: var(--dsw-alias-label-primary);
|
|
87
86
|
}
|
|
87
|
+
.hqpu-sidebar-entry:focus-visible {
|
|
88
|
+
outline: var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));
|
|
89
|
+
outline-offset: -2px;
|
|
90
|
+
}
|
|
88
91
|
.hqpu-sidebar-entry__icon {
|
|
89
92
|
display: inline-flex;
|
|
90
93
|
align-items: center;
|
|
@@ -108,11 +111,13 @@ const ENTRY_CSS_TEXT = `
|
|
|
108
111
|
height: 16px;
|
|
109
112
|
padding: 0 5px;
|
|
110
113
|
border-radius: 8px;
|
|
111
|
-
background: var(--dsw-alias-
|
|
112
|
-
color:
|
|
114
|
+
background: var(--dsw-alias-state-error-primary);
|
|
115
|
+
color: var(--dsw-alias-toast-label);
|
|
113
116
|
font-size: 11px;
|
|
114
117
|
line-height: 16px;
|
|
118
|
+
font-weight: 500;
|
|
115
119
|
text-align: center;
|
|
120
|
+
font-variant-numeric: tabular-nums;
|
|
116
121
|
}
|
|
117
122
|
/* Collapsed rail: icon-only, centered — same 36px square the shell uses. */
|
|
118
123
|
[data-sidebar-collapsed] .hqpu-sidebar-entry {
|
|
@@ -132,9 +137,28 @@ const ENTRY_CSS_TEXT = `
|
|
|
132
137
|
}
|
|
133
138
|
`
|
|
134
139
|
|
|
135
|
-
/**
|
|
136
|
-
*
|
|
140
|
+
/**
|
|
141
|
+
* View styles (hqpu-*). Plain overlay + panel, drawn with the SAME design
|
|
142
|
+
* tokens and geometry the shell's own plugin manager uses
|
|
143
|
+
* (`ui-plugin-manager/src/client/PluginManagerPage.module.css` +
|
|
144
|
+
* `ui-primitives/src/{Modal,Toast,Tag,StateDot}.module.css`), so this page
|
|
145
|
+
* reads as part of the product rather than a bolt-on.
|
|
146
|
+
*
|
|
147
|
+
* Two rules keep it honest across themes:
|
|
148
|
+
* 1. every colour is a `--dsw-alias-*` token — no literal hex/rgb, so light
|
|
149
|
+
* and dark both come out right (the old sheet invented
|
|
150
|
+
* `--dsw-alias-bg-surface` / `--dsw-alias-line-border` / `--dsw-alias-bg-badge`,
|
|
151
|
+
* none of which exist, and so always fell back to a white panel);
|
|
152
|
+
* 2. `color-mix()` tints come from the state token itself, so a palette
|
|
153
|
+
* change moves fill and ink together.
|
|
154
|
+
*
|
|
155
|
+
* The chrome (panel, scrim, header, footer) follows `ui-primitives/Modal`,
|
|
156
|
+
* because this view IS a dialog; the body follows `PluginManagerPage`, because
|
|
157
|
+
* that is the content it heads. The one token the bundled shell does not
|
|
158
|
+
* publish is backfilled by {@link ensureDesignTokens} — see the note there.
|
|
159
|
+
*/
|
|
137
160
|
const VIEW_CSS_TEXT = `
|
|
161
|
+
/* ── Overlay + panel (ui-primitives Modal.module.css) ─────────────────── */
|
|
138
162
|
.hqpu-overlay {
|
|
139
163
|
position: fixed;
|
|
140
164
|
inset: 0;
|
|
@@ -142,177 +166,686 @@ const VIEW_CSS_TEXT = `
|
|
|
142
166
|
display: flex;
|
|
143
167
|
align-items: center;
|
|
144
168
|
justify-content: center;
|
|
145
|
-
|
|
169
|
+
/* The frame's own top inset keeps a tall card clear of the window chrome. */
|
|
170
|
+
padding: max(24px, var(--dsh-frame-overlay-top, 24px)) 24px;
|
|
171
|
+
}
|
|
172
|
+
.hqpu-scrim {
|
|
173
|
+
position: absolute;
|
|
174
|
+
/* Only paint excludes the native caption; the root still blocks clicks. */
|
|
175
|
+
inset: var(--dsh-frame-chrome-top, 0px) 0 0;
|
|
176
|
+
background: var(--dsw-alias-bg-mask-1);
|
|
177
|
+
animation: hqpu-fade var(--ds-transition-duration, 0.2s) var(--ds-ease-in-out, ease);
|
|
146
178
|
}
|
|
147
179
|
.hqpu-panel {
|
|
180
|
+
position: relative;
|
|
181
|
+
z-index: 1;
|
|
148
182
|
box-sizing: border-box;
|
|
149
|
-
width: min(640px, calc(100vw - 48px));
|
|
150
|
-
max-height: min(80vh, 720px);
|
|
151
183
|
display: flex;
|
|
152
184
|
flex-direction: column;
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
box-shadow: 0 24px 64px rgba(0, 0, 0, 0.24);
|
|
185
|
+
gap: 20px;
|
|
186
|
+
width: min(920px, 100%);
|
|
187
|
+
max-height: 100%;
|
|
157
188
|
overflow: hidden;
|
|
189
|
+
border: 0;
|
|
190
|
+
border-radius: var(--dsw-radius-panel);
|
|
191
|
+
background: var(--dsw-alias-bg-layer-2);
|
|
192
|
+
box-shadow: var(--dsw-elevation-prominent);
|
|
193
|
+
color: var(--dsw-alias-label-primary);
|
|
194
|
+
animation: hqpu-panel-in var(--ds-transition-duration, 0.2s) var(--ds-ease-in-out, ease);
|
|
195
|
+
}
|
|
196
|
+
.hqpu-panel:focus {
|
|
197
|
+
outline: none;
|
|
198
|
+
}
|
|
199
|
+
@keyframes hqpu-fade {
|
|
200
|
+
from { opacity: 0; }
|
|
201
|
+
to { opacity: 1; }
|
|
202
|
+
}
|
|
203
|
+
@keyframes hqpu-panel-in {
|
|
204
|
+
from { opacity: 0; }
|
|
205
|
+
to { opacity: 1; }
|
|
158
206
|
}
|
|
159
|
-
|
|
207
|
+
|
|
208
|
+
/* ── Head row (ui-primitives Modal .header) ─────────────────────────────── */
|
|
209
|
+
.hqpu-head {
|
|
160
210
|
display: flex;
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
211
|
+
flex: none;
|
|
212
|
+
flex-wrap: wrap;
|
|
213
|
+
align-items: flex-start;
|
|
214
|
+
justify-content: space-between;
|
|
215
|
+
gap: 16px;
|
|
216
|
+
box-sizing: border-box;
|
|
217
|
+
/* The official Title row: 22/14/12/24 — the toolbar sits 10px further right
|
|
218
|
+
* than the body content on purpose, matching the shell's own close button. */
|
|
219
|
+
padding: 22px 14px 12px 24px;
|
|
220
|
+
}
|
|
221
|
+
.hqpu-head-main {
|
|
222
|
+
min-width: 0;
|
|
223
|
+
max-width: 100%;
|
|
165
224
|
}
|
|
166
225
|
.hqpu-title {
|
|
226
|
+
margin: 0;
|
|
227
|
+
font-size: 20px;
|
|
228
|
+
font-weight: 500;
|
|
229
|
+
line-height: 28px;
|
|
230
|
+
color: var(--dsw-alias-label-primary);
|
|
231
|
+
}
|
|
232
|
+
.hqpu-intro {
|
|
233
|
+
display: flex;
|
|
234
|
+
align-items: center;
|
|
235
|
+
gap: 4px;
|
|
236
|
+
margin: 4px 0 0;
|
|
237
|
+
font-size: 13px;
|
|
238
|
+
line-height: 20px;
|
|
239
|
+
color: var(--dsw-alias-label-secondary);
|
|
240
|
+
}
|
|
241
|
+
.hqpu-info {
|
|
242
|
+
display: inline-flex;
|
|
243
|
+
align-items: center;
|
|
244
|
+
justify-content: center;
|
|
245
|
+
flex: none;
|
|
246
|
+
width: 20px;
|
|
247
|
+
height: 20px;
|
|
248
|
+
padding: 0;
|
|
249
|
+
border: 0;
|
|
250
|
+
border-radius: var(--dsw-radius-sm);
|
|
251
|
+
background: transparent;
|
|
252
|
+
color: var(--dsw-alias-label-caption);
|
|
253
|
+
cursor: pointer;
|
|
254
|
+
}
|
|
255
|
+
.hqpu-info:hover {
|
|
256
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
257
|
+
color: var(--dsw-alias-label-secondary);
|
|
258
|
+
}
|
|
259
|
+
.hqpu-info:focus-visible {
|
|
260
|
+
outline: var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));
|
|
261
|
+
outline-offset: 1px;
|
|
262
|
+
}
|
|
263
|
+
.hqpu-toolbar {
|
|
264
|
+
display: flex;
|
|
265
|
+
flex: none;
|
|
266
|
+
align-items: center;
|
|
267
|
+
gap: 16px;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/* ── Scrolling body: the page's centred column ────────────────────────── */
|
|
271
|
+
.hqpu-scroll {
|
|
167
272
|
flex: 1;
|
|
168
|
-
|
|
169
|
-
|
|
273
|
+
min-height: 0;
|
|
274
|
+
overflow-y: auto;
|
|
275
|
+
box-sizing: border-box;
|
|
276
|
+
/* The panel's 20px gap already separates this from the head and footer. */
|
|
277
|
+
padding: 0 clamp(16px, 3vw, 32px) 4px;
|
|
278
|
+
/* Anything that scrolls inside the panel draws the l2 thumb. */
|
|
279
|
+
--dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2);
|
|
280
|
+
--dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2);
|
|
170
281
|
}
|
|
171
|
-
.hqpu-
|
|
172
|
-
overflow: auto;
|
|
173
|
-
padding: 16px;
|
|
282
|
+
.hqpu-column {
|
|
174
283
|
display: flex;
|
|
175
284
|
flex-direction: column;
|
|
176
|
-
gap:
|
|
285
|
+
gap: 24px;
|
|
286
|
+
width: 100%;
|
|
287
|
+
max-width: 880px;
|
|
288
|
+
margin: 0 auto;
|
|
289
|
+
}
|
|
290
|
+
/* A stack of groups (or the single toast host) inside the column. */
|
|
291
|
+
.hqpu-stack {
|
|
292
|
+
display: flex;
|
|
293
|
+
flex-direction: column;
|
|
294
|
+
gap: 24px;
|
|
177
295
|
}
|
|
178
|
-
.hqpu-
|
|
296
|
+
.hqpu-toast-host:empty {
|
|
297
|
+
display: none;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/* ── Groups (PluginManagerPage .group) ────────────────────────────────── */
|
|
301
|
+
.hqpu-group {
|
|
179
302
|
display: flex;
|
|
180
303
|
flex-direction: column;
|
|
181
304
|
gap: 8px;
|
|
182
305
|
}
|
|
183
|
-
.hqpu-
|
|
306
|
+
.hqpu-group-head {
|
|
184
307
|
display: flex;
|
|
185
308
|
align-items: center;
|
|
186
309
|
gap: 8px;
|
|
187
|
-
font-size: 12px;
|
|
188
|
-
color: var(--dsw-alias-label-secondary, #646a73);
|
|
189
310
|
}
|
|
190
|
-
.hqpu-
|
|
191
|
-
|
|
192
|
-
|
|
311
|
+
.hqpu-group-title {
|
|
312
|
+
flex: 1;
|
|
313
|
+
min-width: 0;
|
|
314
|
+
margin: 0;
|
|
315
|
+
font-size: 14px;
|
|
316
|
+
font-weight: 500;
|
|
317
|
+
line-height: 22px;
|
|
318
|
+
color: var(--dsw-alias-label-primary);
|
|
193
319
|
}
|
|
194
|
-
.hqpu-
|
|
195
|
-
|
|
320
|
+
.hqpu-group-count {
|
|
321
|
+
flex: none;
|
|
322
|
+
font-size: 12px;
|
|
323
|
+
line-height: 18px;
|
|
324
|
+
color: var(--dsw-alias-label-tertiary);
|
|
325
|
+
font-variant-numeric: tabular-nums;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/* ── Cards (PluginManagerPage .card) ──────────────────────────────────── */
|
|
329
|
+
.hqpu-cards {
|
|
196
330
|
margin: 0;
|
|
197
331
|
padding: 0;
|
|
332
|
+
list-style: none;
|
|
198
333
|
display: flex;
|
|
199
334
|
flex-direction: column;
|
|
200
|
-
gap:
|
|
335
|
+
gap: 2px;
|
|
201
336
|
}
|
|
202
|
-
.hqpu-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
337
|
+
.hqpu-card {
|
|
338
|
+
min-width: 0;
|
|
339
|
+
/* The hover surface reaches 8px past the content on both sides. */
|
|
340
|
+
margin: 0 -8px;
|
|
341
|
+
border-radius: var(--dsw-radius-xl);
|
|
342
|
+
}
|
|
343
|
+
.hqpu-card:hover {
|
|
344
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
345
|
+
}
|
|
346
|
+
.hqpu-card-head {
|
|
347
|
+
display: flex;
|
|
206
348
|
align-items: center;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
349
|
+
gap: 14px;
|
|
350
|
+
padding: 8px;
|
|
351
|
+
}
|
|
352
|
+
.hqpu-card-icon {
|
|
353
|
+
display: inline-flex;
|
|
354
|
+
flex: none;
|
|
355
|
+
align-items: center;
|
|
356
|
+
justify-content: center;
|
|
357
|
+
width: 48px;
|
|
358
|
+
height: 48px;
|
|
359
|
+
border: 0.5px solid var(--dsw-alias-border-l3);
|
|
360
|
+
border-radius: var(--dsw-radius-lg);
|
|
361
|
+
color: var(--dsw-alias-label-secondary);
|
|
362
|
+
}
|
|
363
|
+
.hqpu-card-icon svg {
|
|
364
|
+
display: block;
|
|
365
|
+
width: 24px;
|
|
366
|
+
height: 24px;
|
|
367
|
+
}
|
|
368
|
+
.hqpu-card-main {
|
|
369
|
+
flex: 1;
|
|
370
|
+
min-width: 0;
|
|
371
|
+
display: flex;
|
|
372
|
+
flex-direction: column;
|
|
373
|
+
gap: 4px;
|
|
374
|
+
}
|
|
375
|
+
.hqpu-title-row {
|
|
376
|
+
display: flex;
|
|
377
|
+
align-items: center;
|
|
378
|
+
flex-wrap: wrap;
|
|
379
|
+
gap: 8px;
|
|
380
|
+
min-width: 0;
|
|
211
381
|
}
|
|
212
|
-
.hqpu-
|
|
382
|
+
.hqpu-card-title {
|
|
383
|
+
min-width: 0;
|
|
384
|
+
font-size: 14px;
|
|
213
385
|
font-weight: 500;
|
|
386
|
+
line-height: 20px;
|
|
387
|
+
color: var(--dsw-alias-label-primary);
|
|
214
388
|
overflow: hidden;
|
|
215
389
|
text-overflow: ellipsis;
|
|
390
|
+
white-space: nowrap;
|
|
216
391
|
}
|
|
217
|
-
.hqpu-
|
|
218
|
-
|
|
219
|
-
|
|
392
|
+
.hqpu-card-desc {
|
|
393
|
+
font-size: 13px;
|
|
394
|
+
line-height: 18px;
|
|
395
|
+
color: var(--dsw-alias-label-tertiary);
|
|
396
|
+
display: -webkit-box;
|
|
397
|
+
-webkit-box-orient: vertical;
|
|
398
|
+
-webkit-line-clamp: 1;
|
|
399
|
+
overflow: hidden;
|
|
400
|
+
overflow-wrap: anywhere;
|
|
220
401
|
}
|
|
221
|
-
.hqpu-
|
|
222
|
-
|
|
402
|
+
.hqpu-card-end {
|
|
403
|
+
position: relative;
|
|
404
|
+
z-index: 1;
|
|
405
|
+
display: inline-flex;
|
|
406
|
+
flex: none;
|
|
407
|
+
align-items: center;
|
|
408
|
+
gap: 8px;
|
|
409
|
+
}
|
|
410
|
+
.hqpu-card-reason {
|
|
411
|
+
margin: 0;
|
|
412
|
+
padding: 0 8px 8px 70px;
|
|
413
|
+
font-size: 12px;
|
|
414
|
+
line-height: 18px;
|
|
415
|
+
color: var(--dsw-alias-state-error-primary);
|
|
416
|
+
overflow-wrap: anywhere;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/* ── Tags (ui-primitives Tag.module.css) ──────────────────────────────── */
|
|
420
|
+
.hqpu-tag {
|
|
421
|
+
display: inline-flex;
|
|
422
|
+
align-items: center;
|
|
423
|
+
flex: none;
|
|
223
424
|
border-radius: 999px;
|
|
425
|
+
corner-shape: round;
|
|
426
|
+
padding: 1px 8px;
|
|
224
427
|
font-size: 11px;
|
|
225
|
-
|
|
428
|
+
font-weight: 500;
|
|
429
|
+
line-height: 17px;
|
|
430
|
+
white-space: nowrap;
|
|
226
431
|
}
|
|
227
|
-
.hqpu-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
.hqpu-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
432
|
+
.hqpu-tag[data-tone='success'] {
|
|
433
|
+
background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 10%, transparent);
|
|
434
|
+
color: var(--dsw-alias-state-success-primary);
|
|
435
|
+
}
|
|
436
|
+
.hqpu-tag[data-tone='info'] {
|
|
437
|
+
background: color-mix(in srgb, var(--dsw-alias-state-business-primary) 10%, transparent);
|
|
438
|
+
color: var(--dsw-alias-state-business-primary);
|
|
439
|
+
}
|
|
440
|
+
.hqpu-tag[data-tone='warning'] {
|
|
441
|
+
background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 12%, transparent);
|
|
442
|
+
color: var(--dsw-alias-state-warn-primary);
|
|
443
|
+
}
|
|
444
|
+
.hqpu-tag[data-tone='danger'] {
|
|
445
|
+
background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);
|
|
446
|
+
color: var(--dsw-alias-state-error-primary);
|
|
235
447
|
}
|
|
236
|
-
.hqpu-
|
|
448
|
+
.hqpu-tag[data-tone='neutral'] {
|
|
449
|
+
background: var(--dsw-alias-bg-module-platform);
|
|
450
|
+
color: var(--dsw-alias-label-secondary);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/* ── Fact rows (PluginManagerPage .facts / .rows) ─────────────────────── */
|
|
454
|
+
.hqpu-facts {
|
|
455
|
+
margin: 0;
|
|
456
|
+
display: flex;
|
|
457
|
+
flex-direction: column;
|
|
458
|
+
}
|
|
459
|
+
.hqpu-facts > div {
|
|
237
460
|
display: flex;
|
|
238
461
|
align-items: center;
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
462
|
+
justify-content: space-between;
|
|
463
|
+
gap: 16px;
|
|
464
|
+
min-height: 44px;
|
|
465
|
+
padding: 8px 2px;
|
|
466
|
+
border-bottom: 0.5px solid var(--dsw-alias-border-l2);
|
|
467
|
+
}
|
|
468
|
+
.hqpu-facts > div:last-child {
|
|
469
|
+
border-bottom: 0;
|
|
470
|
+
}
|
|
471
|
+
.hqpu-facts dt {
|
|
472
|
+
flex: none;
|
|
473
|
+
font-size: 13px;
|
|
474
|
+
line-height: 20px;
|
|
475
|
+
color: var(--dsw-alias-label-secondary);
|
|
476
|
+
}
|
|
477
|
+
.hqpu-facts dd {
|
|
478
|
+
min-width: 0;
|
|
479
|
+
margin: 0;
|
|
480
|
+
font-size: 13px;
|
|
481
|
+
line-height: 20px;
|
|
482
|
+
text-align: end;
|
|
483
|
+
overflow-wrap: anywhere;
|
|
484
|
+
color: var(--dsw-alias-label-primary);
|
|
485
|
+
}
|
|
486
|
+
.hqpu-facts code {
|
|
487
|
+
padding: 2px 6px;
|
|
488
|
+
border-radius: var(--dsw-radius-xs);
|
|
489
|
+
background: var(--dsw-alias-bg-module-platform);
|
|
490
|
+
font-family: var(--dsw-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
|
|
243
491
|
font-size: 12px;
|
|
244
|
-
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/* ── Banner (PluginManagerPage .installSafety) ────────────────────────── */
|
|
495
|
+
.hqpu-banner {
|
|
496
|
+
display: flex;
|
|
497
|
+
align-items: flex-start;
|
|
498
|
+
gap: 8px;
|
|
499
|
+
padding: 10px 12px;
|
|
500
|
+
border-radius: var(--dsw-radius-md);
|
|
501
|
+
background: var(--dsw-alias-state-warn-tertiary);
|
|
502
|
+
font-size: 12.5px;
|
|
503
|
+
line-height: 18px;
|
|
504
|
+
color: var(--dsw-alias-label-secondary);
|
|
505
|
+
}
|
|
506
|
+
.hqpu-banner > svg {
|
|
507
|
+
flex: none;
|
|
508
|
+
margin-top: 2px;
|
|
509
|
+
color: var(--dsw-alias-state-warn-primary);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/* ── Status, empty, failure (PluginManagerPage .status / .empty / .failure) */
|
|
513
|
+
.hqpu-status {
|
|
514
|
+
margin: 0;
|
|
515
|
+
font-size: 13px;
|
|
516
|
+
line-height: 20px;
|
|
517
|
+
color: var(--dsw-alias-label-tertiary);
|
|
518
|
+
}
|
|
519
|
+
.hqpu-status--dot {
|
|
520
|
+
display: inline-flex;
|
|
521
|
+
align-items: center;
|
|
522
|
+
gap: 6px;
|
|
523
|
+
}
|
|
524
|
+
.hqpu-failure {
|
|
525
|
+
display: flex;
|
|
526
|
+
align-items: center;
|
|
527
|
+
gap: 10px;
|
|
528
|
+
color: var(--dsw-alias-state-error-primary);
|
|
245
529
|
}
|
|
246
530
|
.hqpu-empty {
|
|
247
|
-
padding:
|
|
531
|
+
padding: 32px 0;
|
|
248
532
|
text-align: center;
|
|
249
|
-
color: var(--dsw-alias-label-tertiary, #8f959e);
|
|
250
533
|
font-size: 13px;
|
|
534
|
+
line-height: 20px;
|
|
535
|
+
color: var(--dsw-alias-label-tertiary);
|
|
251
536
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
537
|
+
|
|
538
|
+
/* ── State dot + spinner (ui-primitives StateDot.module.css) ──────────── */
|
|
539
|
+
.hqpu-dot {
|
|
540
|
+
position: relative;
|
|
541
|
+
display: inline-block;
|
|
542
|
+
flex: none;
|
|
543
|
+
width: 10px;
|
|
544
|
+
height: 10px;
|
|
255
545
|
}
|
|
256
|
-
.hqpu-
|
|
257
|
-
|
|
258
|
-
|
|
546
|
+
.hqpu-dot::after {
|
|
547
|
+
content: '';
|
|
548
|
+
position: absolute;
|
|
549
|
+
inset: 20%;
|
|
550
|
+
border-radius: 50%;
|
|
551
|
+
corner-shape: round;
|
|
552
|
+
background: currentColor;
|
|
259
553
|
}
|
|
260
|
-
.hqpu-
|
|
261
|
-
|
|
262
|
-
|
|
554
|
+
.hqpu-dot[data-state='done'] { color: var(--dsw-alias-state-success-primary); }
|
|
555
|
+
.hqpu-dot[data-state='warning'] { color: var(--dsw-alias-state-warn-primary); }
|
|
556
|
+
.hqpu-dot[data-state='error'] { color: var(--dsw-alias-state-error-primary); }
|
|
557
|
+
.hqpu-dot[data-state='idle'] { color: var(--dsw-alias-state-idle-primary); }
|
|
558
|
+
.hqpu-spinner {
|
|
559
|
+
flex: none;
|
|
560
|
+
width: 14px;
|
|
561
|
+
height: 14px;
|
|
562
|
+
border-radius: 50%;
|
|
563
|
+
border: 1.5px solid color-mix(in srgb, currentColor 25%, transparent);
|
|
564
|
+
border-top-color: currentColor;
|
|
565
|
+
animation: hqpu-spin 1.5s linear infinite;
|
|
263
566
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
color: var(--dsw-alias-label-tertiary, #8f959e);
|
|
567
|
+
@keyframes hqpu-spin {
|
|
568
|
+
to { transform: rotate(360deg); }
|
|
267
569
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
570
|
+
|
|
571
|
+
/* ── Skeleton (PluginManagerPage .skeleton*) ──────────────────────────── */
|
|
572
|
+
.hqpu-skeleton-fill {
|
|
573
|
+
background: var(--dsw-alias-bg-skeleton);
|
|
574
|
+
animation: hqpu-skeleton 2s cubic-bezier(0.36, 0, 0.64, 1) infinite;
|
|
271
575
|
}
|
|
272
|
-
.hqpu-
|
|
576
|
+
.hqpu-skeleton-text {
|
|
273
577
|
display: flex;
|
|
274
578
|
align-items: center;
|
|
275
|
-
|
|
579
|
+
height: 1lh;
|
|
580
|
+
}
|
|
581
|
+
.hqpu-skeleton-bar {
|
|
582
|
+
width: 100%;
|
|
583
|
+
height: 12px;
|
|
584
|
+
border-radius: var(--dsw-radius-xs);
|
|
585
|
+
}
|
|
586
|
+
.hqpu-skeleton-heading { width: 96px; }
|
|
587
|
+
.hqpu-skeleton-title { width: min(144px, 60%); }
|
|
588
|
+
.hqpu-skeleton-desc { width: min(280px, 85%); }
|
|
589
|
+
.hqpu-skeleton-icon { border-color: transparent; }
|
|
590
|
+
@keyframes hqpu-skeleton {
|
|
591
|
+
0% { opacity: 1; }
|
|
592
|
+
40% { opacity: 0.6; }
|
|
593
|
+
80%, 100% { opacity: 1; }
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/* ── Toast (ui-primitives Toast.module.css, held inside the panel) ────── */
|
|
597
|
+
.hqpu-toast {
|
|
598
|
+
display: flex;
|
|
599
|
+
align-items: center;
|
|
600
|
+
gap: 10px;
|
|
601
|
+
padding: 12px 16px;
|
|
602
|
+
border-radius: var(--dsw-radius-lg);
|
|
603
|
+
background: var(--dsw-alias-toast-bg);
|
|
604
|
+
color: var(--dsw-alias-toast-label);
|
|
605
|
+
font-size: 14px;
|
|
606
|
+
line-height: 22px;
|
|
607
|
+
box-shadow: var(--dsw-shadow-lv3);
|
|
608
|
+
animation: hqpu-toast-in 160ms ease-out;
|
|
609
|
+
}
|
|
610
|
+
.hqpu-toast-icon {
|
|
611
|
+
display: grid;
|
|
612
|
+
place-items: center;
|
|
613
|
+
flex: none;
|
|
614
|
+
color: var(--dsw-alias-state-warn-label);
|
|
615
|
+
}
|
|
616
|
+
.hqpu-toast-icon[data-tone='success'] { color: var(--dsw-alias-state-success-primary); }
|
|
617
|
+
.hqpu-toast-text { min-width: 0; }
|
|
618
|
+
@keyframes hqpu-toast-in {
|
|
619
|
+
from { opacity: 0; transform: translateY(-6px); }
|
|
620
|
+
to { opacity: 1; transform: none; }
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/* ── Buttons (PluginManagerPage .addButton / .iconButton) ─────────────── */
|
|
624
|
+
.hqpu-icon-btn {
|
|
625
|
+
display: inline-flex;
|
|
626
|
+
align-items: center;
|
|
627
|
+
justify-content: center;
|
|
628
|
+
flex: none;
|
|
629
|
+
width: 28px;
|
|
630
|
+
height: 28px;
|
|
631
|
+
padding: 0;
|
|
632
|
+
border: 0;
|
|
633
|
+
border-radius: var(--dsw-radius-sm);
|
|
634
|
+
background: transparent;
|
|
635
|
+
color: var(--dsw-alias-label-caption);
|
|
636
|
+
cursor: pointer;
|
|
637
|
+
}
|
|
638
|
+
.hqpu-icon-btn svg { display: block; }
|
|
639
|
+
.hqpu-icon-btn:hover:not(:disabled) {
|
|
640
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
641
|
+
color: var(--dsw-alias-label-secondary);
|
|
642
|
+
}
|
|
643
|
+
.hqpu-icon-btn:disabled {
|
|
644
|
+
opacity: 0.5;
|
|
645
|
+
cursor: default;
|
|
646
|
+
}
|
|
647
|
+
.hqpu-icon-btn:focus-visible {
|
|
648
|
+
outline: var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));
|
|
649
|
+
outline-offset: 1px;
|
|
276
650
|
}
|
|
277
651
|
.hqpu-btn {
|
|
278
652
|
box-sizing: border-box;
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
653
|
+
display: inline-flex;
|
|
654
|
+
align-items: center;
|
|
655
|
+
justify-content: center;
|
|
656
|
+
gap: 6px;
|
|
657
|
+
height: 32px;
|
|
658
|
+
padding: 0 12px;
|
|
659
|
+
border: 0;
|
|
660
|
+
border-radius: var(--dsw-radius-md);
|
|
661
|
+
background: var(--dsw-alias-bg-layer-3);
|
|
662
|
+
color: var(--dsw-alias-label-primary);
|
|
284
663
|
font: inherit;
|
|
285
664
|
font-size: 13px;
|
|
665
|
+
line-height: 20px;
|
|
286
666
|
cursor: pointer;
|
|
287
667
|
}
|
|
288
|
-
.hqpu-btn
|
|
289
|
-
|
|
290
|
-
|
|
668
|
+
.hqpu-btn svg { display: block; }
|
|
669
|
+
.hqpu-btn:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover-solid); }
|
|
670
|
+
.hqpu-btn:disabled { opacity: 0.5; cursor: default; }
|
|
671
|
+
.hqpu-btn:focus-visible {
|
|
672
|
+
outline: var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));
|
|
673
|
+
outline-offset: 1px;
|
|
291
674
|
}
|
|
292
|
-
.hqpu-btn
|
|
293
|
-
|
|
294
|
-
|
|
675
|
+
.hqpu-btn--primary {
|
|
676
|
+
background: var(--dsw-alias-button-primary-fill);
|
|
677
|
+
color: var(--dsw-alias-label-primary-foreground);
|
|
295
678
|
}
|
|
296
|
-
.hqpu-btn-
|
|
679
|
+
.hqpu-btn--primary:hover:not(:disabled) { background: var(--dsw-alias-button-primary-hover); }
|
|
680
|
+
.hqpu-btn--outline {
|
|
297
681
|
background: transparent;
|
|
298
|
-
border
|
|
682
|
+
border: 0.5px solid var(--dsw-alias-border-l4);
|
|
683
|
+
color: var(--dsw-alias-label-secondary);
|
|
299
684
|
}
|
|
300
|
-
.hqpu-btn-
|
|
301
|
-
|
|
302
|
-
|
|
685
|
+
.hqpu-btn--outline:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); }
|
|
686
|
+
.hqpu-btn--ghost { background: transparent; color: var(--dsw-alias-label-secondary); }
|
|
687
|
+
.hqpu-btn--ghost:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); }
|
|
688
|
+
.hqpu-btn--danger { color: var(--dsw-alias-state-error-primary); }
|
|
689
|
+
.hqpu-btn--sm { height: 28px; padding: 0 10px; font-size: 12.5px; }
|
|
690
|
+
.hqpu-actions {
|
|
691
|
+
display: flex;
|
|
692
|
+
align-items: center;
|
|
693
|
+
flex-wrap: wrap;
|
|
694
|
+
gap: 8px;
|
|
303
695
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
696
|
+
|
|
697
|
+
/* ── Footer action row (ui-primitives Modal .footer) ───────────────────── */
|
|
698
|
+
.hqpu-footer {
|
|
699
|
+
display: flex;
|
|
700
|
+
flex: none;
|
|
701
|
+
align-items: center;
|
|
702
|
+
justify-content: flex-end;
|
|
703
|
+
gap: 8px;
|
|
704
|
+
padding: 0 24px 20px;
|
|
307
705
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
706
|
+
|
|
707
|
+
.hqpu-visually-hidden {
|
|
708
|
+
position: absolute;
|
|
709
|
+
width: 1px;
|
|
710
|
+
height: 1px;
|
|
711
|
+
margin: -1px;
|
|
712
|
+
padding: 0;
|
|
713
|
+
overflow: hidden;
|
|
714
|
+
clip-path: inset(50%);
|
|
715
|
+
white-space: nowrap;
|
|
716
|
+
border: 0;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/* ── Reduced motion: drop every decorative animation ──────────────────── */
|
|
720
|
+
@media (prefers-reduced-motion: reduce) {
|
|
721
|
+
.hqpu-scrim,
|
|
722
|
+
.hqpu-panel,
|
|
723
|
+
.hqpu-skeleton-fill,
|
|
724
|
+
.hqpu-spinner,
|
|
725
|
+
.hqpu-toast {
|
|
726
|
+
animation: none;
|
|
727
|
+
}
|
|
311
728
|
}
|
|
312
729
|
`
|
|
313
730
|
|
|
314
731
|
const STYLE_TAG_ID = 'data-plugin-css="huaqiu-plugin-update"'
|
|
315
732
|
|
|
733
|
+
/**
|
|
734
|
+
* Design tokens the bundled DSH shell may not publish.
|
|
735
|
+
*
|
|
736
|
+
* hq-edge ships an older `ui-theme` build. A live probe of the running shell
|
|
737
|
+
* (both themes) shows 13 of the tokens this stylesheet reads are absent:
|
|
738
|
+
* `--dsw-radius-{xs,sm,md,lg,xl,panel}`, `--dsw-focus-ring-width`,
|
|
739
|
+
* `--dsw-focus-ring-color`, `--dsw-alias-toast-label`,
|
|
740
|
+
* `--dsw-alias-state-idle-primary`, `--dsw-font-mono`,
|
|
741
|
+
* `--dsh-frame-overlay-top` and `--dsh-frame-chrome-top`.
|
|
742
|
+
*
|
|
743
|
+
* An unresolved `var()` is not an error — it silently degrades the property to
|
|
744
|
+
* its initial value. That is how this panel once rendered with
|
|
745
|
+
* `border-radius: 0px` and a 3px `auto` focus ring while every colour was
|
|
746
|
+
* already correct.
|
|
747
|
+
*
|
|
748
|
+
* The last three (`--dsw-font-mono`, `--dsh-frame-*`) are deliberately NOT
|
|
749
|
+
* backfilled: the shell's own primitives rely on the inline `var(…, fallback)`
|
|
750
|
+
* form for those (`Modal.module.css`, `PluginManagerPage.module.css`), so this
|
|
751
|
+
* sheet does too. Everything else is backfilled below with the canonical value
|
|
752
|
+
* from `ui-theme/src/styles/{base,focus,design-platform}.css`.
|
|
753
|
+
*
|
|
754
|
+
* Values are feature-detected at runtime and scoped to this plugin's own roots
|
|
755
|
+
* rather than `:root`, so a newer shell always wins and filling a gap can never
|
|
756
|
+
* restyle the host.
|
|
757
|
+
*/
|
|
758
|
+
|
|
759
|
+
/** The two roots this plugin styles: the view overlay (built in
|
|
760
|
+
* update-view.ts) and the sidebar row ({@link ENTRY_CSS}.entry). */
|
|
761
|
+
const STYLE_ROOTS = '.hqpu-overlay, .hqpu-sidebar-entry'
|
|
762
|
+
|
|
763
|
+
/** The shell's dark-theme carrier — audited live as `body[data-ds-dark-theme]`. */
|
|
764
|
+
const DARK_THEME_SELECTOR = 'body[data-ds-dark-theme]'
|
|
765
|
+
|
|
766
|
+
/** Same roots, one rule per theme, for tokens whose official value flips. */
|
|
767
|
+
const DARK_STYLE_ROOTS = STYLE_ROOTS.split(',')
|
|
768
|
+
.map((root) => `${DARK_THEME_SELECTOR} ${root.trim()}`)
|
|
769
|
+
.join(',')
|
|
770
|
+
|
|
771
|
+
type DesignTokenFallback =
|
|
772
|
+
| { token: string; value: string }
|
|
773
|
+
| { token: string; value: { light: string; dark: string } }
|
|
774
|
+
|
|
775
|
+
const DESIGN_TOKEN_FALLBACKS: DesignTokenFallback[] = [
|
|
776
|
+
// base.css — geometry; theme-invariant.
|
|
777
|
+
{ token: '--dsw-radius-xs', value: '4px' },
|
|
778
|
+
{ token: '--dsw-radius-sm', value: '8px' },
|
|
779
|
+
{ token: '--dsw-radius-md', value: '12px' },
|
|
780
|
+
{ token: '--dsw-radius-lg', value: '16px' },
|
|
781
|
+
{ token: '--dsw-radius-xl', value: '20px' },
|
|
782
|
+
{ token: '--dsw-radius-panel', value: '28px' },
|
|
783
|
+
// focus.css — the standard ring geometry. The colour already falls back
|
|
784
|
+
// inline in the sheet; publishing it too keeps the ring intact if that
|
|
785
|
+
// fallback is ever edited away.
|
|
786
|
+
{ token: '--dsw-focus-ring-width', value: '2px' },
|
|
787
|
+
{ token: '--dsw-focus-ring-color', value: 'var(--dsw-alias-state-business-primary, #C7000B)' },
|
|
788
|
+
// design-platform.css — `--dsw-static-neutral-bluish-00` is white in both
|
|
789
|
+
// themes, which is exactly what the official alias resolves to.
|
|
790
|
+
{ token: '--dsw-alias-toast-label', value: 'var(--dsw-static-neutral-bluish-00, #fff)' },
|
|
791
|
+
// The one token whose official value flips with the theme. `--dsw-static-*`
|
|
792
|
+
// are theme-invariant, so one rule per carrier reproduces neutral-300
|
|
793
|
+
// (light) / neutral-600 (dark) without knowing the current theme.
|
|
794
|
+
{
|
|
795
|
+
token: '--dsw-alias-state-idle-primary',
|
|
796
|
+
value: {
|
|
797
|
+
light: 'var(--dsw-static-neutral-300, #d4d4d4)',
|
|
798
|
+
dark: 'var(--dsw-static-neutral-600, #545557)',
|
|
799
|
+
},
|
|
800
|
+
},
|
|
801
|
+
]
|
|
802
|
+
|
|
803
|
+
const TOKEN_STYLE_TAG_ID = 'data-plugin-css="huaqiu-plugin-update-tokens"'
|
|
804
|
+
|
|
805
|
+
/**
|
|
806
|
+
* Publish canonical values for the design tokens the shell is missing.
|
|
807
|
+
*
|
|
808
|
+
* Idempotent and cheap: safe to call on every open, so a shell that publishes
|
|
809
|
+
* its tokens late still wins (the stale fallbacks are removed in that case).
|
|
810
|
+
*/
|
|
811
|
+
export function ensureDesignTokens(): void {
|
|
812
|
+
if (typeof document === 'undefined' || document.body === null) return
|
|
813
|
+
const probe = getComputedStyle(document.body)
|
|
814
|
+
const missing = DESIGN_TOKEN_FALLBACKS.filter(
|
|
815
|
+
(entry) => probe.getPropertyValue(entry.token).trim() === '',
|
|
816
|
+
)
|
|
817
|
+
|
|
818
|
+
const existing = document.querySelector<HTMLStyleElement>(`style[${TOKEN_STYLE_TAG_ID}]`)
|
|
819
|
+
if (missing.length === 0) {
|
|
820
|
+
existing?.remove()
|
|
821
|
+
return
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
const flat = missing.filter(
|
|
825
|
+
(entry): entry is { token: string; value: string } => typeof entry.value === 'string',
|
|
826
|
+
)
|
|
827
|
+
const themed = missing.filter(
|
|
828
|
+
(entry): entry is { token: string; value: { light: string; dark: string } } =>
|
|
829
|
+
typeof entry.value !== 'string',
|
|
830
|
+
)
|
|
831
|
+
|
|
832
|
+
const rules: string[] = []
|
|
833
|
+
const lightDecls = [
|
|
834
|
+
...flat.map((entry) => ` ${entry.token}: ${entry.value};`),
|
|
835
|
+
...themed.map((entry) => ` ${entry.token}: ${entry.value.light};`),
|
|
836
|
+
]
|
|
837
|
+
rules.push(`${STYLE_ROOTS} {\n${lightDecls.join('\n')}\n}`)
|
|
838
|
+
if (themed.length > 0) {
|
|
839
|
+
const darkDecls = themed.map((entry) => ` ${entry.token}: ${entry.value.dark};`)
|
|
840
|
+
rules.push(`${DARK_STYLE_ROOTS} {\n${darkDecls.join('\n')}\n}`)
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
const tag = existing ?? document.createElement('style')
|
|
844
|
+
tag.dataset.pluginCss = 'huaqiu-plugin-update-tokens'
|
|
845
|
+
tag.textContent = rules.join('\n')
|
|
846
|
+
if (tag.parentNode === null) document.head.appendChild(tag)
|
|
847
|
+
}
|
|
848
|
+
|
|
316
849
|
/** Inject styles once (idempotent). */
|
|
317
850
|
export function injectUpdateStyles(): void {
|
|
318
851
|
if (typeof document === 'undefined') return
|
|
@@ -432,6 +965,7 @@ export const inject: string[] = []
|
|
|
432
965
|
export function apply(ctx: ClientContext): () => void {
|
|
433
966
|
if (typeof document === 'undefined') return () => {}
|
|
434
967
|
injectUpdateStyles()
|
|
968
|
+
ensureDesignTokens()
|
|
435
969
|
|
|
436
970
|
const store = new UpdateStore()
|
|
437
971
|
const copy = { zh: COPY.zh, en: COPY.en }
|
|
@@ -457,14 +991,12 @@ export function apply(ctx: ClientContext): () => void {
|
|
|
457
991
|
}
|
|
458
992
|
}
|
|
459
993
|
})
|
|
460
|
-
const labelState = { label: entryCopy(locale).label, tooltip: entryCopy(locale).tooltip }
|
|
461
|
-
const applyLabel = (): void => {
|
|
462
|
-
const c = entryCopy(locale)
|
|
463
|
-
labelState.label = c.label
|
|
464
|
-
labelState.tooltip = c.tooltip
|
|
465
|
-
}
|
|
466
994
|
|
|
467
995
|
const openView = (): void => {
|
|
996
|
+
// Re-detect the shell's design tokens on every open: a shell that
|
|
997
|
+
// publishes them after this plugin mounted must win, and one that never
|
|
998
|
+
// does must keep the canonical fallbacks.
|
|
999
|
+
ensureDesignTokens()
|
|
468
1000
|
// Always re-check on open so the review screen never shows stale data.
|
|
469
1001
|
void store.check().then((report) => {
|
|
470
1002
|
if (report !== null) openUpdateView(store, copyFor())
|
|
@@ -477,8 +1009,12 @@ export function apply(ctx: ClientContext): () => void {
|
|
|
477
1009
|
plugin: PLUGIN_ID,
|
|
478
1010
|
icon: UPDATE_ICON,
|
|
479
1011
|
css: ENTRY_CSS,
|
|
480
|
-
|
|
481
|
-
|
|
1012
|
+
// Read the live locale on every refresh (the locale observer fires
|
|
1013
|
+
// `refresh.subscribe`'s listener, which re-applies these) so the row
|
|
1014
|
+
// re-localizes when the host UI language changes — matching the sibling
|
|
1015
|
+
// family rows, which read a live `currentLang` variable the same way.
|
|
1016
|
+
label: () => entryCopy(locale).label,
|
|
1017
|
+
tooltip: () => entryCopy(locale).tooltip,
|
|
482
1018
|
refresh: { subscribe: refreshSubscribe },
|
|
483
1019
|
badge: {
|
|
484
1020
|
subscribe: (listener) => store.subscribe(listener),
|
|
@@ -502,7 +1038,13 @@ export function apply(ctx: ClientContext): () => void {
|
|
|
502
1038
|
store.stop()
|
|
503
1039
|
}
|
|
504
1040
|
if (typeof ctx.effect === 'function') {
|
|
505
|
-
ctx.effect
|
|
1041
|
+
// `ctx.effect` takes a SETUP function and calls it immediately, keeping
|
|
1042
|
+
// whatever it returns as the teardown. Passing `dispose` itself would run
|
|
1043
|
+
// the teardown synchronously — disconnecting the sidebar-entry observers
|
|
1044
|
+
// and stopping the store before the shell ever renders the sidebar, so the
|
|
1045
|
+
// row could never be placed. Hand it a factory that returns the teardown
|
|
1046
|
+
// instead (the same shape the sibling tool plugins use).
|
|
1047
|
+
ctx.effect(() => dispose, 'plugin-update')
|
|
506
1048
|
}
|
|
507
1049
|
return dispose
|
|
508
1050
|
}
|