@linxin666/dsh-client-ui-task-board 0.1.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.
Files changed (50) hide show
  1. package/LICENSE +29 -0
  2. package/README.md +211 -0
  3. package/cordis.patch.yml +12 -0
  4. package/lib/client.js +2592 -0
  5. package/lib/index.js +54 -0
  6. package/lib/invariant.js +6 -0
  7. package/lib/types/client/PluginSettingsCard.d.ts +77 -0
  8. package/lib/types/client/TaskBoardSettingsCard.d.ts +51 -0
  9. package/lib/types/client/board/ConfirmDialog.d.ts +12 -0
  10. package/lib/types/client/board/NewTaskModal.d.ts +6 -0
  11. package/lib/types/client/board/TaskBoard.d.ts +5 -0
  12. package/lib/types/client/board/TaskCard.d.ts +12 -0
  13. package/lib/types/client/board/TaskDetail.d.ts +7 -0
  14. package/lib/types/client/board-mount.d.ts +10 -0
  15. package/lib/types/client/index.d.ts +43 -0
  16. package/lib/types/client/locales.d.ts +105 -0
  17. package/lib/types/client/settings-form.d.ts +118 -0
  18. package/lib/types/client/sidebar-entry.d.ts +25 -0
  19. package/lib/types/core/controller.d.ts +129 -0
  20. package/lib/types/core/execution.d.ts +136 -0
  21. package/lib/types/core/schedule.d.ts +40 -0
  22. package/lib/types/core/scheduler.d.ts +49 -0
  23. package/lib/types/core/store.d.ts +36 -0
  24. package/lib/types/core/tasks.d.ts +111 -0
  25. package/lib/types/index.d.ts +44 -0
  26. package/lib/types/invariant.d.ts +3 -0
  27. package/package.json +85 -0
  28. package/src/client/PluginSettingsCard.tsx +207 -0
  29. package/src/client/TaskBoardSettingsCard.tsx +124 -0
  30. package/src/client/board/ConfirmDialog.tsx +40 -0
  31. package/src/client/board/NewTaskModal.tsx +82 -0
  32. package/src/client/board/TaskBoard.tsx +107 -0
  33. package/src/client/board/TaskCard.tsx +62 -0
  34. package/src/client/board/TaskDetail.tsx +286 -0
  35. package/src/client/board-mount.tsx +74 -0
  36. package/src/client/board.module.css +849 -0
  37. package/src/client/css-modules.d.ts +5 -0
  38. package/src/client/index.ts +167 -0
  39. package/src/client/locales.ts +211 -0
  40. package/src/client/settings-card.module.css +261 -0
  41. package/src/client/settings-form.ts +294 -0
  42. package/src/client/sidebar-entry.ts +118 -0
  43. package/src/core/controller.ts +379 -0
  44. package/src/core/execution.ts +259 -0
  45. package/src/core/schedule.ts +140 -0
  46. package/src/core/scheduler.ts +104 -0
  47. package/src/core/store.ts +183 -0
  48. package/src/core/tasks.ts +204 -0
  49. package/src/index.ts +94 -0
  50. package/src/invariant.ts +4 -0
