@huaqiu/dsh-tool-plugin-update 0.6.13 → 0.6.16

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.
@@ -2,10 +2,17 @@
2
2
  * `@huaqiu/dsh-tool-plugin-update` — browser half.
3
3
  *
4
4
  * Mounts one sidebar toolbutton in the Huaqiu family block ("插件更新") with a
5
- * numeric badge equal to the actionable plugin-update count (spec §6.3/§7.1:
6
- * only compatible `update` + `install` items after host/HQ-Edge filtering and
7
- * after excluding the ignored offer; runtime updates are NOT counted). Click
8
- * opens the review/confirm/ignore modal.
5
+ * numeric badge equal to the number of updates still on offer:
6
+ *
7
+ * - the actionable plugin items (spec §6.3/§7.1 — compatible `update` +
8
+ * `install` after host/HQ-Edge filtering, minus the ignored offer and any
9
+ * muted item), PLUS
10
+ * - one for the HQ Edge runtime update when hq-edge reports one (a separate
11
+ * deliverable with its own card, still a pending update — see `badge.ts`).
12
+ *
13
+ * The durable job history never contributes (a settled job is not an update);
14
+ * a job that is still running is the only non-offer allowed to show a count.
15
+ * Click opens the review/confirm/ignore modal.
9
16
  *
10
17
  * The authoritative state is the hq-edge report fetched through the plugin's
11
18
  * same-origin forwarding routes. A tiny in-memory store owns the latest report
@@ -33,6 +40,7 @@ import {
33
40
  import { COPY } from './copy.js'
34
41
  import { PLUGIN_ICON } from './icons.js'
35
42
  import { openUpdateView, type UpdateStoreLike } from './update-view.js'
43
+ import { badgeCountFor } from './badge.js'
36
44
 
37
45
  /** Stable data attributes identifying the injected entry row. */
38
46
  export const UPDATE_ENTRY_SELECTOR = '[data-hq-plugin-update-entry]'
@@ -185,7 +193,10 @@ const ENTRY_CSS_TEXT = `
185
193
  * publish is backfilled by {@link ensureDesignTokens} — see the note there.
186
194
  */
187
195
  const VIEW_CSS_TEXT = `
188
- /* ── Overlay + panel (ui-primitives Modal.module.css) ─────────────────── */
196
+ /* ── Overlay + panel ─────────────────────────────────────────────────────
197
+ * A 360 px card, the width the prototype fixes: the panel is a decision
198
+ * surface inside the EDA workbench, not a page, and 360 is the width at which
199
+ * a card's title, version row and two actions still fit on one line each. */
189
200
  .hqpu-overlay {
190
201
  position: fixed;
191
202
  inset: 0;
@@ -200,108 +211,280 @@ const VIEW_CSS_TEXT = `
200
211
  position: absolute;
201
212
  /* Only paint excludes the native caption; the root still blocks clicks. */
202
213
  inset: var(--dsh-frame-chrome-top, 0px) 0 0;
203
- background: var(--dsw-alias-bg-mask-1);
214
+ background: var(--dsw-alias-bg-mask-3, var(--dsw-alias-bg-mask-1));
204
215
  animation: hqpu-fade var(--ds-transition-duration, 0.2s) var(--ds-ease-in-out, ease);
205
216
  }
206
217
  .hqpu-panel {
207
218
  position: relative;
208
219
  z-index: 1;
209
- box-sizing: border-box;
210
220
  display: flex;
211
221
  flex-direction: column;
212
- gap: 20px;
213
- width: min(920px, 100%);
214
- max-height: 100%;
222
+ width: min(360px, calc(100vw - 24px));
223
+ max-height: min(86vh, 720px);
215
224
  overflow: hidden;
216
- border: 0;
217
- border-radius: var(--dsw-radius-panel);
218
- background: var(--dsw-alias-bg-layer-2);
219
- box-shadow: var(--dsw-elevation-prominent);
220
- color: var(--dsw-alias-label-primary);
225
+ border: 0.5px solid var(--hq-border);
226
+ border-radius: 12px;
227
+ background: var(--hq-bg);
228
+ color: var(--hq-text);
229
+ box-shadow: var(--dsw-elevation-prominent, 0 16px 48px rgba(0, 0, 0, 0.24));
221
230
  animation: hqpu-panel-in var(--ds-transition-duration, 0.2s) var(--ds-ease-in-out, ease);
222
231
  }
223
232
  .hqpu-panel:focus {
224
233
  outline: none;
225
234
  }
235
+ .hqpu-panel *,
236
+ .hqpu-panel *::before,
237
+ .hqpu-panel *::after {
238
+ box-sizing: border-box;
239
+ }
240
+ /* 'hidden' is the one display switch this sheet uses for drawers, so it has to
241
+ * beat the layout rules below it. */
242
+ .hqpu-panel [hidden] {
243
+ display: none !important;
244
+ }
245
+
246
+ /* The palette, expressed once. Every rule below reads these aliases, so a
247
+ * theme switch (or the HuaQiu brand override) needs no rule to change. */
248
+ .hqpu-panel,
249
+ .hqpu-panel * {
250
+ --hq-bg: var(--dsw-alias-bg-layer-1);
251
+ --hq-card: var(--dsw-alias-bg-layer-2);
252
+ --hq-card-soft: var(--dsw-alias-bg-overlay);
253
+ --hq-border: var(--dsw-alias-border-l2);
254
+ --hq-border-strong: var(--dsw-alias-border-l3);
255
+ --hq-hover: var(--dsw-alias-interactive-bg-hover);
256
+ --hq-text: var(--dsw-alias-label-primary);
257
+ --hq-muted: var(--dsw-alias-label-secondary);
258
+ --hq-faint: var(--dsw-alias-label-tertiary);
259
+ --hq-brand: var(--dsw-alias-brand-primary);
260
+ --hq-brand-soft: var(--dsw-alias-button-primary-hover);
261
+ --hq-brand-dim: var(--dsw-alias-button-primary-dimmed);
262
+ --hq-emerald: var(--dsw-alias-state-success-primary);
263
+ --hq-amber: var(--dsw-alias-state-warn-primary);
264
+ --hq-amber-ink: var(--dsw-alias-state-warn-label, var(--dsw-alias-state-warn-primary));
265
+ /* Ink on an amber fill. Fixed rather than themed: warn-primary is a bright
266
+ * amber in both themes, so the readable ink is the same warm near-black. */
267
+ --hq-on-amber: #2b1a00;
268
+ --hq-rose: var(--dsw-alias-state-error-primary);
269
+ /* Ink on a saturated fill. 'toast-label' is white in both themes and is
270
+ * backfilled by ensureDesignTokens when the shell omits it. */
271
+ --hq-on-accent: var(--dsw-alias-brand-primary-invert, var(--dsw-alias-toast-label, #fff));
272
+ --hq-mono: ui-monospace, SFMono-Regular, Menlo, monospace;
273
+ /* One type scale: 9.5 / 10.5 / 11.5 / 12 / 13. Nothing inline, nothing
274
+ * inherited from the shell's 20 px body default. */
275
+ --hq-fs-micro: 9.5px;
276
+ --hq-fs-meta: 10.5px;
277
+ --hq-fs-body: 11.5px;
278
+ --hq-fs-title: 13px;
279
+ }
280
+
226
281
  @keyframes hqpu-fade {
227
282
  from { opacity: 0; }
228
283
  to { opacity: 1; }
229
284
  }
230
285
  @keyframes hqpu-panel-in {
231
- from { opacity: 0; }
232
- to { opacity: 1; }
286
+ from { opacity: 0; transform: translateY(4px); }
287
+ to { opacity: 1; transform: none; }
233
288
  }
234
289
 
235
- /* ── Head row (ui-primitives Modal .header) ─────────────────────────────── */
290
+ /* ── Header ──────────────────────────────────────────────────────────────
291
+ * Title + the set version / check time, then four icon actions in the order
292
+ * the prototype fixes: refresh · sources · job log · close. */
236
293
  .hqpu-head {
237
- display: flex;
238
294
  flex: none;
239
- flex-wrap: wrap;
240
- align-items: flex-start;
295
+ display: flex;
296
+ align-items: center;
241
297
  justify-content: space-between;
242
- gap: 16px;
243
- box-sizing: border-box;
244
- /* The official Title row: 22/14/12/24 — the toolbar sits 10px further right
245
- * than the body content on purpose, matching the shell's own close button. */
246
- padding: 22px 14px 12px 24px;
298
+ gap: 8px;
299
+ padding: 10px 12px;
300
+ border-bottom: 0.5px solid var(--hq-border);
301
+ background: var(--hq-bg);
247
302
  }
248
303
  .hqpu-head-main {
304
+ display: flex;
305
+ align-items: center;
306
+ gap: 8px;
307
+ min-width: 0;
308
+ }
309
+ .hqpu-brand-icon {
310
+ display: inline-flex;
311
+ align-items: center;
312
+ justify-content: center;
313
+ flex: none;
314
+ width: 28px;
315
+ height: 28px;
316
+ border-radius: 8px;
317
+ background: color-mix(in srgb, var(--hq-brand) 12%, transparent);
318
+ color: var(--hq-brand-soft);
319
+ }
320
+ .hqpu-brand-icon svg {
321
+ display: block;
322
+ }
323
+ .hqpu-title-block {
249
324
  min-width: 0;
250
- max-width: 100%;
251
325
  }
252
326
  .hqpu-title {
253
327
  margin: 0;
254
- font-size: 20px;
255
- font-weight: 500;
256
- line-height: 28px;
257
- color: var(--dsw-alias-label-primary);
328
+ font-size: var(--hq-fs-title);
329
+ font-weight: 600;
330
+ line-height: 17px;
331
+ color: var(--hq-text);
332
+ }
333
+ .hqpu-sub {
334
+ margin: 1px 0 0;
335
+ font-size: var(--hq-fs-meta);
336
+ line-height: 14px;
337
+ color: var(--hq-faint);
338
+ white-space: nowrap;
339
+ overflow: hidden;
340
+ text-overflow: ellipsis;
258
341
  }
259
- .hqpu-intro {
342
+ .hqpu-toolbar {
260
343
  display: flex;
261
344
  align-items: center;
262
- gap: 4px;
263
- margin: 4px 0 0;
264
- font-size: 13px;
265
- line-height: 20px;
266
- color: var(--dsw-alias-label-secondary);
345
+ flex: none;
346
+ gap: 1px;
267
347
  }
268
- .hqpu-info {
348
+ .hqpu-icon-btn {
269
349
  display: inline-flex;
270
350
  align-items: center;
271
351
  justify-content: center;
272
352
  flex: none;
273
- width: 20px;
274
- height: 20px;
353
+ width: 28px;
354
+ height: 28px;
275
355
  padding: 0;
276
356
  border: 0;
277
- border-radius: var(--dsw-radius-sm);
357
+ border-radius: 8px;
278
358
  background: transparent;
279
- color: var(--dsw-alias-label-caption);
359
+ color: var(--hq-muted);
280
360
  cursor: pointer;
361
+ transition: background 0.15s ease, color 0.15s ease;
281
362
  }
282
- .hqpu-info:hover {
283
- background: var(--dsw-alias-interactive-bg-hover);
284
- color: var(--dsw-alias-label-secondary);
363
+ .hqpu-icon-btn svg {
364
+ display: block;
285
365
  }
286
- .hqpu-info:focus-visible {
287
- outline: var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));
288
- outline-offset: 1px;
366
+ .hqpu-icon-btn:hover:not(:disabled) {
367
+ background: var(--hq-hover);
368
+ color: var(--hq-text);
289
369
  }
290
- .hqpu-toolbar {
370
+ .hqpu-icon-btn:disabled {
371
+ opacity: 0.4;
372
+ cursor: default;
373
+ }
374
+ /* A toggled toolbar button (sources, job log) reads as "on", not just hovered —
375
+ * the pressed state is the only hint that a drawer is open behind the body. */
376
+ .hqpu-icon-btn[aria-pressed='true'] {
377
+ background: var(--hq-hover);
378
+ color: var(--hq-text);
379
+ }
380
+ .hqpu-icon-btn--close {
381
+ margin-left: 4px;
382
+ color: var(--hq-faint);
383
+ }
384
+ .hqpu-icon-btn--sm {
385
+ width: 24px;
386
+ height: 24px;
387
+ border-radius: 6px;
388
+ }
389
+
390
+ /* ── Settings strip (gear) ───────────────────────────────────────────────
391
+ * Above the body, never inside it: opening the source editor must not reflow
392
+ * the list the person was reading. */
393
+ .hqpu-settings {
394
+ flex: none;
395
+ display: flex;
396
+ flex-direction: column;
397
+ gap: 8px;
398
+ padding: 10px 12px;
399
+ border-bottom: 0.5px solid var(--hq-border);
400
+ background: var(--hq-card-soft);
401
+ }
402
+ .hqpu-settings-row {
291
403
  display: flex;
404
+ align-items: center;
405
+ justify-content: space-between;
406
+ gap: 8px;
407
+ }
408
+ .hqpu-settings-title {
409
+ font-size: var(--hq-fs-meta);
410
+ font-weight: 600;
411
+ color: var(--hq-muted);
412
+ }
413
+ .hqpu-origin-badge {
292
414
  flex: none;
415
+ padding: 1px 6px;
416
+ border: 0.5px solid;
417
+ border-radius: 4px;
418
+ font-size: var(--hq-fs-micro);
419
+ }
420
+ .hqpu-origin-badge[data-origin='config'] {
421
+ color: var(--hq-emerald);
422
+ border-color: color-mix(in srgb, var(--hq-emerald) 40%, transparent);
423
+ background: color-mix(in srgb, var(--hq-emerald) 10%, transparent);
424
+ }
425
+ .hqpu-origin-badge[data-origin='override'] {
426
+ color: var(--hq-amber-ink);
427
+ border-color: color-mix(in srgb, var(--hq-amber) 40%, transparent);
428
+ background: color-mix(in srgb, var(--hq-amber) 10%, transparent);
429
+ }
430
+ .hqpu-field {
431
+ display: flex;
432
+ flex-direction: column;
433
+ gap: 3px;
434
+ }
435
+ .hqpu-field-head {
436
+ display: flex;
293
437
  align-items: center;
294
- gap: 16px;
438
+ justify-content: space-between;
439
+ gap: 8px;
440
+ }
441
+ .hqpu-field label {
442
+ font-size: var(--hq-fs-micro);
443
+ color: var(--hq-muted);
444
+ }
445
+ .hqpu-source-input {
446
+ width: 100%;
447
+ height: 26px;
448
+ padding: 0 8px;
449
+ border: 0.5px solid var(--hq-border-strong);
450
+ border-radius: 6px;
451
+ background: var(--hq-bg);
452
+ color: var(--hq-muted);
453
+ font-family: var(--hq-mono);
454
+ font-size: var(--hq-fs-micro);
455
+ }
456
+ .hqpu-source-input:disabled {
457
+ opacity: 0.6;
458
+ }
459
+ .hqpu-settings-actions {
460
+ display: flex;
461
+ justify-content: flex-end;
462
+ gap: 6px;
463
+ padding-top: 2px;
295
464
  }
296
465
 
297
- /* ── Scrolling body: the page's centred column ────────────────────────── */
466
+ /* ── Body: scroll + sticky footer + the log drawer over both ─────────────
467
+ * '.hqpu-main' is the positioning box; the drawer covers the body AND the
468
+ * footer, so an open log cannot be clicked through. */
469
+ .hqpu-main {
470
+ position: relative;
471
+ display: flex;
472
+ flex: 1;
473
+ flex-direction: column;
474
+ min-height: 0;
475
+ }
476
+ /* While the job-log drawer is open it covers this whole box; on a short body
477
+ * the box would collapse to nothing, so give it a floor. Set/cleared from
478
+ * update-view.ts (the drawer is absolutely positioned and cannot size it). */
479
+ .hqpu-main[data-log-open='1'] {
480
+ min-height: 340px;
481
+ }
298
482
  .hqpu-scroll {
299
483
  flex: 1;
300
484
  min-height: 0;
301
485
  overflow-y: auto;
302
- box-sizing: border-box;
303
- /* The panel's 20px gap already separates this from the head and footer. */
304
- padding: 0 clamp(16px, 3vw, 32px) 4px;
486
+ padding: 12px;
487
+ background: var(--hq-bg);
305
488
  /* Anything that scrolls inside the panel draws the l2 thumb. */
306
489
  --dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2);
307
490
  --dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2);
@@ -309,806 +492,653 @@ const VIEW_CSS_TEXT = `
309
492
  .hqpu-column {
310
493
  display: flex;
311
494
  flex-direction: column;
312
- gap: 24px;
313
- width: 100%;
314
- max-width: 880px;
315
- margin: 0 auto;
495
+ gap: 10px;
316
496
  }
317
497
  /* A stack of groups (or the single toast host) inside the column. */
318
498
  .hqpu-stack {
319
499
  display: flex;
320
500
  flex-direction: column;
321
- gap: 24px;
501
+ gap: 10px;
322
502
  }
323
- .hqpu-toast-host:empty {
503
+ .hqpu-stack:empty {
324
504
  display: none;
325
505
  }
326
506
 
327
- /* ── Groups (PluginManagerPage .group) ────────────────────────────────── */
328
- .hqpu-group {
507
+ /* ── Decision band ───────────────────────────────────────────────────────
508
+ * The one row that answers "is there anything for me to do", before any list. */
509
+ .hqpu-decision {
329
510
  display: flex;
330
- flex-direction: column;
331
- gap: 8px;
511
+ align-items: center;
512
+ gap: 6px;
513
+ padding: 8px 10px;
514
+ border-radius: 8px;
515
+ background: var(--hq-card-soft);
516
+ font-size: var(--hq-fs-meta);
517
+ }
518
+ .hqpu-decision-main {
519
+ font-weight: 600;
520
+ color: var(--hq-text);
521
+ }
522
+ .hqpu-decision-dot {
523
+ width: 3px;
524
+ height: 3px;
525
+ border-radius: 50%;
526
+ background: var(--hq-faint);
527
+ }
528
+ .hqpu-decision-sub {
529
+ color: var(--hq-muted);
332
530
  }
333
- .hqpu-group-head {
531
+
532
+ /* ── Section head ────────────────────────────────────────────────────────── */
533
+ .hqpu-sec-head {
334
534
  display: flex;
335
535
  align-items: center;
536
+ justify-content: space-between;
336
537
  gap: 8px;
538
+ padding: 0 2px 2px;
337
539
  }
338
- .hqpu-group-title {
339
- flex: 1;
340
- min-width: 0;
341
- margin: 0;
342
- font-size: 14px;
343
- font-weight: 500;
344
- line-height: 22px;
345
- color: var(--dsw-alias-label-primary);
540
+ .hqpu-sec-title {
541
+ display: flex;
542
+ align-items: center;
543
+ gap: 6px;
544
+ font-size: var(--hq-fs-body);
545
+ font-weight: 600;
546
+ color: var(--hq-text);
346
547
  }
347
- .hqpu-group-count {
348
- flex: none;
349
- font-size: 12px;
350
- line-height: 18px;
351
- color: var(--dsw-alias-label-tertiary);
352
- font-variant-numeric: tabular-nums;
548
+ .hqpu-count-pill {
549
+ padding: 0 5px;
550
+ border-radius: 999px;
551
+ background: color-mix(in srgb, var(--hq-brand) 14%, transparent);
552
+ color: var(--hq-brand-soft);
553
+ font-family: var(--hq-mono);
554
+ font-size: var(--hq-fs-micro);
555
+ font-weight: 700;
556
+ }
557
+
558
+ /* ── Text links ──────────────────────────────────────────────────────────── */
559
+ .hqpu-link {
560
+ padding: 0;
561
+ border: 0;
562
+ background: none;
563
+ color: var(--hq-muted);
564
+ font: inherit;
565
+ font-size: var(--hq-fs-meta);
566
+ cursor: pointer;
567
+ transition: color 0.15s ease;
568
+ }
569
+ .hqpu-link:hover {
570
+ color: var(--hq-text);
571
+ }
572
+ .hqpu-link--tiny {
573
+ font-size: var(--hq-fs-micro);
574
+ }
575
+ .hqpu-link--icon {
576
+ display: inline-flex;
577
+ align-items: center;
578
+ gap: 3px;
579
+ }
580
+ .hqpu-link--icon svg {
581
+ display: block;
353
582
  }
354
583
 
355
- /* ── Cards (PluginManagerPage .card) ──────────────────────────────────── */
584
+ /* ── Plugin cards ────────────────────────────────────────────────────────── */
356
585
  .hqpu-cards {
586
+ display: flex;
587
+ flex-direction: column;
588
+ gap: 6px;
357
589
  margin: 0;
358
590
  padding: 0;
359
591
  list-style: none;
360
- display: flex;
361
- flex-direction: column;
362
- gap: 2px;
363
592
  }
364
593
  .hqpu-card {
365
- min-width: 0;
366
- /* The hover surface reaches 8px past the content on both sides. */
367
- margin: 0 -8px;
368
- border-radius: var(--dsw-radius-xl);
594
+ display: flex;
595
+ flex-direction: column;
596
+ gap: 6px;
597
+ padding: 10px;
598
+ border: 0.5px solid var(--hq-border);
599
+ border-radius: 8px;
600
+ background: var(--hq-card);
601
+ transition: border-color 0.15s ease, background 0.15s ease;
369
602
  }
370
603
  .hqpu-card:hover {
371
- background: var(--dsw-alias-interactive-bg-hover);
604
+ border-color: var(--hq-border-strong);
372
605
  }
373
606
  .hqpu-card-head {
607
+ display: flex;
608
+ align-items: flex-start;
609
+ justify-content: space-between;
610
+ gap: 8px;
611
+ }
612
+ .hqpu-card-id {
374
613
  display: flex;
375
614
  align-items: center;
376
- gap: 14px;
377
- padding: 8px;
615
+ gap: 8px;
616
+ min-width: 0;
378
617
  }
379
- .hqpu-card-icon {
618
+ .hqpu-avatar {
380
619
  display: inline-flex;
381
- flex: none;
382
620
  align-items: center;
383
621
  justify-content: center;
384
- width: 48px;
385
- height: 48px;
386
- border: 0.5px solid var(--dsw-alias-border-l3);
387
- border-radius: var(--dsw-radius-lg);
388
- color: var(--dsw-alias-label-secondary);
389
- }
390
- .hqpu-card-icon svg {
391
- display: block;
392
- width: 24px;
393
- height: 24px;
622
+ flex: none;
623
+ width: 30px;
624
+ height: 30px;
625
+ border-radius: 7px;
626
+ background: var(--hq-card-soft);
627
+ color: var(--hq-muted);
628
+ font-size: var(--hq-fs-micro);
629
+ font-weight: 700;
394
630
  }
395
631
  .hqpu-card-main {
396
- flex: 1;
397
- min-width: 0;
398
632
  display: flex;
399
633
  flex-direction: column;
400
- gap: 4px;
401
- }
402
- .hqpu-title-row {
403
- display: flex;
404
- align-items: center;
405
- flex-wrap: wrap;
406
- gap: 8px;
407
634
  min-width: 0;
635
+ gap: 1px;
408
636
  }
409
637
  .hqpu-card-title {
410
- min-width: 0;
411
- font-size: 14px;
412
- font-weight: 500;
413
- line-height: 20px;
414
- color: var(--dsw-alias-label-primary);
638
+ font-size: var(--hq-fs-body);
639
+ font-weight: 600;
640
+ line-height: 15px;
641
+ color: var(--hq-text);
415
642
  overflow: hidden;
416
643
  text-overflow: ellipsis;
417
644
  white-space: nowrap;
418
645
  }
419
- .hqpu-card-desc {
420
- font-size: 13px;
421
- line-height: 18px;
422
- color: var(--dsw-alias-label-tertiary);
423
- display: -webkit-box;
424
- -webkit-box-orient: vertical;
425
- -webkit-line-clamp: 1;
646
+ .hqpu-card-sub {
647
+ font-family: var(--hq-mono);
648
+ font-size: var(--hq-fs-micro);
649
+ line-height: 12px;
650
+ color: var(--hq-faint);
426
651
  overflow: hidden;
427
- overflow-wrap: anywhere;
652
+ text-overflow: ellipsis;
653
+ white-space: nowrap;
428
654
  }
429
655
  .hqpu-card-end {
430
- position: relative;
431
- z-index: 1;
432
- display: inline-flex;
433
- flex: none;
434
- align-items: center;
435
- gap: 8px;
436
- }
437
- .hqpu-card-reason {
438
- margin: 0;
439
- padding: 0 8px 8px 70px;
440
- font-size: 12px;
441
- line-height: 18px;
442
- color: var(--dsw-alias-state-error-primary);
443
- overflow-wrap: anywhere;
444
- }
445
-
446
- /* ── Tags (ui-primitives Tag.module.css) ──────────────────────────────── */
447
- .hqpu-tag {
448
- display: inline-flex;
656
+ display: flex;
449
657
  align-items: center;
450
658
  flex: none;
451
- border-radius: 999px;
452
- corner-shape: round;
453
- padding: 1px 8px;
454
- font-size: 11px;
659
+ gap: 2px;
660
+ }
661
+ /* The card's own action: tinted, not filled — the filled brand fill belongs to
662
+ * the one set-level CTA in the footer. */
663
+ .hqpu-card-action {
664
+ height: 24px;
665
+ padding: 0 8px;
666
+ border: 0;
667
+ border-radius: 6px;
668
+ background: color-mix(in srgb, var(--hq-brand) 12%, transparent);
669
+ color: var(--hq-brand-soft);
670
+ font: inherit;
671
+ font-size: var(--hq-fs-meta);
455
672
  font-weight: 500;
456
- line-height: 17px;
457
673
  white-space: nowrap;
674
+ cursor: pointer;
675
+ transition: background 0.15s ease, color 0.15s ease;
458
676
  }
459
- .hqpu-tag[data-tone='success'] {
460
- background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 10%, transparent);
461
- color: var(--dsw-alias-state-success-primary);
462
- }
463
- .hqpu-tag[data-tone='info'] {
464
- background: color-mix(in srgb, var(--dsw-alias-state-business-primary) 10%, transparent);
465
- color: var(--dsw-alias-state-business-primary);
466
- }
467
- .hqpu-tag[data-tone='warning'] {
468
- background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 12%, transparent);
469
- color: var(--dsw-alias-state-warn-primary);
470
- }
471
- .hqpu-tag[data-tone='danger'] {
472
- background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);
473
- color: var(--dsw-alias-state-error-primary);
474
- }
475
- .hqpu-tag[data-tone='neutral'] {
476
- background: var(--dsw-alias-bg-module-platform);
477
- color: var(--dsw-alias-label-secondary);
677
+ .hqpu-card-action:hover:not(:disabled) {
678
+ background: var(--hq-brand);
679
+ color: var(--hq-on-accent);
478
680
  }
479
-
480
- /* ── Fact rows (PluginManagerPage .facts / .rows) ─────────────────────── */
481
- .hqpu-facts {
482
- margin: 0;
483
- display: flex;
484
- flex-direction: column;
681
+ .hqpu-card-action:disabled {
682
+ opacity: 0.5;
683
+ cursor: default;
485
684
  }
486
- .hqpu-facts > div {
685
+ .hqpu-card-foot {
487
686
  display: flex;
488
687
  align-items: center;
489
688
  justify-content: space-between;
490
- gap: 16px;
491
- min-height: 44px;
492
- padding: 8px 2px;
493
- border-bottom: 0.5px solid var(--dsw-alias-border-l2);
494
- }
495
- .hqpu-facts > div:last-child {
496
- border-bottom: 0;
497
- }
498
- .hqpu-facts dt {
499
- flex: none;
500
- font-size: 13px;
501
- line-height: 20px;
502
- color: var(--dsw-alias-label-secondary);
689
+ gap: 8px;
503
690
  }
504
- .hqpu-facts dd {
691
+ .hqpu-version-row {
692
+ display: flex;
693
+ align-items: baseline;
694
+ gap: 4px;
505
695
  min-width: 0;
506
- margin: 0;
507
- font-size: 13px;
508
- line-height: 20px;
509
- text-align: end;
510
- overflow-wrap: anywhere;
511
- color: var(--dsw-alias-label-primary);
696
+ font-family: var(--hq-mono);
697
+ font-size: var(--hq-fs-meta);
698
+ color: var(--hq-muted);
512
699
  }
513
- .hqpu-facts code {
514
- padding: 2px 6px;
515
- border-radius: var(--dsw-radius-xs);
516
- background: var(--dsw-alias-bg-module-platform);
517
- font-family: var(--dsw-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
518
- font-size: 12px;
700
+ .hqpu-ver-old {
701
+ color: var(--hq-muted);
519
702
  }
520
-
521
- /* ── Banner (PluginManagerPage .installSafety) ────────────────────────── */
522
- .hqpu-banner {
523
- display: flex;
524
- align-items: flex-start;
525
- gap: 8px;
526
- padding: 10px 12px;
527
- border-radius: var(--dsw-radius-md);
528
- background: var(--dsw-alias-state-warn-tertiary);
529
- font-size: 12.5px;
530
- line-height: 18px;
531
- color: var(--dsw-alias-label-secondary);
703
+ .hqpu-ver-arrow {
704
+ color: var(--hq-border-strong);
532
705
  }
533
- .hqpu-banner > svg {
534
- flex: none;
535
- margin-top: 2px;
536
- color: var(--dsw-alias-state-warn-primary);
706
+ .hqpu-ver-new {
707
+ color: var(--hq-emerald);
708
+ font-weight: 600;
537
709
  }
538
-
539
- /* ── Status, empty, failure (PluginManagerPage .status / .empty / .failure) */
540
- .hqpu-status {
541
- margin: 0;
542
- font-size: 13px;
543
- line-height: 20px;
544
- color: var(--dsw-alias-label-tertiary);
710
+ .hqpu-ver-new--lg {
711
+ font-size: var(--hq-fs-body);
712
+ }
713
+ .hqpu-ver-size {
714
+ font-size: var(--hq-fs-micro);
715
+ color: var(--hq-faint);
545
716
  }
546
- .hqpu-status--dot {
717
+ .hqpu-toggle {
547
718
  display: inline-flex;
548
719
  align-items: center;
549
- gap: 6px;
720
+ gap: 2px;
721
+ padding: 0;
722
+ border: 0;
723
+ background: none;
724
+ color: var(--hq-faint);
725
+ font: inherit;
726
+ font-size: var(--hq-fs-micro);
727
+ white-space: nowrap;
728
+ cursor: pointer;
550
729
  }
551
- .hqpu-failure {
552
- display: flex;
553
- align-items: center;
554
- gap: 10px;
555
- color: var(--dsw-alias-state-error-primary);
730
+ .hqpu-toggle:hover {
731
+ color: var(--hq-text);
556
732
  }
557
- .hqpu-empty {
558
- padding: 32px 0;
559
- text-align: center;
560
- font-size: 13px;
561
- line-height: 20px;
562
- color: var(--dsw-alias-label-tertiary);
733
+ .hqpu-toggle-chevron {
734
+ display: inline-flex;
735
+ transition: transform 0.15s ease;
563
736
  }
564
-
565
- /* ── State dot + spinner (ui-primitives StateDot.module.css) ──────────── */
566
- .hqpu-dot {
567
- position: relative;
568
- display: inline-block;
569
- flex: none;
570
- width: 10px;
571
- height: 10px;
737
+ .hqpu-toggle[data-open='true'] .hqpu-toggle-chevron {
738
+ transform: rotate(90deg);
572
739
  }
573
- .hqpu-dot::after {
574
- content: '';
575
- position: absolute;
576
- inset: 20%;
577
- border-radius: 50%;
578
- corner-shape: round;
579
- background: currentColor;
580
- }
581
- .hqpu-dot[data-state='done'] { color: var(--dsw-alias-state-success-primary); }
582
- .hqpu-dot[data-state='warning'] { color: var(--dsw-alias-state-warn-primary); }
583
- .hqpu-dot[data-state='error'] { color: var(--dsw-alias-state-error-primary); }
584
- .hqpu-dot[data-state='idle'] { color: var(--dsw-alias-state-idle-primary); }
585
- .hqpu-spinner {
586
- flex: none;
587
- width: 14px;
588
- height: 14px;
589
- border-radius: 50%;
590
- border: 1.5px solid color-mix(in srgb, currentColor 25%, transparent);
591
- border-top-color: currentColor;
592
- animation: hqpu-spin 1.5s linear infinite;
740
+ .hqpu-changes {
741
+ padding-top: 8px;
742
+ border-top: 0.5px solid var(--hq-border);
743
+ font-size: var(--hq-fs-meta);
744
+ color: var(--hq-muted);
593
745
  }
594
- @keyframes hqpu-spin {
595
- to { transform: rotate(360deg); }
746
+ .hqpu-changes-title {
747
+ margin: 0 0 4px;
748
+ font-size: var(--hq-fs-micro);
749
+ font-weight: 600;
750
+ color: var(--hq-muted);
751
+ }
752
+ .hqpu-changes ul {
753
+ margin: 0;
754
+ padding-left: 16px;
755
+ font-size: var(--hq-fs-micro);
756
+ line-height: 16px;
596
757
  }
597
758
 
598
- /* ── Skeleton (PluginManagerPage .skeleton*) ──────────────────────────── */
599
- .hqpu-skeleton-fill {
600
- background: var(--dsw-alias-bg-skeleton);
601
- animation: hqpu-skeleton 2s cubic-bezier(0.36, 0, 0.64, 1) infinite;
759
+ /* ── Collapsed groups (已忽略 / 已是最新 / 版本说明) ─────────────────────── */
760
+ .hqpu-disc {
761
+ padding-top: 6px;
762
+ border-top: 0.5px solid var(--hq-border);
602
763
  }
603
- .hqpu-skeleton-text {
764
+ .hqpu-disc-head {
604
765
  display: flex;
605
766
  align-items: center;
606
- height: 1lh;
607
- }
608
- .hqpu-skeleton-bar {
767
+ justify-content: space-between;
768
+ gap: 8px;
609
769
  width: 100%;
610
- height: 12px;
611
- border-radius: var(--dsw-radius-xs);
612
- }
613
- .hqpu-skeleton-heading { width: 96px; }
614
- .hqpu-skeleton-title { width: min(144px, 60%); }
615
- .hqpu-skeleton-desc { width: min(280px, 85%); }
616
- .hqpu-skeleton-icon { border-color: transparent; }
617
- @keyframes hqpu-skeleton {
618
- 0% { opacity: 1; }
619
- 40% { opacity: 0.6; }
620
- 80%, 100% { opacity: 1; }
770
+ padding: 2px 0;
771
+ border: 0;
772
+ background: none;
773
+ color: var(--hq-muted);
774
+ font: inherit;
775
+ cursor: pointer;
621
776
  }
622
-
623
- /* ── Toast (ui-primitives Toast.module.css, held inside the panel) ────── */
624
- .hqpu-toast {
777
+ .hqpu-disc-left {
625
778
  display: flex;
626
779
  align-items: center;
627
- gap: 10px;
628
- padding: 12px 16px;
629
- border-radius: var(--dsw-radius-lg);
630
- background: var(--dsw-alias-toast-bg);
631
- color: var(--dsw-alias-toast-label);
632
- font-size: 14px;
633
- line-height: 22px;
634
- box-shadow: var(--dsw-shadow-lv3);
635
- animation: hqpu-toast-in 160ms ease-out;
636
- }
637
- .hqpu-toast-icon {
638
- display: grid;
639
- place-items: center;
640
- flex: none;
641
- color: var(--dsw-alias-state-warn-label);
642
- }
643
- .hqpu-toast-icon[data-tone='success'] { color: var(--dsw-alias-state-success-primary); }
644
- .hqpu-toast-text { min-width: 0; }
645
- @keyframes hqpu-toast-in {
646
- from { opacity: 0; transform: translateY(-6px); }
647
- to { opacity: 1; transform: none; }
780
+ gap: 6px;
781
+ min-width: 0;
648
782
  }
649
-
650
- /* ── Buttons (PluginManagerPage .addButton / .iconButton) ─────────────── */
651
- .hqpu-icon-btn {
783
+ .hqpu-disc-chevron {
652
784
  display: inline-flex;
653
- align-items: center;
654
- justify-content: center;
655
785
  flex: none;
656
- width: 28px;
657
- height: 28px;
658
- padding: 0;
659
- border: 0;
660
- border-radius: var(--dsw-radius-sm);
661
- background: transparent;
662
- color: var(--dsw-alias-label-caption);
663
- cursor: pointer;
664
- }
665
- .hqpu-icon-btn svg { display: block; }
666
- .hqpu-icon-btn:hover:not(:disabled) {
667
- background: var(--dsw-alias-interactive-bg-hover);
668
- color: var(--dsw-alias-label-secondary);
669
- }
670
- .hqpu-icon-btn:disabled {
671
- opacity: 0.5;
672
- cursor: default;
786
+ color: var(--hq-faint);
787
+ transition: transform 0.15s ease;
673
788
  }
674
- .hqpu-icon-btn:focus-visible {
675
- outline: var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));
676
- outline-offset: 1px;
789
+ .hqpu-disc-head[aria-expanded='true'] .hqpu-disc-chevron {
790
+ transform: rotate(90deg);
677
791
  }
678
- .hqpu-btn {
679
- box-sizing: border-box;
680
- display: inline-flex;
681
- align-items: center;
682
- justify-content: center;
683
- gap: 6px;
684
- height: 32px;
685
- padding: 0 12px;
686
- border: 0;
687
- border-radius: var(--dsw-radius-md);
688
- background: var(--dsw-alias-bg-layer-3);
689
- color: var(--dsw-alias-label-primary);
690
- font: inherit;
691
- font-size: 13px;
692
- line-height: 20px;
693
- cursor: pointer;
694
- }
695
- .hqpu-btn svg { display: block; }
696
- .hqpu-btn:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover-solid); }
697
- .hqpu-btn:disabled { opacity: 0.5; cursor: default; }
698
- .hqpu-btn:focus-visible {
699
- outline: var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));
700
- outline-offset: 1px;
792
+ .hqpu-disc-title {
793
+ font-size: var(--hq-fs-body);
794
+ font-weight: 600;
795
+ color: var(--hq-text);
701
796
  }
702
- .hqpu-btn--primary {
703
- background: var(--dsw-alias-button-primary-fill);
704
- color: var(--dsw-alias-label-primary-foreground);
797
+ .hqpu-disc-count {
798
+ font-family: var(--hq-mono);
799
+ font-size: var(--hq-fs-meta);
800
+ color: var(--hq-faint);
705
801
  }
706
- .hqpu-btn--primary:hover:not(:disabled) { background: var(--dsw-alias-button-primary-hover); }
707
- .hqpu-btn--outline {
708
- background: transparent;
709
- border: 0.5px solid var(--dsw-alias-border-l4);
710
- color: var(--dsw-alias-label-secondary);
802
+ .hqpu-disc-note {
803
+ font-size: var(--hq-fs-micro);
804
+ color: var(--hq-faint);
805
+ white-space: nowrap;
806
+ overflow: hidden;
807
+ text-overflow: ellipsis;
711
808
  }
712
- .hqpu-btn--outline:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); }
713
- .hqpu-btn--ghost { background: transparent; color: var(--dsw-alias-label-secondary); }
714
- .hqpu-btn--ghost:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); }
715
- .hqpu-btn--danger { color: var(--dsw-alias-state-error-primary); }
716
- .hqpu-btn--sm { height: 28px; padding: 0 10px; font-size: 12.5px; }
717
- .hqpu-actions {
809
+ .hqpu-disc-body {
718
810
  display: flex;
719
- align-items: center;
720
- flex-wrap: wrap;
721
- gap: 8px;
722
- }
723
-
724
- /* ── Runtime update action row (arm → confirm step) ───────────────────── */
725
- .hqpu-runtime-actions {
726
- margin-top: 12px;
811
+ flex-direction: column;
812
+ gap: 6px;
813
+ padding-top: 6px;
727
814
  }
728
- .hqpu-runtime-confirm {
729
- flex: 1 1 100%;
815
+ .hqpu-rows {
816
+ display: flex;
817
+ flex-direction: column;
818
+ gap: 4px;
730
819
  margin: 0;
820
+ padding: 0;
821
+ list-style: none;
731
822
  }
732
-
733
- /* ── Runtime update source (fact row + editable URL field) ────────────── */
734
- .hqpu-source-line {
735
- display: inline-flex;
823
+ .hqpu-row {
824
+ display: flex;
736
825
  align-items: center;
826
+ justify-content: space-between;
737
827
  gap: 8px;
738
- min-width: 0;
739
- max-width: 100%;
828
+ padding: 6px 8px;
829
+ border-radius: 6px;
830
+ background: var(--hq-card-soft);
831
+ font-size: var(--hq-fs-meta);
740
832
  }
741
- .hqpu-source-url {
742
- min-width: 0;
833
+ .hqpu-row-name {
834
+ font-weight: 500;
835
+ color: var(--hq-text);
743
836
  overflow: hidden;
744
837
  text-overflow: ellipsis;
745
838
  white-space: nowrap;
746
839
  }
747
- .hqpu-source-editor {
840
+ .hqpu-row-end {
748
841
  display: flex;
749
842
  align-items: center;
750
- flex-wrap: wrap;
843
+ flex: none;
751
844
  gap: 8px;
752
- margin-top: 12px;
753
- }
754
- /* The plugin-source editor sits inside its own group right under the fact
755
- * row, so it needs less air than the runtime one under the facts block. */
756
- .hqpu-source-editor--plugin {
757
- margin-top: 4px;
758
845
  }
759
- .hqpu-source-hint {
760
- flex: 1 1 100%;
761
- margin: 0;
762
- }
763
- .hqpu-source-controls {
764
- flex: 1 1 100%;
765
- }
766
- .hqpu-source-input {
767
- flex: 1 1 240px;
768
- min-width: 0;
769
- height: 28px;
770
- box-sizing: border-box;
771
- padding: 0 10px;
772
- border: 0.5px solid var(--dsw-alias-border-l4);
773
- border-radius: var(--dsw-radius-md);
774
- background: var(--dsw-alias-bg-layer-3);
775
- color: var(--dsw-alias-label-primary);
776
- font: inherit;
777
- font-size: 12.5px;
778
- }
779
- .hqpu-source-input:disabled {
780
- opacity: 0.5;
781
- }
782
- .hqpu-source-input:focus-visible {
783
- outline: var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));
784
- outline-offset: 1px;
846
+ .hqpu-row-meta {
847
+ font-family: var(--hq-mono);
848
+ font-size: var(--hq-fs-micro);
849
+ color: var(--hq-faint);
785
850
  }
786
851
 
787
- /* ── Footer action row (ui-primitives Modal .footer) ───────────────────── */
788
- .hqpu-footer {
852
+ /* ── Runtime card (amber: a swap is not a plugin install) ───────────────── */
853
+ .hqpu-engine {
854
+ display: flex;
855
+ flex-direction: column;
856
+ gap: 10px;
857
+ padding: 10px;
858
+ border: 0.5px solid color-mix(in srgb, var(--hq-amber) 32%, transparent);
859
+ border-radius: 10px;
860
+ background: linear-gradient(
861
+ 135deg,
862
+ color-mix(in srgb, var(--hq-amber) 10%, var(--hq-card)) 0%,
863
+ var(--hq-card) 60%,
864
+ var(--hq-card-soft) 100%
865
+ );
866
+ }
867
+ .hqpu-engine-head {
789
868
  display: flex;
790
- flex: none;
791
869
  align-items: center;
792
- justify-content: flex-end;
870
+ justify-content: space-between;
793
871
  gap: 8px;
794
- padding: 0 24px 20px;
795
872
  }
796
-
797
- /* ── Durable job (active / result) ────────────────────────────────────── */
798
- .hqpu-job {
873
+ .hqpu-engine-title {
799
874
  display: flex;
800
- flex-direction: column;
875
+ align-items: center;
801
876
  gap: 6px;
802
- padding: 12px;
803
- border: 0.5px solid var(--dsw-alias-border-l2);
804
- border-radius: var(--dsw-radius-lg);
805
- background: var(--dsw-alias-bg-layer-3);
806
- }
807
- .hqpu-job-meta {
808
- font-variant-numeric: tabular-nums;
877
+ font-size: var(--hq-fs-body);
878
+ font-weight: 600;
879
+ color: var(--hq-text);
809
880
  }
810
- .hqpu-progress {
811
- height: 6px;
812
- border-radius: 999px;
813
- background: var(--dsw-alias-bg-module-platform);
814
- overflow: hidden;
881
+ .hqpu-engine-bolt {
882
+ display: inline-flex;
883
+ color: var(--hq-amber);
815
884
  }
816
- .hqpu-progress-fill {
817
- height: 100%;
818
- border-radius: 999px;
819
- background: var(--dsw-alias-state-business-primary);
820
- transition: width var(--ds-transition-duration, 0.2s) ease;
885
+ .hqpu-chip {
886
+ flex: none;
887
+ padding: 1px 6px;
888
+ border: 0.5px solid var(--hq-border-strong);
889
+ border-radius: 4px;
890
+ background: var(--hq-card);
891
+ color: var(--hq-muted);
892
+ font-family: var(--hq-mono);
893
+ font-size: var(--hq-fs-micro);
821
894
  }
822
- .hqpu-job-steps {
895
+ .hqpu-engine-row {
823
896
  display: flex;
824
- flex-direction: column;
825
- gap: 4px;
897
+ align-items: flex-start;
898
+ justify-content: space-between;
899
+ gap: 10px;
826
900
  }
827
- .hqpu-steps {
828
- margin: 0;
829
- padding: 0;
830
- list-style: none;
831
- display: flex;
832
- flex-direction: column;
833
- gap: 4px;
901
+ .hqpu-engine-info {
902
+ min-width: 0;
834
903
  }
835
- .hqpu-step {
904
+ .hqpu-engine-new {
836
905
  display: flex;
837
906
  align-items: center;
838
907
  gap: 6px;
839
- font-size: 12.5px;
840
- line-height: 18px;
841
- color: var(--dsw-alias-label-secondary);
842
- }
843
- .hqpu-step--skipped,
844
- .hqpu-step--pending { opacity: 0.6; }
845
-
846
- /* ── Per-plugin change list + release notes ────────────────────────────── */
847
- .hqpu-changes {
848
- padding: 0 8px 8px 70px;
849
908
  }
850
- .hqpu-changes-list,
851
- .hqpu-release-notes {
852
- margin: 4px 0 0;
853
- padding-left: 18px;
854
- font-size: 12.5px;
855
- line-height: 19px;
856
- color: var(--dsw-alias-label-secondary);
857
- }
858
- .hqpu-release-link {
859
- align-self: flex-start;
860
- }
861
-
862
- /* ── Advanced disclosure (source editors) ───────────────────────────────── */
863
- .hqpu-advanced > summary {
864
- cursor: pointer;
865
- font-size: 12.5px;
866
- line-height: 20px;
867
- color: var(--dsw-alias-label-tertiary);
868
- list-style: none;
869
- padding: 2px 0;
870
- }
871
- .hqpu-advanced > summary::-webkit-details-marker { display: none; }
872
- .hqpu-advanced > summary::before {
873
- content: '▸ ';
909
+ .hqpu-engine-size {
910
+ margin: 2px 0 0;
911
+ font-size: var(--hq-fs-micro);
912
+ line-height: 14px;
913
+ color: var(--hq-muted);
874
914
  }
875
- .hqpu-advanced[open] > summary::before {
876
- content: '▾ ';
915
+ .hqpu-engine-state {
916
+ flex: none;
917
+ max-width: 45%;
918
+ font-size: var(--hq-fs-micro);
919
+ line-height: 14px;
920
+ color: var(--hq-muted);
921
+ text-align: right;
877
922
  }
878
-
879
- /* Danger-toned banner (check failure, breaking changes) */
880
- .hqpu-banner[data-tone='danger'] {
881
- background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 8%, transparent);
882
- color: var(--dsw-alias-label-secondary);
923
+ .hqpu-engine-error {
924
+ margin: 0;
925
+ font-size: var(--hq-fs-micro);
926
+ color: var(--hq-rose);
883
927
  }
884
- .hqpu-banner[data-tone='danger'] > svg {
885
- color: var(--dsw-alias-state-error-primary);
928
+ .hqpu-tag--brand {
929
+ flex: none;
930
+ padding: 0 4px;
931
+ border-radius: 3px;
932
+ background: color-mix(in srgb, var(--hq-amber) 14%, transparent);
933
+ color: var(--hq-amber-ink);
934
+ font-size: var(--hq-fs-micro);
935
+ font-weight: 500;
886
936
  }
887
-
888
- .hqpu-visually-hidden {
889
- position: absolute;
890
- width: 1px;
891
- height: 1px;
892
- margin: -1px;
893
- padding: 0;
894
- overflow: hidden;
895
- clip-path: inset(50%);
896
- white-space: nowrap;
937
+ .hqpu-btn--engine {
938
+ flex: none;
939
+ height: 26px;
940
+ padding: 0 10px;
897
941
  border: 0;
942
+ border-radius: 6px;
943
+ background: var(--hq-amber);
944
+ color: var(--hq-on-amber);
945
+ font: inherit;
946
+ font-size: var(--hq-fs-meta);
947
+ font-weight: 500;
948
+ white-space: nowrap;
949
+ cursor: pointer;
950
+ transition: background 0.15s ease;
898
951
  }
899
-
900
- /* ── Reduced motion: drop every decorative animation ──────────────────── */
901
- @media (prefers-reduced-motion: reduce) {
902
- .hqpu-scrim,
903
- .hqpu-panel,
904
- .hqpu-skeleton-fill,
905
- .hqpu-spinner,
906
- .hqpu-toast,
907
- .hqpu-pulse-ring,
908
- .hqpu-job-step--active {
909
- animation: none;
910
- }
952
+ .hqpu-btn--engine:hover:not(:disabled) {
953
+ background: color-mix(in srgb, var(--hq-amber) 85%, #000);
911
954
  }
912
-
913
- /* ════════════════════════════════════════════════════════════════════════
914
- * Refactored dock (interactive_refactored_ui_mockup.html) — a fluid
915
- * update-center column. These rules cascade over the shell-token base: the
916
- * panel takes the available width (up to 720px), every colour is a
917
- * --dsw-alias-* token so light/dark follow the shell's
918
- * body[data-ds-dark-theme] and the brand follows the HuaQiu theme overrides,
919
- * with a sticky header / scroll body / sticky footer. No Tailwind, no
920
- * dependencies.
921
- * ════════════════════════════════════════════════════════════════════════ */
922
- .hqpu-panel,
923
- .hqpu-panel * {
924
- /* Every colour rides a shell theme token, so light/dark follow
925
- * body[data-ds-dark-theme] automatically. The brand tokens are exactly the
926
- * ones @huaqiu/dsh-ui-theme-huaqiu overrides (light #C7000B / dark #FF6B74),
927
- * so the dock honours the HuaQiu brand scheme in both themes without knowing
928
- * which theme is active. */
929
- --hq-bg: var(--dsw-alias-bg-layer-1);
930
- --hq-card: var(--dsw-alias-bg-layer-2);
931
- --hq-card-soft: var(--dsw-alias-bg-overlay);
932
- --hq-border: var(--dsw-alias-border-l2);
933
- --hq-border-strong: var(--dsw-alias-border-l3);
934
- --hq-hover: var(--dsw-alias-interactive-bg-hover);
935
- --hq-text: var(--dsw-alias-label-primary);
936
- --hq-text-strong: var(--dsw-alias-label-primary);
937
- --hq-text-code: var(--dsw-alias-label-secondary);
938
- --hq-muted: var(--dsw-alias-label-secondary);
939
- --hq-faint: var(--dsw-alias-label-tertiary);
940
- --hq-brand: var(--dsw-alias-brand-primary);
941
- --hq-brand-soft: var(--dsw-alias-button-primary-hover);
942
- --hq-brand-dim: var(--dsw-alias-button-primary-dimmed);
943
- --hq-brand-fg: var(--dsw-alias-brand-primary-invert);
944
- --hq-emerald: var(--dsw-alias-state-success-primary);
945
- --hq-amber: var(--dsw-alias-state-warn-primary);
946
- --hq-rose: var(--dsw-alias-state-error-primary);
955
+ .hqpu-btn--engine:disabled {
956
+ opacity: 0.5;
957
+ cursor: default;
947
958
  }
948
- .hqpu-panel {
949
- width: min(360px, calc(100vw - 32px));
950
- max-height: min(86vh, 720px);
951
- background: var(--hq-bg);
952
- color: var(--hq-text);
953
- border: 0.5px solid var(--hq-border);
954
- border-radius: 12px;
955
- gap: 0;
956
- overflow: hidden;
959
+ .hqpu-engine-confirm {
960
+ display: flex;
961
+ flex-direction: column;
962
+ gap: 8px;
963
+ padding: 10px;
964
+ border: 0.5px solid color-mix(in srgb, var(--hq-amber) 24%, transparent);
965
+ border-radius: 8px;
966
+ background: color-mix(in srgb, var(--hq-amber) 10%, transparent);
957
967
  }
958
- .hqpu-scrim {
959
- background: var(--dsw-alias-bg-mask-3);
968
+ .hqpu-engine-confirm-text {
969
+ margin: 0;
970
+ font-size: var(--hq-fs-meta);
971
+ line-height: 16px;
972
+ color: var(--hq-amber-ink);
960
973
  }
961
-
962
- /* Sticky header */
963
- .hqpu-head {
964
- flex: none;
974
+ .hqpu-engine-confirm-actions {
965
975
  display: flex;
966
976
  align-items: center;
967
- justify-content: space-between;
968
- gap: 8px;
969
- padding: 10px 12px;
970
- border-bottom: 0.5px solid var(--hq-border);
971
- background: color-mix(in srgb, var(--hq-bg) 95%, transparent);
972
- position: sticky;
973
- top: 0;
974
- z-index: 2;
977
+ justify-content: flex-end;
978
+ gap: 6px;
975
979
  }
976
- .hqpu-head-main {
980
+ .hqpu-engine-note {
977
981
  display: flex;
978
982
  align-items: center;
979
- gap: 8px;
980
- min-width: 0;
983
+ gap: 4px;
981
984
  margin: 0;
985
+ font-size: var(--hq-fs-micro);
986
+ color: var(--hq-faint);
982
987
  }
983
- .hqpu-brand-icon {
988
+ .hqpu-engine-note svg {
989
+ flex: none;
990
+ color: var(--hq-amber);
991
+ }
992
+
993
+ /* ── Error / result banners ──────────────────────────────────────────────── */
994
+ .hqpu-error {
984
995
  display: flex;
985
- align-items: center;
986
- justify-content: center;
996
+ flex-direction: column;
997
+ gap: 6px;
998
+ padding: 10px;
999
+ border: 0.5px solid;
1000
+ border-radius: 8px;
1001
+ }
1002
+ .hqpu-error[data-tone='danger'] {
1003
+ border-color: color-mix(in srgb, var(--hq-rose) 35%, transparent);
1004
+ background: color-mix(in srgb, var(--hq-rose) 8%, transparent);
1005
+ }
1006
+ .hqpu-error[data-tone='warning'] {
1007
+ border-color: color-mix(in srgb, var(--hq-amber) 35%, transparent);
1008
+ background: color-mix(in srgb, var(--hq-amber) 8%, transparent);
1009
+ }
1010
+ .hqpu-error-head {
1011
+ display: flex;
1012
+ align-items: flex-start;
1013
+ gap: 6px;
1014
+ }
1015
+ .hqpu-error-icon {
1016
+ display: inline-flex;
987
1017
  flex: none;
988
- width: 24px;
989
- height: 24px;
990
- border-radius: 6px;
991
- background: color-mix(in srgb, var(--hq-brand) 20%, transparent);
992
- color: var(--hq-brand-soft);
1018
+ margin-top: 1px;
1019
+ color: var(--hq-rose);
993
1020
  }
994
- .hqpu-brand-icon svg { width: 14px; height: 14px; display: block; }
995
- .hqpu-title-block { min-width: 0; }
996
- .hqpu-title {
1021
+ .hqpu-error[data-tone='warning'] .hqpu-error-icon {
1022
+ color: var(--hq-amber);
1023
+ }
1024
+ .hqpu-error-text {
1025
+ min-width: 0;
1026
+ }
1027
+ .hqpu-error-title {
997
1028
  margin: 0;
998
- font-size: 13px;
1029
+ font-size: var(--hq-fs-body);
999
1030
  font-weight: 600;
1000
- line-height: 16px;
1001
- color: var(--hq-text);
1031
+ line-height: 15px;
1032
+ color: var(--hq-rose);
1002
1033
  }
1003
- .hqpu-sub {
1034
+ .hqpu-error[data-tone='warning'] .hqpu-error-title {
1035
+ color: var(--hq-amber-ink);
1036
+ }
1037
+ .hqpu-error-body {
1004
1038
  margin: 2px 0 0;
1005
- font-size: 10px;
1039
+ font-size: var(--hq-fs-micro);
1006
1040
  line-height: 14px;
1007
1041
  color: var(--hq-muted);
1008
- white-space: nowrap;
1009
- overflow: hidden;
1010
- text-overflow: ellipsis;
1011
- }
1012
- .hqpu-toolbar { gap: 2px; }
1013
- .hqpu-icon-btn {
1014
- width: 26px;
1015
- height: 26px;
1016
- border-radius: 6px;
1017
- color: var(--hq-muted);
1042
+ overflow-wrap: anywhere;
1018
1043
  }
1019
- .hqpu-icon-btn:hover:not(:disabled) {
1020
- background: var(--hq-hover);
1021
- color: var(--hq-text);
1044
+ .hqpu-error-code {
1045
+ display: inline-block;
1046
+ margin-top: 3px;
1047
+ padding: 0 4px;
1048
+ border-radius: 3px;
1049
+ background: color-mix(in srgb, var(--hq-rose) 12%, transparent);
1050
+ color: var(--hq-rose);
1051
+ font-family: var(--hq-mono);
1052
+ font-size: var(--hq-fs-micro);
1022
1053
  }
1023
- .hqpu-icon-btn svg { width: 14px; height: 14px; }
1024
-
1025
- /* Collapsible settings panel */
1026
- .hqpu-settings {
1027
- flex: none;
1028
- padding: 10px 12px;
1029
- border-bottom: 0.5px solid var(--hq-border);
1030
- background: var(--hq-card-soft);
1054
+ .hqpu-error[data-tone='warning'] .hqpu-error-code {
1055
+ background: color-mix(in srgb, var(--hq-amber) 14%, transparent);
1056
+ color: var(--hq-amber-ink);
1031
1057
  }
1032
- .hqpu-settings-row {
1058
+ .hqpu-error-foot {
1033
1059
  display: flex;
1034
1060
  align-items: center;
1035
1061
  justify-content: space-between;
1036
1062
  gap: 8px;
1037
- margin-bottom: 8px;
1063
+ padding-top: 6px;
1064
+ border-top: 0.5px solid color-mix(in srgb, currentColor 12%, transparent);
1065
+ font-size: var(--hq-fs-micro);
1066
+ color: var(--hq-muted);
1067
+ }
1068
+ .hqpu-error-actions {
1069
+ display: flex;
1070
+ flex: none;
1071
+ gap: 10px;
1072
+ }
1073
+
1074
+ .hqpu-alert {
1075
+ display: flex;
1076
+ align-items: flex-start;
1077
+ gap: 8px;
1078
+ padding: 10px;
1079
+ border-radius: 8px;
1038
1080
  }
1039
- .hqpu-settings-title {
1040
- font-size: 11px;
1041
- font-weight: 500;
1042
- color: var(--hq-text-code);
1081
+ .hqpu-alert--amber {
1082
+ border: 0.5px solid color-mix(in srgb, var(--hq-amber) 32%, transparent);
1083
+ background: color-mix(in srgb, var(--hq-amber) 10%, transparent);
1043
1084
  }
1044
- .hqpu-origin-badge {
1045
- font-size: 9px;
1046
- padding: 1px 6px;
1047
- border-radius: 4px;
1048
- border: 0.5px solid;
1085
+ .hqpu-alert-icon {
1086
+ display: inline-flex;
1087
+ flex: none;
1088
+ color: var(--hq-amber);
1049
1089
  }
1050
- .hqpu-origin-badge[data-origin='config'] {
1051
- background: color-mix(in srgb, var(--dsw-alias-state-success-tertiary) 55%, transparent);
1052
- color: var(--hq-emerald);
1053
- border-color: color-mix(in srgb, var(--hq-emerald) 40%, transparent);
1090
+ .hqpu-alert-main {
1091
+ min-width: 0;
1054
1092
  }
1055
- .hqpu-origin-badge[data-origin='override'] {
1056
- background: color-mix(in srgb, var(--dsw-alias-state-warn-tertiary) 55%, transparent);
1057
- color: var(--hq-amber);
1058
- border-color: color-mix(in srgb, var(--hq-amber) 40%, transparent);
1093
+ .hqpu-alert-title {
1094
+ font-size: var(--hq-fs-meta);
1095
+ font-weight: 600;
1096
+ color: var(--hq-amber-ink);
1059
1097
  }
1060
- .hqpu-settings label {
1061
- display: block;
1062
- font-size: 10px;
1098
+ .hqpu-alert-body {
1099
+ margin: 2px 0 0;
1100
+ font-size: var(--hq-fs-micro);
1101
+ line-height: 14px;
1063
1102
  color: var(--hq-muted);
1064
- margin: 6px 0 3px;
1065
- }
1066
- .hqpu-source-input {
1067
- width: 100%;
1068
- height: 26px;
1069
- font-size: 10px;
1070
- font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
1071
- background: var(--hq-bg);
1072
- border: 0.5px solid var(--hq-border);
1073
- border-radius: 5px;
1074
- color: var(--hq-text-code);
1075
- padding: 0 8px;
1076
- box-sizing: border-box;
1077
1103
  }
1078
- .hqpu-source-input:focus-visible {
1079
- outline: none;
1080
- border-color: var(--hq-brand-soft);
1104
+ .hqpu-alert-body code {
1105
+ padding: 0 3px;
1106
+ border-radius: 3px;
1107
+ background: color-mix(in srgb, var(--hq-amber) 16%, transparent);
1108
+ font-family: var(--hq-mono);
1081
1109
  }
1082
- .hqpu-settings-actions {
1083
- display: flex;
1084
- justify-content: flex-end;
1085
- gap: 6px;
1086
- margin-top: 8px;
1110
+ .hqpu-alert-actions {
1111
+ margin-top: 4px;
1087
1112
  }
1088
-
1089
- /* Scroll body */
1090
- .hqpu-scroll {
1091
- flex: 1;
1092
- padding: 12px;
1093
- background: var(--hq-bg);
1094
- --dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2);
1095
- --dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2);
1113
+ .hqpu-alert-dismiss {
1114
+ padding: 0;
1115
+ border: 0;
1116
+ background: none;
1117
+ color: var(--hq-amber-ink);
1118
+ font: inherit;
1119
+ font-size: var(--hq-fs-micro);
1120
+ text-decoration: underline;
1121
+ cursor: pointer;
1096
1122
  }
1097
- .hqpu-column {
1098
- max-width: none;
1123
+ .hqpu-alert-green {
1099
1124
  margin: 0;
1100
- gap: 10px;
1125
+ padding: 6px 10px;
1126
+ border: 0.5px solid color-mix(in srgb, var(--hq-emerald) 30%, transparent);
1127
+ border-radius: 6px;
1128
+ background: color-mix(in srgb, var(--hq-emerald) 8%, transparent);
1129
+ color: var(--hq-emerald);
1130
+ font-size: var(--hq-fs-micro);
1101
1131
  }
1102
- .hqpu-stack { gap: 10px; }
1103
1132
 
1104
- /* ── Active-job banner ─────────────────────────────────────────────────── */
1133
+ /* ── Active job ──────────────────────────────────────────────────────────── */
1105
1134
  .hqpu-active {
1106
- position: relative;
1107
- padding: 12px;
1135
+ display: flex;
1136
+ flex-direction: column;
1137
+ gap: 8px;
1138
+ padding: 10px;
1139
+ border: 0.5px solid color-mix(in srgb, var(--hq-brand) 40%, transparent);
1108
1140
  border-radius: 8px;
1109
- border: 0.5px solid color-mix(in srgb, var(--hq-brand-soft) 30%, transparent);
1110
- background: var(--hq-bg);
1111
- box-shadow: var(--dsw-elevation-prominent);
1141
+ background: var(--hq-card);
1112
1142
  }
1113
1143
  .hqpu-active-top {
1114
1144
  display: flex;
@@ -1121,9 +1151,14 @@ const VIEW_CSS_TEXT = `
1121
1151
  align-items: center;
1122
1152
  gap: 6px;
1123
1153
  min-width: 0;
1124
- font-size: 11px;
1154
+ font-size: var(--hq-fs-body);
1125
1155
  font-weight: 600;
1126
- color: var(--hq-text-strong);
1156
+ color: var(--hq-text);
1157
+ }
1158
+ .hqpu-active-label {
1159
+ overflow: hidden;
1160
+ text-overflow: ellipsis;
1161
+ white-space: nowrap;
1127
1162
  }
1128
1163
  .hqpu-pulse-dot {
1129
1164
  position: relative;
@@ -1136,426 +1171,548 @@ const VIEW_CSS_TEXT = `
1136
1171
  position: absolute;
1137
1172
  inset: 0;
1138
1173
  border-radius: 50%;
1139
- background: var(--hq-brand-soft);
1174
+ background: var(--hq-brand);
1140
1175
  }
1141
1176
  .hqpu-pulse-ring {
1142
1177
  position: absolute;
1143
1178
  inset: 0;
1144
1179
  border-radius: 50%;
1145
- background: var(--hq-brand-soft);
1180
+ background: var(--hq-brand);
1146
1181
  opacity: 0.6;
1147
1182
  animation: hqpu-ping 1.4s cubic-bezier(0, 0, 0.2, 1) infinite;
1148
1183
  }
1149
1184
  @keyframes hqpu-ping {
1150
- 75%, 100% { transform: scale(2.2); opacity: 0; }
1185
+ 75%, 100% { transform: scale(2.4); opacity: 0; }
1151
1186
  }
1152
1187
  .hqpu-active-percent {
1153
1188
  flex: none;
1154
- font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
1155
- font-size: 11px;
1156
- font-weight: 500;
1189
+ font-family: var(--hq-mono);
1190
+ font-size: var(--hq-fs-body);
1191
+ font-weight: 700;
1157
1192
  color: var(--hq-brand-soft);
1158
1193
  }
1159
1194
  .hqpu-progress {
1160
1195
  height: 6px;
1161
- margin-top: 10px;
1162
1196
  border-radius: 999px;
1163
- background: var(--hq-card);
1197
+ background: var(--hq-border);
1164
1198
  overflow: hidden;
1165
1199
  }
1166
1200
  .hqpu-progress-fill {
1167
- background: linear-gradient(to right, var(--hq-brand), var(--hq-brand-soft));
1201
+ height: 100%;
1202
+ border-radius: 999px;
1203
+ background: linear-gradient(90deg, var(--hq-brand), var(--hq-brand-soft));
1168
1204
  transition: width 0.3s ease;
1169
1205
  }
1170
1206
  .hqpu-active-meta {
1171
1207
  display: flex;
1208
+ align-items: baseline;
1172
1209
  justify-content: space-between;
1173
1210
  gap: 8px;
1174
- margin-top: 4px;
1175
- font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
1176
- font-size: 10px;
1211
+ font-size: var(--hq-fs-micro);
1212
+ line-height: 14px;
1213
+ color: var(--hq-muted);
1214
+ }
1215
+ .hqpu-num {
1216
+ font-family: var(--hq-mono);
1217
+ font-size: var(--hq-fs-micro);
1218
+ }
1219
+ .hqpu-active-versions {
1220
+ font-family: var(--hq-mono);
1221
+ font-size: var(--hq-fs-micro);
1177
1222
  color: var(--hq-muted);
1178
1223
  }
1179
- /* 4-step indicator row */
1224
+ .hqpu-active-note {
1225
+ margin: 0;
1226
+ font-size: var(--hq-fs-micro);
1227
+ line-height: 14px;
1228
+ color: var(--hq-faint);
1229
+ }
1180
1230
  .hqpu-job-steps4 {
1181
1231
  display: grid;
1182
1232
  grid-template-columns: repeat(4, 1fr);
1183
- gap: 4px;
1184
- margin-top: 10px;
1233
+ gap: 2px;
1185
1234
  padding-top: 8px;
1186
1235
  border-top: 0.5px solid var(--hq-border);
1187
1236
  text-align: center;
1188
- font-size: 9px;
1237
+ font-size: var(--hq-fs-micro);
1189
1238
  font-weight: 500;
1190
1239
  }
1191
- .hqpu-job-step--done { color: var(--hq-emerald); }
1240
+ .hqpu-job-step--done {
1241
+ color: var(--hq-emerald);
1242
+ }
1192
1243
  .hqpu-job-step--active {
1193
1244
  color: var(--hq-brand-soft);
1194
1245
  animation: hqpu-step-pulse 1.2s ease-in-out infinite;
1195
1246
  }
1196
- .hqpu-job-step--todo { color: var(--hq-muted); }
1247
+ .hqpu-job-step--todo {
1248
+ color: var(--hq-faint);
1249
+ }
1197
1250
  @keyframes hqpu-step-pulse {
1198
1251
  0%, 100% { opacity: 1; }
1199
1252
  50% { opacity: 0.45; }
1200
1253
  }
1201
1254
 
1202
- /* ── Result / rollback alert (amber) vs green success ──────────────────── */
1203
- .hqpu-alert {
1255
+ /* ── Footer ──────────────────────────────────────────────────────────────── */
1256
+ .hqpu-footer {
1257
+ flex: none;
1204
1258
  display: flex;
1205
- align-items: flex-start;
1206
- gap: 8px;
1207
- padding: 10px;
1208
- border-radius: 8px;
1259
+ flex-direction: column;
1260
+ gap: 6px;
1261
+ padding: 10px 12px 12px;
1262
+ border-top: 0.5px solid var(--hq-border);
1263
+ background: var(--hq-bg);
1209
1264
  }
1210
- .hqpu-alert--amber {
1211
- background: color-mix(in srgb, var(--dsw-alias-state-warn-tertiary) 60%, transparent);
1212
- border: 0.5px solid color-mix(in srgb, var(--hq-amber) 35%, transparent);
1265
+ .hqpu-btn--block {
1266
+ display: inline-flex;
1267
+ align-items: center;
1268
+ justify-content: center;
1269
+ gap: 8px;
1270
+ width: 100%;
1271
+ height: 36px;
1272
+ padding: 0 12px;
1273
+ border: 0;
1274
+ border-radius: 10px;
1275
+ background: var(--hq-brand);
1276
+ color: var(--hq-on-accent);
1277
+ font: inherit;
1278
+ font-size: var(--hq-fs-body);
1279
+ font-weight: 500;
1280
+ cursor: pointer;
1281
+ box-shadow: 0 4px 12px color-mix(in srgb, var(--hq-brand) 35%, transparent);
1282
+ transition: background 0.15s ease, box-shadow 0.15s ease;
1213
1283
  }
1214
- .hqpu-alert--amber svg { color: var(--hq-amber); flex: none; margin-top: 1px; }
1215
- .hqpu-alert-green {
1216
- background: color-mix(in srgb, var(--dsw-alias-state-success-tertiary) 55%, transparent);
1217
- border: 0.5px solid color-mix(in srgb, var(--hq-emerald) 35%, transparent);
1218
- color: var(--hq-emerald);
1219
- font-size: 11px;
1284
+ .hqpu-btn--block:hover:not(:disabled) {
1285
+ background: var(--hq-brand-soft);
1220
1286
  }
1221
- .hqpu-alert-title {
1222
- font-size: 11px;
1223
- font-weight: 500;
1224
- color: var(--dsw-alias-state-warn-label);
1287
+ /* The disabled CTA states the reason in its own label, so it must stay legible
1288
+ * (a 40 %-opacity red button reads as "broken", not as "nothing to do"). */
1289
+ .hqpu-btn--block:disabled {
1290
+ background: var(--hq-card-soft);
1291
+ color: var(--hq-faint);
1292
+ box-shadow: none;
1293
+ cursor: not-allowed;
1225
1294
  }
1226
- .hqpu-alert-body {
1227
- margin: 2px 0 0;
1228
- font-size: 10px;
1229
- line-height: 15px;
1230
- color: color-mix(in srgb, var(--dsw-alias-state-warn-label) 85%, transparent);
1295
+ .hqpu-cta-spinner {
1296
+ width: 13px;
1297
+ height: 13px;
1298
+ border-radius: 50%;
1299
+ border: 1.5px solid color-mix(in srgb, currentColor 30%, transparent);
1300
+ border-top-color: currentColor;
1301
+ animation: hqpu-spin 0.9s linear infinite;
1231
1302
  }
1232
- .hqpu-alert-body code {
1233
- font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
1234
- background: color-mix(in srgb, var(--hq-amber) 18%, transparent);
1235
- padding: 1px 4px;
1236
- border-radius: 3px;
1303
+ @keyframes hqpu-spin {
1304
+ to { transform: rotate(360deg); }
1237
1305
  }
1238
- .hqpu-alert-actions {
1306
+ .hqpu-foot-meta {
1239
1307
  display: flex;
1308
+ align-items: center;
1309
+ justify-content: space-between;
1240
1310
  gap: 8px;
1241
- margin-top: 6px;
1311
+ min-height: 14px;
1312
+ font-size: var(--hq-fs-micro);
1313
+ color: var(--hq-faint);
1242
1314
  }
1243
- .hqpu-alert-dismiss {
1244
- background: none;
1245
- border: 0;
1246
- padding: 0;
1247
- font-size: 10px;
1248
- color: var(--dsw-alias-state-warn-label);
1249
- cursor: pointer;
1250
- text-decoration: underline;
1315
+ .hqpu-footnote {
1316
+ margin: 0;
1317
+ font-size: var(--hq-fs-micro);
1318
+ line-height: 13px;
1319
+ text-align: center;
1320
+ color: var(--hq-faint);
1251
1321
  }
1252
1322
 
1253
- /* ── Runtime engine card ────────────────────────────────────────────────── */
1254
- .hqpu-engine {
1323
+ /* ── Job log drawer ──────────────────────────────────────────────────────
1324
+ * Covers the body and the footer, never the header: the log button and the
1325
+ * close button stay in place while it is open. */
1326
+ .hqpu-log {
1327
+ position: absolute;
1328
+ inset: 0;
1329
+ z-index: 10;
1330
+ display: flex;
1331
+ flex-direction: column;
1332
+ gap: 8px;
1255
1333
  padding: 12px;
1334
+ background: var(--hq-bg);
1335
+ }
1336
+ .hqpu-log-head {
1337
+ display: flex;
1338
+ align-items: center;
1339
+ justify-content: space-between;
1340
+ gap: 8px;
1341
+ padding-bottom: 8px;
1342
+ border-bottom: 0.5px solid var(--hq-border);
1343
+ }
1344
+ .hqpu-log-title {
1345
+ display: flex;
1346
+ align-items: center;
1347
+ gap: 6px;
1348
+ min-width: 0;
1349
+ }
1350
+ .hqpu-log-name {
1351
+ font-size: var(--hq-fs-body);
1352
+ font-weight: 600;
1353
+ color: var(--hq-text);
1354
+ white-space: nowrap;
1355
+ }
1356
+ .hqpu-log-body {
1357
+ display: flex;
1358
+ flex: 1;
1359
+ flex-direction: column;
1360
+ gap: 8px;
1361
+ min-height: 0;
1362
+ overflow-y: auto;
1363
+ --dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2);
1364
+ }
1365
+ .hqpu-log-block {
1366
+ display: flex;
1367
+ flex-direction: column;
1368
+ gap: 4px;
1369
+ padding: 8px;
1256
1370
  border-radius: 8px;
1371
+ background: var(--hq-card-soft);
1372
+ font-family: var(--hq-mono);
1373
+ font-size: var(--hq-fs-micro);
1374
+ }
1375
+ .hqpu-log-meta {
1376
+ display: flex;
1377
+ align-items: center;
1378
+ justify-content: space-between;
1379
+ gap: 8px;
1380
+ padding-bottom: 4px;
1381
+ border-bottom: 0.5px solid var(--hq-border);
1382
+ color: var(--hq-faint);
1383
+ overflow-wrap: anywhere;
1384
+ }
1385
+ .hqpu-log-line {
1386
+ display: flex;
1387
+ align-items: center;
1388
+ gap: 6px;
1389
+ color: var(--hq-muted);
1390
+ line-height: 15px;
1391
+ }
1392
+ .hqpu-log-line--empty {
1393
+ color: var(--hq-faint);
1394
+ }
1395
+ .hqpu-log-line-text {
1396
+ min-width: 0;
1397
+ overflow-wrap: anywhere;
1398
+ }
1399
+ .hqpu-log-dot {
1400
+ flex: none;
1401
+ width: 5px;
1402
+ height: 5px;
1403
+ border-radius: 50%;
1404
+ background: var(--hq-border-strong);
1405
+ }
1406
+ .hqpu-log-dot[data-state='done'] { background: var(--hq-emerald); }
1407
+ .hqpu-log-dot[data-state='error'] { background: var(--hq-rose); }
1408
+ .hqpu-log-dot[data-state='warning'] { background: var(--hq-amber); }
1409
+ .hqpu-log-dot[data-state='idle'] { background: var(--hq-border-strong); }
1410
+ .hqpu-log-detail {
1411
+ display: flex;
1412
+ flex-direction: column;
1413
+ gap: 3px;
1414
+ padding: 8px;
1257
1415
  border: 0.5px solid var(--hq-border);
1258
- background: var(--hq-card);
1416
+ border-radius: 8px;
1417
+ font-size: var(--hq-fs-micro);
1418
+ color: var(--hq-muted);
1259
1419
  }
1260
- .hqpu-engine-head {
1420
+ .hqpu-log-detail p {
1421
+ margin: 0;
1422
+ overflow-wrap: anywhere;
1423
+ }
1424
+ .hqpu-log-recent {
1425
+ display: flex;
1426
+ flex-direction: column;
1427
+ gap: 3px;
1428
+ }
1429
+ .hqpu-log-recent-title {
1430
+ margin: 0 0 2px;
1431
+ font-size: var(--hq-fs-micro);
1432
+ font-weight: 600;
1433
+ color: var(--hq-muted);
1434
+ }
1435
+ .hqpu-log-recent-row {
1261
1436
  display: flex;
1262
1437
  align-items: center;
1263
1438
  justify-content: space-between;
1264
1439
  gap: 8px;
1440
+ padding: 4px 6px;
1441
+ border-radius: 5px;
1442
+ background: var(--hq-card-soft);
1443
+ font-size: var(--hq-fs-micro);
1444
+ color: var(--hq-muted);
1265
1445
  }
1266
- .hqpu-engine-title {
1446
+ .hqpu-log-recent-meta {
1447
+ flex: none;
1448
+ font-family: var(--hq-mono);
1449
+ color: var(--hq-faint);
1450
+ }
1451
+ .hqpu-log-foot {
1267
1452
  display: flex;
1268
1453
  align-items: center;
1454
+ justify-content: space-between;
1455
+ gap: 8px;
1456
+ padding-top: 8px;
1457
+ border-top: 0.5px solid var(--hq-border);
1458
+ }
1459
+
1460
+ /* ── Toast ──────────────────────────────────────────────────────────────── */
1461
+ .hqpu-toast-host {
1462
+ position: absolute;
1463
+ top: 8px;
1464
+ left: 12px;
1465
+ right: 12px;
1466
+ z-index: 20;
1467
+ display: flex;
1468
+ flex-direction: column;
1269
1469
  gap: 6px;
1270
- font-size: 12px;
1271
- font-weight: 600;
1272
- color: var(--hq-text-strong);
1470
+ pointer-events: none;
1273
1471
  }
1274
- .hqpu-engine-title svg { width: 14px; height: 14px; color: var(--hq-muted); }
1275
- .hqpu-chip {
1276
- font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
1277
- font-size: 9px;
1278
- padding: 2px 6px;
1279
- border-radius: 4px;
1280
- background: var(--hq-card);
1281
- border: 0.5px solid var(--hq-border-strong);
1282
- color: var(--hq-text-code);
1472
+ .hqpu-toast-host:empty {
1473
+ display: none;
1283
1474
  }
1284
- .hqpu-engine-update-row {
1475
+ .hqpu-toast {
1285
1476
  display: flex;
1286
1477
  align-items: center;
1287
- justify-content: space-between;
1288
1478
  gap: 8px;
1289
- margin-top: 10px;
1290
- padding: 8px;
1291
- border-radius: 6px;
1292
- border: 0.5px solid color-mix(in srgb, var(--hq-border) 70%, transparent);
1293
- background: color-mix(in srgb, var(--hq-card) 60%, transparent);
1479
+ padding: 8px 10px;
1480
+ border-radius: 8px;
1481
+ background: var(--dsw-alias-toast-bg, var(--hq-card));
1482
+ color: var(--dsw-alias-toast-label, var(--hq-text));
1483
+ font-size: var(--hq-fs-meta);
1484
+ box-shadow: var(--dsw-shadow-lv3, 0 8px 24px rgba(0, 0, 0, 0.2));
1485
+ pointer-events: auto;
1486
+ animation: hqpu-toast-in 160ms ease-out;
1294
1487
  }
1295
- .hqpu-engine-new-line {
1296
- display: flex;
1297
- align-items: center;
1298
- gap: 4px;
1299
- font-size: 11px;
1300
- font-weight: 500;
1301
- color: var(--hq-text-code);
1488
+ .hqpu-toast-icon {
1489
+ display: inline-flex;
1490
+ flex: none;
1491
+ color: var(--hq-amber);
1302
1492
  }
1303
- .hqpu-tag--brand {
1304
- font-size: 9px;
1305
- padding: 1px 4px;
1306
- border-radius: 3px;
1307
- color: var(--hq-brand-soft);
1308
- background: color-mix(in srgb, var(--hq-brand-dim) 60%, transparent);
1309
- border: 0.5px solid color-mix(in srgb, var(--hq-brand) 45%, transparent);
1493
+ .hqpu-toast-icon[data-tone='success'] {
1494
+ color: var(--hq-emerald);
1310
1495
  }
1311
- .hqpu-engine-size {
1312
- margin: 3px 0 0;
1313
- font-size: 10px;
1314
- color: var(--hq-muted);
1315
- overflow: hidden;
1316
- text-overflow: ellipsis;
1317
- white-space: nowrap;
1496
+ .hqpu-toast-icon[data-tone='error'] {
1497
+ color: var(--hq-rose);
1318
1498
  }
1319
- .hqpu-btn--engine {
1499
+ .hqpu-toast-text {
1500
+ min-width: 0;
1501
+ flex: 1;
1502
+ overflow-wrap: anywhere;
1503
+ }
1504
+ .hqpu-toast-close {
1505
+ display: inline-flex;
1320
1506
  flex: none;
1321
- height: auto;
1322
- padding: 6px 10px;
1323
- font-size: 10px;
1324
- background: var(--hq-card);
1325
- border: 0.5px solid var(--hq-border-strong);
1326
- color: var(--hq-text-strong);
1327
- border-radius: 5px;
1507
+ padding: 0;
1508
+ border: 0;
1509
+ background: none;
1510
+ color: inherit;
1511
+ opacity: 0.55;
1512
+ cursor: pointer;
1328
1513
  }
1329
- .hqpu-btn--engine:hover:not(:disabled) { background: var(--hq-border); }
1330
- .hqpu-engine-note {
1331
- display: flex;
1332
- align-items: center;
1333
- gap: 4px;
1334
- margin: 8px 0 0;
1335
- font-size: 9px;
1336
- color: var(--hq-muted);
1514
+ .hqpu-toast-close:hover {
1515
+ opacity: 1;
1516
+ }
1517
+ @keyframes hqpu-toast-in {
1518
+ from { opacity: 0; transform: translateY(-4px); }
1519
+ to { opacity: 1; transform: none; }
1337
1520
  }
1338
- .hqpu-engine-note svg { width: 10px; height: 10px; color: var(--hq-amber); flex: none; }
1339
1521
 
1340
- /* ── Plugin cards ───────────────────────────────────────────────────────── */
1341
- .hqpu-section-head {
1342
- display: flex;
1522
+ /* ── Badges, banners, misc shared bits ──────────────────────────────────── */
1523
+ .hqpu-tag {
1524
+ display: inline-flex;
1343
1525
  align-items: center;
1344
- justify-content: space-between;
1345
- gap: 8px;
1346
- padding: 0 2px;
1526
+ flex: none;
1527
+ padding: 0 6px;
1528
+ border-radius: 999px;
1529
+ font-size: var(--hq-fs-micro);
1530
+ font-weight: 500;
1531
+ line-height: 16px;
1532
+ white-space: nowrap;
1533
+ }
1534
+ .hqpu-tag[data-tone='success'] {
1535
+ background: color-mix(in srgb, var(--hq-emerald) 12%, transparent);
1536
+ color: var(--hq-emerald);
1537
+ }
1538
+ .hqpu-tag[data-tone='warning'] {
1539
+ background: color-mix(in srgb, var(--hq-amber) 14%, transparent);
1540
+ color: var(--hq-amber-ink);
1541
+ }
1542
+ .hqpu-tag[data-tone='danger'] {
1543
+ background: color-mix(in srgb, var(--hq-rose) 12%, transparent);
1544
+ color: var(--hq-rose);
1545
+ }
1546
+ .hqpu-tag[data-tone='info'] {
1547
+ background: color-mix(in srgb, var(--dsw-alias-state-business-primary, var(--hq-brand)) 12%, transparent);
1548
+ color: var(--dsw-alias-state-business-primary, var(--hq-brand));
1549
+ }
1550
+ .hqpu-tag[data-tone='neutral'] {
1551
+ background: var(--hq-card-soft);
1552
+ color: var(--hq-muted);
1347
1553
  }
1348
- .hqpu-section-title {
1554
+ .hqpu-banner {
1349
1555
  display: flex;
1350
- align-items: center;
1556
+ align-items: flex-start;
1351
1557
  gap: 6px;
1352
- font-size: 12px;
1353
- font-weight: 600;
1354
- color: var(--hq-text-strong);
1558
+ padding: 8px 10px;
1559
+ border-radius: 6px;
1560
+ font-size: var(--hq-fs-micro);
1561
+ line-height: 15px;
1562
+ color: var(--hq-muted);
1355
1563
  }
1356
- .hqpu-count-pill {
1357
- font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
1358
- font-size: 10px;
1359
- font-weight: 700;
1360
- padding: 1px 6px;
1361
- border-radius: 999px;
1362
- background: color-mix(in srgb, var(--hq-brand) 20%, transparent);
1363
- color: var(--hq-brand-soft);
1564
+ .hqpu-banner[data-tone='danger'] {
1565
+ background: color-mix(in srgb, var(--hq-rose) 8%, transparent);
1364
1566
  }
1365
- .hqpu-all-changelog {
1366
- background: none;
1367
- border: 0;
1368
- padding: 0;
1369
- font-size: 10px;
1567
+ .hqpu-banner[data-tone='danger'] span {
1568
+ color: var(--hq-rose);
1569
+ }
1570
+ .hqpu-release-summary {
1571
+ margin: 0;
1572
+ font-size: var(--hq-fs-meta);
1573
+ line-height: 16px;
1370
1574
  color: var(--hq-muted);
1371
- cursor: pointer;
1372
1575
  }
1373
- .hqpu-all-changelog:hover { color: var(--hq-text); }
1374
-
1375
- .hqpu-cards {
1376
- gap: 8px;
1377
- display: grid;
1378
- grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
1379
- align-items: start;
1576
+ .hqpu-release-notes {
1577
+ margin: 0;
1578
+ padding-left: 16px;
1579
+ font-size: var(--hq-fs-micro);
1580
+ line-height: 16px;
1581
+ color: var(--hq-muted);
1380
1582
  }
1381
- .hqpu-card {
1382
- position: relative;
1583
+ .hqpu-release-meta {
1383
1584
  margin: 0;
1384
- padding: 10px;
1385
- border-radius: 8px;
1386
- border: 0.5px solid var(--hq-border);
1387
- background: var(--hq-card);
1388
- transition: border-color 0.15s ease;
1585
+ font-size: var(--hq-fs-micro);
1586
+ color: var(--hq-faint);
1389
1587
  }
1390
- .hqpu-card:hover { border-color: var(--hq-border-strong); }
1391
- .hqpu-card-head {
1392
- display: flex;
1393
- align-items: flex-start;
1394
- justify-content: space-between;
1395
- gap: 8px;
1396
- padding: 0;
1588
+ .hqpu-release-link {
1589
+ font-size: var(--hq-fs-micro);
1590
+ color: var(--hq-brand-soft);
1591
+ text-decoration: none;
1397
1592
  }
1398
- .hqpu-card-head > * { display: flex; align-items: center; gap: 8px; min-width: 0; }
1399
- .hqpu-avatar {
1400
- display: flex;
1401
- align-items: center;
1402
- justify-content: center;
1403
- flex: none;
1404
- width: 28px;
1405
- height: 28px;
1406
- border-radius: 6px;
1407
- border: 0.5px solid var(--hq-border-strong);
1408
- background: var(--hq-card);
1409
- color: var(--hq-text-code);
1410
- font-size: 10px;
1411
- font-weight: 700;
1593
+ .hqpu-release-link:hover {
1594
+ text-decoration: underline;
1412
1595
  }
1413
- .hqpu-card-title {
1414
- font-size: 12px;
1415
- font-weight: 600;
1416
- color: var(--hq-text-strong);
1596
+ .hqpu-status {
1597
+ margin: 0;
1598
+ font-size: var(--hq-fs-meta);
1599
+ line-height: 15px;
1600
+ color: var(--hq-muted);
1417
1601
  }
1418
- .hqpu-card-sub {
1419
- font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
1420
- font-size: 9px;
1602
+ .hqpu-empty {
1603
+ padding: 24px 0;
1604
+ text-align: center;
1605
+ font-size: var(--hq-fs-meta);
1421
1606
  color: var(--hq-faint);
1422
1607
  }
1423
- .hqpu-version-row {
1424
- display: flex;
1425
- align-items: center;
1426
- gap: 4px;
1427
- margin-top: 2px;
1428
- font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
1429
- font-size: 10px;
1430
- }
1431
- .hqpu-ver-old { color: var(--hq-muted); }
1432
- .hqpu-ver-arrow { color: var(--hq-border-strong); }
1433
- .hqpu-ver-new { color: var(--hq-emerald); font-weight: 700; }
1434
- .hqpu-log-toggle {
1608
+
1609
+ /* ── Buttons (settings / log drawer) ─────────────────────────────────────── */
1610
+ .hqpu-btn {
1435
1611
  display: inline-flex;
1436
1612
  align-items: center;
1437
- gap: 2px;
1438
- background: none;
1613
+ justify-content: center;
1614
+ gap: 6px;
1615
+ height: 30px;
1616
+ padding: 0 12px;
1439
1617
  border: 0;
1440
- padding: 4px;
1441
- font-size: 10px;
1442
- color: var(--hq-muted);
1618
+ border-radius: 8px;
1619
+ background: var(--hq-card-soft);
1620
+ color: var(--hq-text);
1621
+ font: inherit;
1622
+ font-size: var(--hq-fs-meta);
1443
1623
  cursor: pointer;
1444
1624
  }
1445
- .hqpu-log-toggle:hover { color: var(--hq-text); }
1446
- .hqpu-log-toggle svg { width: 12px; height: 12px; transition: transform 0.15s ease; }
1447
- .hqpu-log-toggle[data-open='true'] svg { transform: rotate(180deg); }
1448
- .hqpu-changelog-actions {
1449
- display: flex;
1450
- gap: 8px;
1451
- margin-top: 10px;
1452
- padding-top: 8px;
1453
- border-top: 0.5px solid var(--hq-border);
1625
+ .hqpu-btn svg {
1626
+ display: block;
1454
1627
  }
1455
- .hqpu-card-action {
1456
- height: 28px;
1457
- padding: 0 10px;
1458
- border: 0.5px solid var(--hq-border-strong);
1459
- border-radius: 6px;
1460
- background: var(--hq-card);
1461
- color: var(--hq-text);
1462
- font-size: 10px;
1463
- cursor: pointer;
1628
+ .hqpu-btn:hover:not(:disabled) {
1629
+ background: var(--hq-hover);
1464
1630
  }
1465
- .hqpu-card-action:hover { border-color: var(--hq-brand-soft); color: var(--hq-brand-soft); }
1466
- .hqpu-card-action--install {
1467
- border-color: var(--hq-brand);
1468
- background: var(--hq-brand);
1469
- color: var(--hq-brand-fg);
1631
+ .hqpu-btn:disabled {
1632
+ opacity: 0.5;
1633
+ cursor: default;
1470
1634
  }
1471
- .hqpu-card-action--install:hover { border-color: var(--hq-brand-soft); background: var(--hq-brand-soft); color: var(--hq-brand-fg); }
1472
- .hqpu-changelog-drawer {
1473
- margin-top: 8px;
1474
- padding: 8px;
1475
- border-radius: 6px;
1476
- border: 0.5px solid var(--hq-border);
1477
- background: color-mix(in srgb, var(--hq-card) 80%, transparent);
1478
- font-size: 10px;
1635
+ .hqpu-btn--sm {
1636
+ height: 26px;
1637
+ padding: 0 8px;
1638
+ font-size: var(--hq-fs-micro);
1479
1639
  }
1480
- .hqpu-changelog-drawer-title {
1481
- font-size: 9px;
1482
- font-weight: 500;
1483
- text-transform: uppercase;
1484
- letter-spacing: 0.05em;
1640
+ .hqpu-btn--ghost {
1641
+ background: transparent;
1485
1642
  color: var(--hq-muted);
1486
1643
  }
1487
- .hqpu-changelog-drawer ul {
1488
- margin: 4px 0 0;
1489
- padding-left: 16px;
1490
- color: var(--hq-text-code);
1491
- line-height: 16px;
1644
+ .hqpu-btn--brand {
1645
+ background: var(--hq-brand);
1646
+ color: var(--hq-on-accent);
1647
+ }
1648
+ .hqpu-btn--brand:hover:not(:disabled) {
1649
+ background: var(--hq-brand-soft);
1650
+ }
1651
+ /* The runtime confirm is the same amber as the card it lives in (the
1652
+ * prototype's treatment): red is the plugin-set path, amber the runtime path.
1653
+ * Ink is a warm near-black because amber stays bright in both themes — white
1654
+ * on amber-500 sits near 2:1 and reads as a disabled button. */
1655
+ .hqpu-btn--engine-solid {
1656
+ height: 28px;
1657
+ padding: 0 12px;
1658
+ background: var(--hq-amber);
1659
+ color: var(--hq-on-amber);
1660
+ font-size: var(--hq-fs-meta);
1661
+ font-weight: 600;
1662
+ }
1663
+ .hqpu-btn--engine-solid:hover:not(:disabled) {
1664
+ background: color-mix(in srgb, var(--hq-amber) 85%, #000);
1492
1665
  }
1493
1666
 
1494
- /* Sticky footer */
1495
- .hqpu-footer {
1496
- flex: none;
1497
- display: flex;
1498
- flex-direction: column;
1499
- align-items: stretch;
1500
- gap: 8px;
1501
- padding: 10px 12px 12px;
1502
- border-top: 0.5px solid var(--hq-border);
1503
- background: color-mix(in srgb, var(--hq-bg) 95%, transparent);
1667
+ /* ── Skeleton ────────────────────────────────────────────────────────────── */
1668
+ .hqpu-skeleton-fill {
1669
+ background: var(--hq-border);
1670
+ animation: hqpu-skeleton 2s cubic-bezier(0.36, 0, 0.64, 1) infinite;
1504
1671
  }
1505
- .hqpu-foot-meta {
1506
- display: flex;
1507
- align-items: center;
1508
- justify-content: space-between;
1509
- gap: 8px;
1510
- font-size: 10px;
1511
- color: var(--hq-muted);
1672
+ .hqpu-skeleton-title {
1673
+ width: 60%;
1674
+ height: 10px;
1675
+ border-radius: 3px;
1512
1676
  }
1513
- .hqpu-foot-set {
1514
- font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
1677
+ .hqpu-skeleton-sub {
1678
+ width: 40%;
1679
+ height: 8px;
1680
+ border-radius: 3px;
1515
1681
  }
1516
- .hqpu-foot-actions {
1517
- display: flex;
1518
- gap: 8px;
1682
+ .hqpu-card .hqpu-avatar.hqpu-skeleton-fill {
1683
+ background: var(--hq-border);
1519
1684
  }
1520
- .hqpu-btn--block {
1521
- flex: 1;
1522
- height: 32px;
1523
- background: var(--hq-brand);
1524
- color: var(--hq-brand-fg);
1525
- font-size: 11px;
1526
- font-weight: 500;
1527
- border-radius: 6px;
1528
- box-shadow: 0 4px 12px color-mix(in srgb, var(--hq-brand) 40%, transparent);
1685
+ @keyframes hqpu-skeleton {
1686
+ 0% { opacity: 1; }
1687
+ 40% { opacity: 0.55; }
1688
+ 80%, 100% { opacity: 1; }
1529
1689
  }
1530
- .hqpu-btn--block:hover:not(:disabled) { background: var(--hq-brand-soft); }
1531
- .hqpu-btn--block:disabled { opacity: 0.5; box-shadow: none; }
1532
- .hqpu-btn--ignore {
1533
- height: 32px;
1534
- padding: 0 10px;
1535
- background: var(--hq-card);
1536
- border: 0.5px solid var(--hq-border);
1537
- color: var(--hq-muted);
1538
- font-size: 10px;
1539
- border-radius: 6px;
1690
+
1691
+ /* ── Focus + reduced motion ──────────────────────────────────────────────── */
1692
+ .hqpu-panel button:focus-visible,
1693
+ .hqpu-panel a:focus-visible,
1694
+ .hqpu-panel input:focus-visible {
1695
+ outline: var(--dsw-focus-ring-width, 2px) solid
1696
+ var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));
1697
+ outline-offset: 1px;
1540
1698
  }
1541
- .hqpu-btn--ignore:hover:not(:disabled) { background: var(--hq-hover); color: var(--hq-text); }
1542
- .hqpu-footnote {
1543
- margin: 0;
1544
- font-size: 9px;
1545
- text-align: center;
1546
- color: var(--hq-muted);
1699
+ .hqpu-panel input:focus-visible {
1700
+ outline: none;
1701
+ border-color: var(--hq-brand-soft);
1702
+ }
1703
+
1704
+ @media (prefers-reduced-motion: reduce) {
1705
+ .hqpu-scrim,
1706
+ .hqpu-panel,
1707
+ .hqpu-skeleton-fill,
1708
+ .hqpu-cta-spinner,
1709
+ .hqpu-toast,
1710
+ .hqpu-pulse-ring,
1711
+ .hqpu-job-step--active {
1712
+ animation: none;
1713
+ }
1547
1714
  }
1548
1715
 
1549
- /* Shared bits on the new palette */
1550
- .hqpu-status { color: var(--hq-muted); font-size: 11px; }
1551
- .hqpu-empty { color: var(--hq-muted); font-size: 11px; padding: 16px 0; }
1552
- .hqpu-toast { background: var(--hq-card); border: 0.5px solid var(--hq-border); color: var(--hq-text); font-size: 12px; }
1553
- .hqpu-skeleton-fill { background: var(--hq-border); }
1554
- .hqpu-job { background: var(--hq-card); border-color: var(--hq-border); }
1555
- .hqpu-job-meta { color: var(--hq-muted); }
1556
- .hqpu-release-notes { color: var(--hq-muted); font-size: 11px; }
1557
- .hqpu-advanced > summary { color: var(--hq-muted); }
1558
- .hqpu-recent-jobs li { color: var(--hq-muted); font-size: 10px; }
1559
1716
  `
1560
1717
 
1561
1718
  const STYLE_TAG_ID = 'data-plugin-css="huaqiu-plugin-update"'
@@ -1730,31 +1887,14 @@ class UpdateStore implements UpdateStoreLike {
1730
1887
  }
1731
1888
 
1732
1889
  /**
1733
- * Badge: counts actionable plugin updates AND an available runtime update AND
1734
- * (while a job runs) an attention pill. A recent failed/rolled-back job with
1735
- * nothing actionable left also keeps a dot, so the outcome is never silently
1736
- * forgotten after the panel closes.
1890
+ * Sidebar badge: how many updates are still on offer — actionable plugin
1891
+ * items plus (once) the HQ Edge runtime update.
1892
+ *
1893
+ * The whole rule lives in {@link badgeCountFor}; in particular the job
1894
+ * history never contributes, which is what used to pin this at 1 forever.
1737
1895
  */
1738
1896
  badgeCount(): number {
1739
- const job = this.active
1740
- if (job !== null && !TERMINAL_PHASES.has(job.phase)) return 1
1741
- const report = this.current
1742
- if (report === null) return 0
1743
- let count = report.items.filter(
1744
- (item) => item.state === 'update' || item.state === 'install',
1745
- ).length
1746
- if (report.runtime?.updateAvailable) count += 1
1747
- if (count === 0) {
1748
- const latest = this.recent[0]
1749
- // Keep a visible attention dot for every terminal outcome, including
1750
- // success. Without this, a background update could finish while the
1751
- // panel was closed and the user would have no indication that a result
1752
- // is waiting in the update centre.
1753
- if (latest !== undefined && TERMINAL_PHASES.has(latest.phase)) {
1754
- count = 1
1755
- }
1756
- }
1757
- return count
1897
+ return badgeCountFor(this.current, this.active)
1758
1898
  }
1759
1899
 
1760
1900
  private emit(): void {
@@ -1776,6 +1916,12 @@ class UpdateStore implements UpdateStoreLike {
1776
1916
  // The active job settled (moved to recent/terminal) — the installing
1777
1917
  // footer state must clear on its own, without the panel's closure.
1778
1918
  this.installingFlag = false
1919
+ // …and the report is now stale: it still names the versions this job
1920
+ // just installed. hq-edge refreshes its own copy before it settles the
1921
+ // job, so one cheap read of `/report` is enough to bring the list (and
1922
+ // the badge) down to zero. Without it the badge kept counting updates
1923
+ // that no longer exist until the 60 s idle poll came round.
1924
+ void this.refresh()
1779
1925
  }
1780
1926
  this.emit()
1781
1927
  this.schedulePoll()