@@ -0,0 +1,849 @@
1
+ /**
2
+ * Task-board styles. Scoped by the plugin's own data attributes so nothing
3
+ * leaks into the rest of the GUI; colors ride the dsh --dsw-* tokens so the
4
+ * board follows the active theme (light/dark and skins).
5
+ */
6
+
7
+ /* --- center-column takeover (global rules, attribute-scoped) ----------------- */
8
+
9
+ [data-pane='conversation'] {
10
+ position: relative;
11
+ }
12
+
13
+ /* The board container rides inside the conversation grid item as an extra
14
+ trailing child; hidden unless the board is active. */
15
+ [data-dsh-taskboard-view] {
16
+ position: absolute;
17
+ inset: 0;
18
+ display: none;
19
+ z-index: 5;
20
+ }
21
+
22
+ html[data-dsh-taskboard-active] [data-dsh-taskboard-view] {
23
+ display: block;
24
+ }
25
+
26
+ /* While the board is active, the conversation content underneath is hidden
27
+ (it stays mounted and stateful). */
28
+ html[data-dsh-taskboard-active] [data-pane='conversation'] > :not([data-dsh-taskboard-view]) {
29
+ display: none;
30
+ }
31
+
32
+ /* --- sidebar entry row ------------------------------------------------------- */
33
+
34
+ .entry {
35
+ display: flex;
36
+ align-items: center;
37
+ gap: 8px;
38
+ width: 100%;
39
+ height: 32px;
40
+ padding: 0 12px;
41
+ background: transparent;
42
+ border: none;
43
+ border-radius: 8px;
44
+ color: var(--dsw-alias-label-secondary);
45
+ cursor: pointer;
46
+ font-size: 13px;
47
+ white-space: nowrap;
48
+ }
49
+
50
+ .entry:hover {
51
+ background: var(--dsw-specific-sidebar-nav-item-hover);
52
+ color: var(--dsw-alias-label-primary);
53
+ }
54
+
55
+ .entry[data-active] {
56
+ background: var(--dsw-specific-sidebar-nav-item-active);
57
+ color: var(--dsw-alias-label-primary);
58
+ font-weight: 600;
59
+ }
60
+
61
+ .entryIcon {
62
+ display: inline-flex;
63
+ align-items: center;
64
+ justify-content: center;
65
+ flex: none;
66
+ }
67
+
68
+ .entryLabel {
69
+ overflow: hidden;
70
+ text-overflow: ellipsis;
71
+ }
72
+
73
+ /* Collapsed rail: icon-only, centered, matching the shell's 56px rail. */
74
+ [data-dsh-frame][data-sidebar-collapsed] .entry {
75
+ justify-content: center;
76
+ padding: 0;
77
+ width: 100%;
78
+ }
79
+
80
+ [data-dsh-frame][data-sidebar-collapsed] .entryLabel {
81
+ display: none;
82
+ }
83
+
84
+ /* --- board frame -------------------------------------------------------------- */
85
+
86
+ .board {
87
+ display: flex;
88
+ flex-direction: column;
89
+ height: 100%;
90
+ min-width: 0;
91
+ min-height: 0;
92
+ padding: 14px 16px 16px;
93
+ gap: 12px;
94
+ background: var(--dsw-alias-bg-base);
95
+ color: var(--dsw-alias-label-primary);
96
+ font-family: var(--dsw-font-family);
97
+ }
98
+
99
+ .boardHeader {
100
+ display: flex;
101
+ align-items: center;
102
+ gap: 10px;
103
+ flex: none;
104
+ }
105
+
106
+ .boardTitle {
107
+ margin: 0;
108
+ font-size: 16px;
109
+ font-weight: 700;
110
+ color: var(--dsw-alias-label-primary);
111
+ white-space: nowrap;
112
+ }
113
+
114
+ .search {
115
+ flex: 0 1 260px;
116
+ min-width: 120px;
117
+ padding: 6px 10px;
118
+ font-size: 13px;
119
+ color: var(--dsw-alias-label-primary);
120
+ background: var(--dsw-specific-input-major);
121
+ border: 1px solid var(--dsw-alias-border-l2);
122
+ border-radius: 8px;
123
+ outline: none;
124
+ }
125
+
126
+ .search::placeholder {
127
+ color: var(--dsw-alias-label-tertiary);
128
+ }
129
+
130
+ /* --- columns ------------------------------------------------------------------ */
131
+
132
+ .columns {
133
+ display: grid;
134
+ grid-template-columns: repeat(5, minmax(0, 1fr));
135
+ gap: 12px;
136
+ flex: 1;
137
+ min-height: 0;
138
+ }
139
+
140
+ .column {
141
+ display: flex;
142
+ flex-direction: column;
143
+ min-height: 0;
144
+ background: var(--dsw-alias-bg-layer-2);
145
+ border: 1px solid var(--dsw-alias-border-l1);
146
+ border-radius: 12px;
147
+ overflow: hidden;
148
+ }
149
+
150
+ .columnHeader {
151
+ display: flex;
152
+ align-items: center;
153
+ gap: 6px;
154
+ padding: 10px 12px;
155
+ flex: none;
156
+ }
157
+
158
+ .columnTitle {
159
+ margin: 0;
160
+ flex: 1;
161
+ font-size: 13px;
162
+ font-weight: 700;
163
+ color: var(--dsw-alias-label-primary);
164
+ overflow: hidden;
165
+ text-overflow: ellipsis;
166
+ white-space: nowrap;
167
+ }
168
+
169
+ .columnCount {
170
+ flex: none;
171
+ min-width: 0;
172
+ font-size: 12px;
173
+ color: var(--dsw-alias-label-tertiary);
174
+ background: var(--dsw-alias-interactive-bg-hover);
175
+ border-radius: 999px;
176
+ padding: 1px 8px;
177
+ }
178
+
179
+ .statusDot {
180
+ width: 8px;
181
+ height: 8px;
182
+ border-radius: 50%;
183
+ flex: none;
184
+ }
185
+
186
+ .statusDot[data-status='backlog'] { background: var(--dsw-alias-label-tertiary); }
187
+ .statusDot[data-status='todo'] { background: var(--dsw-alias-state-business-primary); }
188
+ .statusDot[data-status='running'] { background: var(--dsw-alias-state-warn-primary); }
189
+ .statusDot[data-status='done'] { background: var(--dsw-alias-state-success-primary); }
190
+ .statusDot[data-status='failed'] { background: var(--dsw-alias-state-error-primary); }
191
+
192
+ .cards {
193
+ display: flex;
194
+ flex-direction: column;
195
+ gap: 8px;
196
+ padding: 2px 8px 10px;
197
+ overflow-y: auto;
198
+ flex: 1;
199
+ min-height: 0;
200
+ }
201
+
202
+ .columnEmpty {
203
+ padding: 24px 8px;
204
+ text-align: center;
205
+ font-size: 12px;
206
+ color: var(--dsw-alias-label-tertiary);
207
+ }
208
+
209
+ /* --- cards -------------------------------------------------------------------- */
210
+
211
+ .card {
212
+ display: flex;
213
+ flex-direction: column;
214
+ gap: 6px;
215
+ padding: 10px 12px;
216
+ text-align: left;
217
+ background: var(--dsw-alias-bg-base);
218
+ border: 1px solid var(--dsw-alias-border-l2);
219
+ border-radius: 10px;
220
+ cursor: pointer;
221
+ color: var(--dsw-alias-label-primary);
222
+ font-family: inherit;
223
+ transition: box-shadow 120ms ease, border-color 120ms ease, transform 120ms ease;
224
+ }
225
+
226
+ .card:hover {
227
+ box-shadow: var(--dsw-shadow-lv2);
228
+ border-color: var(--dsw-alias-border-l3);
229
+ transform: translateY(-1px);
230
+ }
231
+
232
+ .card[data-status='running'] {
233
+ border-color: var(--dsw-alias-state-warn-primary);
234
+ }
235
+
236
+ .cardTitle {
237
+ font-size: 13px;
238
+ font-weight: 600;
239
+ line-height: 1.35;
240
+ overflow: hidden;
241
+ display: -webkit-box;
242
+ -webkit-line-clamp: 2;
243
+ -webkit-box-orient: vertical;
244
+ }
245
+
246
+ .cardExcerpt {
247
+ font-size: 12px;
248
+ line-height: 1.4;
249
+ color: var(--dsw-alias-label-secondary);
250
+ overflow: hidden;
251
+ display: -webkit-box;
252
+ -webkit-line-clamp: 2;
253
+ -webkit-box-orient: vertical;
254
+ }
255
+
256
+ .cardMeta {
257
+ display: flex;
258
+ align-items: center;
259
+ gap: 8px;
260
+ font-size: 11px;
261
+ color: var(--dsw-alias-label-tertiary);
262
+ }
263
+
264
+ .cardTime {
265
+ flex: 1;
266
+ overflow: hidden;
267
+ text-overflow: ellipsis;
268
+ white-space: nowrap;
269
+ }
270
+
271
+ .cardSchedule {
272
+ flex: none;
273
+ min-width: 0;
274
+ font-size: 12px;
275
+ line-height: 1;
276
+ white-space: nowrap;
277
+ padding: 2px 6px;
278
+ border-radius: 999px;
279
+ color: var(--dsw-alias-label-secondary);
280
+ background: var(--dsw-alias-interactive-bg-hover);
281
+ }
282
+
283
+ .cardRun {
284
+ flex: none;
285
+ }
286
+
287
+ .cardRun[data-result='failed'] {
288
+ color: var(--dsw-alias-state-error-primary);
289
+ }
290
+
291
+ .cardRun[data-result='succeeded'] {
292
+ color: var(--dsw-alias-state-success-primary);
293
+ }
294
+
295
+ .cardSession {
296
+ flex: none;
297
+ color: var(--dsw-alias-state-business-primary);
298
+ }
299
+
300
+ .cardRunningLabel {
301
+ font-size: 11px;
302
+ color: var(--dsw-alias-state-warn-primary);
303
+ }
304
+
305
+ .cardSpinner {
306
+ width: 10px;
307
+ height: 10px;
308
+ flex: none;
309
+ border: 2px solid var(--dsw-alias-state-warn-primary);
310
+ border-top-color: transparent;
311
+ border-radius: 50%;
312
+ animation: dshTbSpin 800ms linear infinite;
313
+ }
314
+
315
+ @keyframes dshTbSpin {
316
+ to { transform: rotate(360deg); }
317
+ }
318
+
319
+ /* --- buttons ------------------------------------------------------------------ */
320
+
321
+ .primaryButton {
322
+ padding: 6px 14px;
323
+ font-size: 13px;
324
+ font-weight: 600;
325
+ color: var(--dsw-alias-label-primary-foreground);
326
+ background: var(--dsw-alias-button-info-fill);
327
+ border: none;
328
+ border-radius: 8px;
329
+ cursor: pointer;
330
+ white-space: nowrap;
331
+ }
332
+
333
+ .primaryButton:hover:not(:disabled) {
334
+ background: var(--dsw-alias-button-info-hover);
335
+ }
336
+
337
+ .primaryButton:disabled {
338
+ opacity: 0.5;
339
+ cursor: default;
340
+ }
341
+
342
+ .ghostButton {
343
+ padding: 5px 12px;
344
+ font-size: 12px;
345
+ color: var(--dsw-alias-label-primary);
346
+ background: transparent;
347
+ border: 1px solid var(--dsw-alias-border-l2);
348
+ border-radius: 8px;
349
+ cursor: pointer;
350
+ white-space: nowrap;
351
+ }
352
+
353
+ .ghostButton:hover:not(:disabled) {
354
+ background: var(--dsw-alias-interactive-bg-hover);
355
+ }
356
+
357
+ .ghostButton:disabled {
358
+ opacity: 0.45;
359
+ cursor: default;
360
+ }
361
+
362
+ .dangerButton {
363
+ padding: 6px 14px;
364
+ font-size: 13px;
365
+ font-weight: 600;
366
+ color: #fff;
367
+ background: var(--dsw-alias-state-error-primary);
368
+ border: none;
369
+ border-radius: 8px;
370
+ cursor: pointer;
371
+ white-space: nowrap;
372
+ }
373
+
374
+ .dangerButton:hover:not(:disabled) {
375
+ filter: brightness(1.08);
376
+ }
377
+
378
+ .dangerButton:active:not(:disabled) {
379
+ filter: brightness(0.94);
380
+ }
381
+
382
+ .dangerButton:disabled {
383
+ opacity: 0.5;
384
+ cursor: default;
385
+ }
386
+
387
+ .iconButton {
388
+ display: inline-flex;
389
+ align-items: center;
390
+ justify-content: center;
391
+ width: 26px;
392
+ height: 26px;
393
+ padding: 0;
394
+ background: transparent;
395
+ border: none;
396
+ border-radius: 6px;
397
+ color: var(--dsw-alias-label-secondary);
398
+ cursor: pointer;
399
+ font-size: 13px;
400
+ }
401
+
402
+ .iconButton:hover {
403
+ background: var(--dsw-alias-interactive-bg-hover);
404
+ color: var(--dsw-alias-label-primary);
405
+ }
406
+
407
+ .linkButton {
408
+ padding: 0;
409
+ font-size: 12px;
410
+ color: var(--dsw-alias-state-business-primary);
411
+ background: none;
412
+ border: none;
413
+ cursor: pointer;
414
+ white-space: nowrap;
415
+ }
416
+
417
+ .linkButton:hover {
418
+ text-decoration: underline;
419
+ }
420
+
421
+ /* --- modals ------------------------------------------------------------------- */
422
+
423
+ .modalBackdrop {
424
+ position: fixed;
425
+ inset: 0;
426
+ z-index: 40;
427
+ display: flex;
428
+ align-items: center;
429
+ justify-content: center;
430
+ background: var(--dsw-alias-bg-mask-1);
431
+ }
432
+
433
+ .modal {
434
+ display: flex;
435
+ flex-direction: column;
436
+ gap: 12px;
437
+ width: min(520px, calc(100vw - 48px));
438
+ max-height: calc(100vh - 96px);
439
+ overflow-y: auto;
440
+ padding: 18px;
441
+ background: var(--dsw-alias-bg-base);
442
+ border: 1px solid var(--dsw-alias-border-l2);
443
+ border-radius: 14px;
444
+ box-shadow: var(--dsw-shadow-lv3);
445
+ color: var(--dsw-alias-label-primary);
446
+ }
447
+
448
+ .modalTitle {
449
+ margin: 0;
450
+ font-size: 15px;
451
+ font-weight: 700;
452
+ }
453
+
454
+ .confirmMessage {
455
+ margin: 0;
456
+ font-size: 13px;
457
+ line-height: 1.5;
458
+ color: var(--dsw-alias-label-secondary);
459
+ white-space: pre-wrap;
460
+ overflow-wrap: anywhere;
461
+ }
462
+
463
+ .modalFooter {
464
+ display: flex;
465
+ justify-content: flex-end;
466
+ gap: 10px;
467
+ margin-top: 4px;
468
+ }
469
+
470
+ .field {
471
+ display: flex;
472
+ flex-direction: column;
473
+ gap: 5px;
474
+ }
475
+
476
+ .fieldLabel {
477
+ font-size: 12px;
478
+ font-weight: 600;
479
+ color: var(--dsw-alias-label-secondary);
480
+ }
481
+
482
+ .input {
483
+ padding: 7px 10px;
484
+ font-size: 13px;
485
+ color: var(--dsw-alias-label-primary);
486
+ background: var(--dsw-specific-input-major);
487
+ border: 1px solid var(--dsw-alias-border-l2);
488
+ border-radius: 8px;
489
+ outline: none;
490
+ resize: vertical;
491
+ font-family: inherit;
492
+ }
493
+
494
+ .input:focus {
495
+ border-color: var(--dsw-alias-state-business-primary);
496
+ }
497
+
498
+ .input::placeholder {
499
+ color: var(--dsw-alias-label-tertiary);
500
+ }
501
+
502
+ .formError {
503
+ margin: 0;
504
+ font-size: 12px;
505
+ color: var(--dsw-alias-state-error-primary);
506
+ }
507
+
508
+ /* --- detail ------------------------------------------------------------------- */
509
+
510
+ .detail {
511
+ display: flex;
512
+ flex-direction: column;
513
+ width: min(640px, calc(100vw - 48px));
514
+ max-height: calc(100vh - 80px);
515
+ background: var(--dsw-alias-bg-base);
516
+ border: 1px solid var(--dsw-alias-border-l2);
517
+ border-radius: 14px;
518
+ box-shadow: var(--dsw-shadow-lv3);
519
+ color: var(--dsw-alias-label-primary);
520
+ overflow: hidden;
521
+ }
522
+
523
+ .detailHeader {
524
+ display: flex;
525
+ align-items: center;
526
+ gap: 10px;
527
+ padding: 14px 18px;
528
+ border-bottom: 1px solid var(--dsw-alias-separator-primary);
529
+ flex: none;
530
+ }
531
+
532
+ .detailTitle {
533
+ margin: 0;
534
+ flex: 1;
535
+ font-size: 15px;
536
+ font-weight: 700;
537
+ overflow-wrap: anywhere;
538
+ }
539
+
540
+ .statusBadge {
541
+ flex: none;
542
+ padding: 2px 10px;
543
+ font-size: 12px;
544
+ border-radius: 999px;
545
+ border: 1px solid var(--dsw-alias-border-l2);
546
+ color: var(--dsw-alias-label-secondary);
547
+ }
548
+
549
+ .statusBadge[data-status='running'] {
550
+ color: var(--dsw-alias-state-warn-primary);
551
+ border-color: var(--dsw-alias-state-warn-primary);
552
+ }
553
+
554
+ .statusBadge[data-status='done'] {
555
+ color: var(--dsw-alias-state-success-primary);
556
+ border-color: var(--dsw-alias-state-success-primary);
557
+ }
558
+
559
+ .statusBadge[data-status='failed'] {
560
+ color: var(--dsw-alias-state-error-primary);
561
+ border-color: var(--dsw-alias-state-error-primary);
562
+ }
563
+
564
+ .detailBody {
565
+ padding: 14px 18px;
566
+ overflow-y: auto;
567
+ display: flex;
568
+ flex-direction: column;
569
+ gap: 16px;
570
+ flex: 1;
571
+ }
572
+
573
+ .detailSection {
574
+ display: flex;
575
+ flex-direction: column;
576
+ gap: 6px;
577
+ }
578
+
579
+ .detailSection h4 {
580
+ margin: 0;
581
+ font-size: 12px;
582
+ font-weight: 700;
583
+ color: var(--dsw-alias-label-tertiary);
584
+ text-transform: none;
585
+ }
586
+
587
+ .detailText {
588
+ margin: 0;
589
+ font-size: 13px;
590
+ line-height: 1.55;
591
+ color: var(--dsw-alias-label-primary);
592
+ white-space: pre-wrap;
593
+ overflow-wrap: anywhere;
594
+ }
595
+
596
+ /* --- scheduled runs ---------------------------------------------------------- */
597
+
598
+ .scheduleToggle {
599
+ display: flex;
600
+ align-items: center;
601
+ gap: 8px;
602
+ font-size: 13px;
603
+ color: var(--dsw-alias-label-primary);
604
+ cursor: pointer;
605
+ user-select: none;
606
+ }
607
+
608
+ .scheduleToggle input {
609
+ accent-color: var(--dsw-alias-state-business-primary);
610
+ }
611
+
612
+ .scheduleRow {
613
+ display: flex;
614
+ align-items: center;
615
+ gap: 8px;
616
+ }
617
+
618
+ .scheduleInput {
619
+ flex: 1;
620
+ min-width: 0;
621
+ font-family: var(--dsw-font-markdown-code-block-small);
622
+ font-size: 12.5px;
623
+ }
624
+
625
+ .scheduleInputInvalid {
626
+ border-color: var(--dsw-alias-state-error-primary);
627
+ }
628
+
629
+ .scheduleInputInvalid:focus {
630
+ border-color: var(--dsw-alias-state-error-primary);
631
+ }
632
+
633
+ .schedulePreset {
634
+ flex: none;
635
+ padding: 7px 8px;
636
+ font-size: 12.5px;
637
+ color: var(--dsw-alias-label-primary);
638
+ background: var(--dsw-specific-input-major);
639
+ border: 1px solid var(--dsw-alias-border-l2);
640
+ border-radius: 8px;
641
+ outline: none;
642
+ }
643
+
644
+ .scheduleMeta {
645
+ margin: 0;
646
+ font-size: 12px;
647
+ color: var(--dsw-alias-label-secondary);
648
+ overflow-wrap: anywhere;
649
+ }
650
+
651
+ .promptBlock {
652
+ margin: 0;
653
+ padding: 10px 12px;
654
+ font-size: 12.5px;
655
+ line-height: 1.5;
656
+ font-family: var(--dsw-font-markdown-code-block-small);
657
+ color: var(--dsw-alias-label-primary);
658
+ background: var(--dsw-alias-markdown-code-block);
659
+ border: 1px solid var(--dsw-alias-border-l1);
660
+ border-radius: 8px;
661
+ white-space: pre-wrap;
662
+ overflow-wrap: anywhere;
663
+ max-height: 240px;
664
+ overflow-y: auto;
665
+ }
666
+
667
+ .executionList {
668
+ list-style: none;
669
+ margin: 0;
670
+ padding: 0;
671
+ display: flex;
672
+ flex-direction: column;
673
+ gap: 8px;
674
+ }
675
+
676
+ .executionRow {
677
+ display: flex;
678
+ align-items: center;
679
+ gap: 10px;
680
+ padding: 8px 10px;
681
+ border: 1px solid var(--dsw-alias-border-l1);
682
+ border-radius: 8px;
683
+ flex-wrap: wrap;
684
+ }
685
+
686
+ .executionBadge {
687
+ flex: none;
688
+ padding: 1px 8px;
689
+ font-size: 11px;
690
+ font-weight: 600;
691
+ border-radius: 999px;
692
+ color: var(--dsw-alias-state-warn-primary);
693
+ background: var(--dsw-alias-state-warn-secondary);
694
+ }
695
+
696
+ .executionBadge[data-result='succeeded'] {
697
+ color: var(--dsw-alias-state-success-primary);
698
+ background: transparent;
699
+ }
700
+
701
+ .executionBadge[data-result='failed'] {
702
+ color: var(--dsw-alias-state-error-primary);
703
+ background: transparent;
704
+ }
705
+
706
+ .executionBadge[data-result='cancelled'] {
707
+ color: var(--dsw-alias-label-tertiary);
708
+ background: transparent;
709
+ }
710
+
711
+ .executionTimes {
712
+ font-size: 12px;
713
+ color: var(--dsw-alias-label-secondary);
714
+ }
715
+
716
+ .executionError {
717
+ width: 100%;
718
+ font-size: 12px;
719
+ color: var(--dsw-alias-state-error-primary);
720
+ overflow-wrap: anywhere;
721
+ }
722
+
723
+ .moveRow {
724
+ display: flex;
725
+ gap: 8px;
726
+ flex-wrap: wrap;
727
+ }
728
+
729
+ .detailFooter {
730
+ display: flex;
731
+ align-items: center;
732
+ gap: 10px;
733
+ padding: 12px 18px;
734
+ border-top: 1px solid var(--dsw-alias-separator-primary);
735
+ flex: none;
736
+ }
737
+
738
+ .detailMeta {
739
+ margin-left: auto;
740
+ font-size: 11px;
741
+ color: var(--dsw-alias-label-tertiary);
742
+ }
743
+
744
+ /* -----------------------------------------------------------------------------
745
+ * Polish pass: complete interaction states (hover / active / focus-visible /
746
+ * disabled), unified 120ms motion, and reduced-motion fallback.
747
+ * Colors use the --dsw-alias-* design tokens, which adapt to light/dark/skins
748
+ * automatically, so every rule below is dark-safe by construction.
749
+ * --------------------------------------------------------------------------- */
750
+
751
+ /* --- one themed focus-visible ring (approx 2px, 2px offset) --- */
752
+ .entry:focus-visible,
753
+ .card:focus-visible,
754
+ .primaryButton:focus-visible,
755
+ .ghostButton:focus-visible,
756
+ .dangerButton:focus-visible,
757
+ .iconButton:focus-visible,
758
+ .linkButton:focus-visible,
759
+ .search:focus-visible,
760
+ .input:focus-visible,
761
+ .schedulePreset:focus-visible,
762
+ .scheduleToggle input:focus-visible {
763
+ outline: 2px solid var(--dsw-alias-state-business-primary);
764
+ outline-offset: 2px;
765
+ }
766
+
767
+ /* --- unified 120ms motion across the board's interactive controls --- */
768
+ .entry,
769
+ .primaryButton,
770
+ .ghostButton,
771
+ .dangerButton,
772
+ .iconButton,
773
+ .linkButton,
774
+ .search,
775
+ .input,
776
+ .schedulePreset,
777
+ .scheduleToggle input {
778
+ transition: background-color 120ms ease, color 120ms ease, border-color 120ms ease, outline-color 120ms ease, box-shadow 120ms ease, transform 120ms ease;
779
+ }
780
+
781
+ /* --- press (active) feedback --- */
782
+ .card:active {
783
+ box-shadow: var(--dsw-shadow-lv1);
784
+ transform: translateY(0);
785
+ }
786
+
787
+ .entry:active,
788
+ .primaryButton:active:not(:disabled),
789
+ .ghostButton:active:not(:disabled),
790
+ .dangerButton:active:not(:disabled),
791
+ .iconButton:active:not(:disabled),
792
+ .linkButton:active:not(:disabled) {
793
+ transform: translateY(1px);
794
+ }
795
+
796
+ /* --- entry row (sidebar) states --- */
797
+ .entry[data-active]:hover {
798
+ background: var(--dsw-specific-sidebar-nav-item-active);
799
+ }
800
+
801
+ /* --- guarded hover for the remaining icon/link buttons (defensive: never
802
+ disabled today, but a disabled element must not react to hover) --- */
803
+ .iconButton:hover:not(:disabled) {
804
+ background: var(--dsw-alias-interactive-bg-hover);
805
+ color: var(--dsw-alias-label-primary);
806
+ }
807
+
808
+ .linkButton:hover:not(:disabled) {
809
+ text-decoration: underline;
810
+ }
811
+
812
+ .iconButton:disabled,
813
+ .linkButton:disabled {
814
+ opacity: 0.45;
815
+ cursor: default;
816
+ }
817
+
818
+ /* --- search box focus (border tint + themed ring above) --- */
819
+ .search:focus,
820
+ .schedulePreset:focus {
821
+ border-color: var(--dsw-alias-state-business-primary);
822
+ }
823
+
824
+ /* --- schedule toggle checkbox focus already covered by the shared ring;
825
+ keep the accent-color and size stable --- */
826
+ .scheduleToggle input {
827
+ margin: 0;
828
+ }
829
+
830
+ /* --- reduced motion: strip transitions, stop the decorative spinner --- */
831
+ @media (prefers-reduced-motion: reduce) {
832
+ .entry,
833
+ .card,
834
+ .primaryButton,
835
+ .ghostButton,
836
+ .dangerButton,
837
+ .iconButton,
838
+ .linkButton,
839
+ .search,
840
+ .input,
841
+ .schedulePreset,
842
+ .scheduleToggle input {
843
+ transition: none;
844
+ }
845
+
846
+ .cardSpinner {
847
+ animation: none;
848
+ }
849
+ }