@scenetest/dashboard 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/README.md +87 -35
  2. package/dist/app.d.ts +10 -7
  3. package/dist/app.d.ts.map +1 -1
  4. package/dist/app.js +12 -130
  5. package/dist/app.js.map +1 -1
  6. package/dist/collections/index.d.ts +19 -0
  7. package/dist/collections/index.d.ts.map +1 -0
  8. package/dist/collections/index.js +18 -0
  9. package/dist/collections/index.js.map +1 -0
  10. package/dist/collections/options.d.ts +29 -0
  11. package/dist/collections/options.d.ts.map +1 -0
  12. package/dist/collections/options.js +68 -0
  13. package/dist/collections/options.js.map +1 -0
  14. package/dist/collections/projections.d.ts +117 -0
  15. package/dist/collections/projections.d.ts.map +1 -0
  16. package/dist/collections/projections.js +228 -0
  17. package/dist/collections/projections.js.map +1 -0
  18. package/dist/collections/source.d.ts +32 -0
  19. package/dist/collections/source.d.ts.map +1 -0
  20. package/dist/collections/source.js +84 -0
  21. package/dist/collections/source.js.map +1 -0
  22. package/dist/collections/types.d.ts +87 -0
  23. package/dist/collections/types.d.ts.map +1 -0
  24. package/dist/collections/types.js +2 -0
  25. package/dist/collections/types.js.map +1 -0
  26. package/dist/dashboard.d.ts +20 -0
  27. package/dist/dashboard.d.ts.map +1 -0
  28. package/dist/dashboard.js +114 -0
  29. package/dist/dashboard.js.map +1 -0
  30. package/dist/dev-transport.d.ts +3 -3
  31. package/dist/dev-transport.d.ts.map +1 -1
  32. package/dist/dev-transport.js +3 -7
  33. package/dist/dev-transport.js.map +1 -1
  34. package/dist/index.d.ts +6 -3
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +4 -2
  37. package/dist/index.js.map +1 -1
  38. package/dist/runner.d.ts +8 -0
  39. package/dist/runner.d.ts.map +1 -0
  40. package/dist/runner.js +249 -0
  41. package/dist/runner.js.map +1 -0
  42. package/dist/select-helpers.d.ts +50 -0
  43. package/dist/select-helpers.d.ts.map +1 -0
  44. package/dist/select-helpers.js +48 -0
  45. package/dist/select-helpers.js.map +1 -0
  46. package/dist/select-runner.d.ts +55 -0
  47. package/dist/select-runner.d.ts.map +1 -0
  48. package/dist/select-runner.js +108 -0
  49. package/dist/select-runner.js.map +1 -0
  50. package/dist/select-waterfall.d.ts +12 -0
  51. package/dist/select-waterfall.d.ts.map +1 -0
  52. package/dist/select-waterfall.js +79 -0
  53. package/dist/select-waterfall.js.map +1 -0
  54. package/dist/style.css +878 -0
  55. package/dist/types.d.ts +6 -20
  56. package/dist/types.d.ts.map +1 -1
  57. package/dist/use-live-query.d.ts +28 -0
  58. package/dist/use-live-query.d.ts.map +1 -0
  59. package/dist/use-live-query.js +25 -0
  60. package/dist/use-live-query.js.map +1 -0
  61. package/dist/use-run-slice.d.ts +25 -0
  62. package/dist/use-run-slice.d.ts.map +1 -0
  63. package/dist/use-run-slice.js +48 -0
  64. package/dist/use-run-slice.js.map +1 -0
  65. package/package.json +10 -5
  66. package/dist/mount.d.ts +0 -14
  67. package/dist/mount.d.ts.map +0 -1
  68. package/dist/mount.js +0 -50
  69. package/dist/mount.js.map +0 -1
  70. package/dist/store.d.ts +0 -19
  71. package/dist/store.d.ts.map +0 -1
  72. package/dist/store.js +0 -164
  73. package/dist/store.js.map +0 -1
  74. package/dist/styles.d.ts +0 -10
  75. package/dist/styles.d.ts.map +0 -1
  76. package/dist/styles.js +0 -152
  77. package/dist/styles.js.map +0 -1
package/dist/style.css ADDED
@@ -0,0 +1,878 @@
1
+ /*
2
+ * Dashboard stylesheet — the whole app (nav · Home · Runner · Waterfall),
3
+ * scoped under the single root class `.scenetest-dashboard`. There is no shadow
4
+ * root anymore: this ships as a normal stylesheet the host imports
5
+ * (`import '@scenetest/dashboard/style.css'`), the way one would style any app.
6
+ * The Waterfall view's widget styles live under `.waterfall-host` so their
7
+ * bare-element rules (header, h1, button, …) stay confined to that subtree
8
+ * instead of leaking into the chrome or the host page.
9
+ *
10
+ * Theming surface: a host may set `--st-bg`, `--st-accent`, `--st-font`,
11
+ * `--st-font-size` on the root element (inline — `<Dashboard theme={…} />`
12
+ * does this — or via CSS). Everything else derives from them or from a fixed
13
+ * terminal palette. These four are versioned with the component, like the wire
14
+ * protocol.
15
+ */
16
+ .scenetest-dashboard {
17
+ /* ── Internal palette (derived; not a public surface) ── */
18
+ --bg: var(--st-bg, #0f1117);
19
+ --bg2: #1a1d27;
20
+ --bg3: #252833;
21
+ --border: #2e3140;
22
+ --text: #e1e4ed;
23
+ --text2: #8b8fa3;
24
+ --text3: #5a5e72;
25
+ --green: #22c55e;
26
+ --red: #ef4444;
27
+ --amber: #f59e0b;
28
+ --blue: #3b82f6;
29
+
30
+ display: flex;
31
+ flex-direction: column;
32
+ height: 100%;
33
+ overflow: hidden;
34
+ font-family: var(--st-font, 'SF Mono', 'Cascadia Code', 'Fira Code', ui-monospace, monospace);
35
+ font-size: var(--st-font-size, 13px);
36
+ background: var(--bg);
37
+ color: var(--text);
38
+
39
+ *,
40
+ *::before,
41
+ *::after {
42
+ margin: 0;
43
+ padding: 0;
44
+ box-sizing: border-box;
45
+ }
46
+
47
+ /* ── Nav ──────────────────────────────────────────────── */
48
+ .dashboard-nav {
49
+ display: flex;
50
+ align-items: center;
51
+ gap: 16px;
52
+ padding: 10px 16px;
53
+ background: var(--bg2);
54
+ border-bottom: 1px solid var(--border);
55
+ flex-shrink: 0;
56
+ }
57
+ .dashboard-nav h1 {
58
+ font-size: 14px;
59
+ font-weight: 600;
60
+ display: flex;
61
+ align-items: center;
62
+ gap: 8px;
63
+ }
64
+ .logo {
65
+ width: 26px;
66
+ height: 26px;
67
+ border-radius: 6px;
68
+ background: rgba(80, 70, 229, 0.15);
69
+ box-shadow: inset 0 1px 4px rgba(80, 70, 229, 0.3);
70
+ display: inline-flex;
71
+ align-items: center;
72
+ justify-content: center;
73
+ font-size: 13px;
74
+ }
75
+ .tabs {
76
+ display: flex;
77
+ gap: 4px;
78
+ }
79
+ .tab {
80
+ padding: 5px 12px;
81
+ background: transparent;
82
+ color: var(--text2);
83
+ border: 1px solid var(--border);
84
+ border-radius: 4px;
85
+ font: inherit;
86
+ cursor: pointer;
87
+ font-size: 12px;
88
+ display: inline-block;
89
+ text-decoration: none;
90
+ }
91
+ .tab:hover {
92
+ color: var(--text);
93
+ border-color: var(--text2);
94
+ }
95
+ .tab.active {
96
+ background: var(--bg3);
97
+ color: var(--text);
98
+ border-color: var(--text2);
99
+ }
100
+
101
+ .view {
102
+ flex: 1;
103
+ min-height: 0;
104
+ display: flex;
105
+ flex-direction: column;
106
+ }
107
+
108
+ /* ── Runner ───────────────────────────────────────────── */
109
+ .runner {
110
+ display: flex;
111
+ flex-direction: column;
112
+ flex: 1;
113
+ min-height: 0;
114
+ }
115
+ .runner-bar {
116
+ display: flex;
117
+ align-items: center;
118
+ gap: 16px;
119
+ padding: 8px 16px;
120
+ background: var(--bg2);
121
+ border-bottom: 1px solid var(--border);
122
+ flex-shrink: 0;
123
+ }
124
+ .run-picker {
125
+ display: flex;
126
+ align-items: center;
127
+ gap: 8px;
128
+ font-size: 12px;
129
+ color: var(--text2);
130
+ }
131
+ .run-picker select {
132
+ background: var(--bg3);
133
+ color: var(--text);
134
+ border: 1px solid var(--border);
135
+ border-radius: 4px;
136
+ padding: 4px 8px;
137
+ font: inherit;
138
+ font-size: 12px;
139
+ min-width: 200px;
140
+ }
141
+ .conn {
142
+ width: 8px;
143
+ height: 8px;
144
+ border-radius: 50%;
145
+ background: var(--text3);
146
+ }
147
+ .conn.connected {
148
+ background: var(--green);
149
+ }
150
+ .conn.disconnected {
151
+ background: var(--red);
152
+ }
153
+ .status-bar {
154
+ margin-left: auto;
155
+ font-size: 12px;
156
+ color: var(--text2);
157
+ display: flex;
158
+ gap: 14px;
159
+ }
160
+ .status-bar .ok {
161
+ color: var(--green);
162
+ }
163
+ .status-bar .fail {
164
+ color: var(--red);
165
+ }
166
+
167
+ .runner main {
168
+ display: grid;
169
+ grid-template-columns: 240px minmax(0, 1fr) minmax(320px, 420px);
170
+ flex: 1;
171
+ min-height: 0;
172
+ }
173
+ .runner aside.tree,
174
+ .runner aside.detail {
175
+ background: var(--bg2);
176
+ border-right: 1px solid var(--border);
177
+ overflow: auto;
178
+ }
179
+ .runner aside.detail {
180
+ border-right: none;
181
+ border-left: 1px solid var(--border);
182
+ padding: 14px;
183
+ }
184
+ .list-pane {
185
+ display: flex;
186
+ flex-direction: column;
187
+ min-width: 0;
188
+ }
189
+ .filters {
190
+ display: flex;
191
+ flex-wrap: wrap;
192
+ gap: 8px;
193
+ align-items: center;
194
+ padding: 10px 14px;
195
+ border-bottom: 1px solid var(--border);
196
+ background: var(--bg2);
197
+ }
198
+ .filters input[type='search'] {
199
+ flex: 1;
200
+ min-width: 180px;
201
+ background: var(--bg3);
202
+ color: var(--text);
203
+ border: 1px solid var(--border);
204
+ border-radius: 4px;
205
+ padding: 5px 10px;
206
+ font: inherit;
207
+ font-size: 12px;
208
+ }
209
+ .chips {
210
+ display: flex;
211
+ gap: 4px;
212
+ }
213
+ .chip {
214
+ border: 1px solid var(--border);
215
+ background: transparent;
216
+ color: var(--text3);
217
+ padding: 4px 10px;
218
+ border-radius: 12px;
219
+ font: inherit;
220
+ font-size: 11px;
221
+ cursor: pointer;
222
+ }
223
+ .chip.on {
224
+ color: var(--text);
225
+ border-color: var(--text2);
226
+ background: var(--bg3);
227
+ }
228
+ .chip[data-status='failed'].on {
229
+ color: var(--red);
230
+ border-color: var(--red);
231
+ }
232
+ .chip[data-status='completed'].on {
233
+ color: var(--green);
234
+ border-color: var(--green);
235
+ }
236
+ .chip[data-status='running'].on {
237
+ color: var(--blue);
238
+ border-color: var(--blue);
239
+ }
240
+ .chip[data-status='timeout'].on {
241
+ color: var(--amber);
242
+ border-color: var(--amber);
243
+ }
244
+ select.group-by {
245
+ background: var(--bg3);
246
+ color: var(--text);
247
+ border: 1px solid var(--border);
248
+ border-radius: 4px;
249
+ padding: 4px 8px;
250
+ font: inherit;
251
+ font-size: 12px;
252
+ }
253
+ .btn {
254
+ background: var(--bg3);
255
+ color: var(--text);
256
+ border: 1px solid var(--border);
257
+ border-radius: 4px;
258
+ padding: 4px 10px;
259
+ font: inherit;
260
+ font-size: 12px;
261
+ cursor: pointer;
262
+ text-decoration: none;
263
+ }
264
+ .btn:hover {
265
+ border-color: var(--blue);
266
+ color: var(--blue);
267
+ }
268
+ .btn.subtle {
269
+ color: var(--text2);
270
+ }
271
+ .btn.copied {
272
+ color: var(--green);
273
+ border-color: var(--green);
274
+ }
275
+ .list {
276
+ flex: 1;
277
+ overflow: auto;
278
+ }
279
+ .group-header {
280
+ padding: 8px 14px;
281
+ font-size: 11px;
282
+ text-transform: uppercase;
283
+ color: var(--text2);
284
+ background: var(--bg);
285
+ border-bottom: 1px solid var(--border);
286
+ position: sticky;
287
+ top: 0;
288
+ letter-spacing: 0.04em;
289
+ }
290
+ .row {
291
+ display: grid;
292
+ grid-template-columns: 22px minmax(0, 1fr) auto auto auto;
293
+ gap: 10px;
294
+ align-items: center;
295
+ padding: 6px 14px;
296
+ border-bottom: 1px solid rgba(46, 49, 64, 0.4);
297
+ cursor: pointer;
298
+ }
299
+ .row .row-team {
300
+ color: var(--blue);
301
+ font-size: 11px;
302
+ white-space: nowrap;
303
+ padding: 1px 6px;
304
+ border: 1px solid var(--border);
305
+ border-radius: 3px;
306
+ background: rgba(59, 130, 246, 0.08);
307
+ }
308
+ .row:hover {
309
+ background: rgba(255, 255, 255, 0.02);
310
+ }
311
+ .row.selected {
312
+ background: rgba(59, 130, 246, 0.08);
313
+ }
314
+ .row .icon {
315
+ font-weight: 700;
316
+ }
317
+ .row .icon.completed {
318
+ color: var(--green);
319
+ }
320
+ .row .icon.failed {
321
+ color: var(--red);
322
+ }
323
+ .row .icon.timeout {
324
+ color: var(--amber);
325
+ }
326
+ .row .icon.running {
327
+ color: var(--blue);
328
+ }
329
+ .row .name {
330
+ overflow: hidden;
331
+ text-overflow: ellipsis;
332
+ white-space: nowrap;
333
+ font-size: 13px;
334
+ }
335
+ .row .meta {
336
+ color: var(--text2);
337
+ font-size: 11px;
338
+ white-space: nowrap;
339
+ }
340
+ .row .file {
341
+ color: var(--text3);
342
+ font-size: 11px;
343
+ white-space: nowrap;
344
+ overflow: hidden;
345
+ text-overflow: ellipsis;
346
+ max-width: 240px;
347
+ }
348
+
349
+ .tree {
350
+ padding: 10px 0;
351
+ font-size: 12px;
352
+ }
353
+ .tree-file {
354
+ padding: 4px 14px;
355
+ color: var(--text2);
356
+ display: flex;
357
+ justify-content: space-between;
358
+ gap: 8px;
359
+ }
360
+ .tree-file .fail {
361
+ color: var(--red);
362
+ }
363
+ .tree-scene {
364
+ padding: 3px 14px 3px 28px;
365
+ color: var(--text);
366
+ display: flex;
367
+ justify-content: space-between;
368
+ gap: 8px;
369
+ cursor: pointer;
370
+ }
371
+ .tree-scene:hover {
372
+ background: rgba(255, 255, 255, 0.03);
373
+ }
374
+ .tree-scene.failed,
375
+ .tree-scene.timeout {
376
+ color: var(--red);
377
+ }
378
+ .tree-scene.running {
379
+ color: var(--blue);
380
+ }
381
+ .tree-scene.selected {
382
+ background: rgba(59, 130, 246, 0.08);
383
+ }
384
+
385
+ .detail h3 {
386
+ font-size: 13px;
387
+ margin-bottom: 8px;
388
+ word-break: break-word;
389
+ }
390
+ .detail .meta-row {
391
+ color: var(--text2);
392
+ font-size: 11px;
393
+ margin-bottom: 12px;
394
+ display: flex;
395
+ flex-wrap: wrap;
396
+ gap: 8px;
397
+ }
398
+ .detail .meta-row .pill {
399
+ border: 1px solid var(--border);
400
+ border-radius: 10px;
401
+ padding: 1px 8px;
402
+ }
403
+ .detail .err {
404
+ background: rgba(239, 68, 68, 0.08);
405
+ border: 1px solid rgba(239, 68, 68, 0.4);
406
+ color: var(--red);
407
+ padding: 8px 10px;
408
+ border-radius: 4px;
409
+ font-size: 12px;
410
+ white-space: pre-wrap;
411
+ word-break: break-word;
412
+ margin-bottom: 12px;
413
+ }
414
+ .detail h4 {
415
+ font-size: 11px;
416
+ text-transform: uppercase;
417
+ color: var(--text2);
418
+ margin: 14px 0 6px;
419
+ letter-spacing: 0.04em;
420
+ }
421
+ .detail ul {
422
+ list-style: none;
423
+ }
424
+ .detail .alist li {
425
+ font-size: 12px;
426
+ padding: 2px 0;
427
+ }
428
+ .detail .alist .pass {
429
+ color: var(--green);
430
+ }
431
+ .detail .alist .fail {
432
+ color: var(--red);
433
+ }
434
+ .detail .timeline li {
435
+ font-size: 11px;
436
+ color: var(--text2);
437
+ padding: 2px 0;
438
+ }
439
+ .detail .timeline .err-step {
440
+ color: var(--red);
441
+ }
442
+ .detail .actions {
443
+ display: flex;
444
+ gap: 6px;
445
+ margin-bottom: 10px;
446
+ flex-wrap: wrap;
447
+ }
448
+ .detail .empty {
449
+ color: var(--text2);
450
+ font-size: 12px;
451
+ }
452
+
453
+ pre.snippet {
454
+ background: var(--bg);
455
+ border: 1px solid var(--border);
456
+ border-radius: 4px;
457
+ padding: 8px 0;
458
+ font-size: 11px;
459
+ overflow: auto;
460
+ max-height: 240px;
461
+ line-height: 1.45;
462
+ }
463
+ pre.snippet .ln {
464
+ display: inline-block;
465
+ width: 38px;
466
+ text-align: right;
467
+ color: var(--text3);
468
+ padding-right: 10px;
469
+ user-select: none;
470
+ }
471
+ pre.snippet .row-line {
472
+ padding: 0 8px;
473
+ white-space: pre;
474
+ }
475
+ pre.snippet .row-line.hl {
476
+ background: rgba(239, 68, 68, 0.12);
477
+ }
478
+
479
+ /* ── Home ─────────────────────────────────────────────── */
480
+ .index {
481
+ padding: 48px 32px;
482
+ max-width: 720px;
483
+ margin: 0 auto;
484
+ }
485
+ .index h1 {
486
+ font-size: 22px;
487
+ font-weight: 600;
488
+ display: flex;
489
+ align-items: center;
490
+ gap: 10px;
491
+ margin-bottom: 8px;
492
+ }
493
+ .index .lede {
494
+ color: var(--text2);
495
+ font-size: 13px;
496
+ margin-bottom: 32px;
497
+ }
498
+ .index .cards {
499
+ display: grid;
500
+ gap: 14px;
501
+ grid-template-columns: 1fr 1fr;
502
+ }
503
+ .index .card {
504
+ display: block;
505
+ text-align: left;
506
+ width: 100%;
507
+ padding: 20px;
508
+ border: 1px solid var(--border);
509
+ border-radius: 8px;
510
+ background: var(--bg2);
511
+ color: var(--text);
512
+ font: inherit;
513
+ cursor: pointer;
514
+ text-decoration: none;
515
+ transition:
516
+ border-color 0.15s,
517
+ background 0.15s;
518
+ }
519
+ .index .card:hover {
520
+ border-color: var(--text2);
521
+ background: var(--bg3);
522
+ }
523
+ .index .card .name {
524
+ font-size: 14px;
525
+ font-weight: 600;
526
+ margin-bottom: 6px;
527
+ }
528
+ .index .card .desc {
529
+ font-size: 12px;
530
+ color: var(--text2);
531
+ line-height: 1.5;
532
+ }
533
+
534
+ /* ── Waterfall view ───────────────────────────────────────
535
+ * Its own palette (so `--blue` tracks `--st-accent`, as the widget always
536
+ * has) and bare-element rules, all confined to this subtree. */
537
+ .waterfall-host {
538
+ --bg: var(--st-bg, #0f1117);
539
+ --bg2: #1a1d27;
540
+ --bg3: #252833;
541
+ --border: #2e3140;
542
+ --text: #e1e4ed;
543
+ --text2: #8b8fa3;
544
+ --green: #22c55e;
545
+ --red: #ef4444;
546
+ --amber: #f59e0b;
547
+ --blue: var(--st-accent, #3b82f6);
548
+
549
+ flex: 1;
550
+ min-height: 0;
551
+ display: block;
552
+ overflow: auto;
553
+ font-family: var(--st-font, 'SF Mono', 'Cascadia Code', 'Fira Code', ui-monospace, monospace);
554
+ font-size: var(--st-font-size, 13px);
555
+ color: var(--text);
556
+ background: var(--bg);
557
+
558
+ .root {
559
+ min-height: 100%;
560
+ background: var(--bg);
561
+ }
562
+
563
+ header {
564
+ position: sticky;
565
+ top: 0;
566
+ z-index: 10;
567
+ padding: 12px 20px;
568
+ border-bottom: 1px solid var(--border);
569
+ background: var(--bg2);
570
+ display: flex;
571
+ align-items: center;
572
+ gap: 14px;
573
+ flex-wrap: wrap;
574
+ }
575
+ header.running .logo {
576
+ animation: pulse 1.2s ease-in-out infinite;
577
+ }
578
+
579
+ h1 {
580
+ font-size: 15px;
581
+ font-weight: 600;
582
+ display: flex;
583
+ align-items: center;
584
+ gap: 8px;
585
+ }
586
+ .logo {
587
+ display: inline-flex;
588
+ align-items: center;
589
+ justify-content: center;
590
+ width: 22px;
591
+ height: 22px;
592
+ border-radius: 5px;
593
+ background: var(--blue);
594
+ color: #fff;
595
+ font-weight: 700;
596
+ box-shadow: none;
597
+ }
598
+
599
+ button {
600
+ font-family: inherit;
601
+ font-size: 12px;
602
+ cursor: pointer;
603
+ border: 1px solid var(--border);
604
+ background: var(--bg3);
605
+ color: var(--text);
606
+ padding: 5px 10px;
607
+ border-radius: 5px;
608
+ display: inline-flex;
609
+ align-items: center;
610
+ gap: 6px;
611
+ }
612
+ button:hover:not(:disabled) {
613
+ border-color: var(--blue);
614
+ }
615
+ button:disabled {
616
+ opacity: 0.5;
617
+ cursor: default;
618
+ }
619
+ .replay-all-btn {
620
+ color: var(--green);
621
+ }
622
+ .stop-btn {
623
+ color: var(--red);
624
+ }
625
+
626
+ .team-select-wrap {
627
+ font-size: 12px;
628
+ color: var(--text2);
629
+ display: flex;
630
+ align-items: center;
631
+ gap: 6px;
632
+ }
633
+ select {
634
+ font-family: inherit;
635
+ font-size: 12px;
636
+ background: var(--bg3);
637
+ color: var(--text);
638
+ border: 1px solid var(--border);
639
+ border-radius: 5px;
640
+ padding: 4px 6px;
641
+ }
642
+
643
+ .spacer {
644
+ flex: 1;
645
+ }
646
+
647
+ .stats {
648
+ display: flex;
649
+ align-items: center;
650
+ gap: 14px;
651
+ font-size: 12px;
652
+ }
653
+ .stat {
654
+ display: flex;
655
+ align-items: center;
656
+ gap: 5px;
657
+ }
658
+ .stat .label {
659
+ color: var(--text2);
660
+ }
661
+ .stat .value {
662
+ font-weight: 600;
663
+ }
664
+ .stat.pass .value {
665
+ color: var(--green);
666
+ }
667
+ .stat.fail .value {
668
+ color: var(--red);
669
+ }
670
+
671
+ .conn {
672
+ width: 9px;
673
+ height: 9px;
674
+ border-radius: 50%;
675
+ background: var(--text2);
676
+ }
677
+ .conn.connected {
678
+ background: var(--green);
679
+ }
680
+ .conn.disconnected {
681
+ background: var(--red);
682
+ }
683
+
684
+ .progress {
685
+ flex-basis: 100%;
686
+ height: 3px;
687
+ background: var(--bg3);
688
+ border-radius: 2px;
689
+ overflow: hidden;
690
+ }
691
+ .progress-fill {
692
+ height: 100%;
693
+ width: 0;
694
+ background: var(--blue);
695
+ transition: width 0.2s ease;
696
+ }
697
+ .progress.done .progress-fill {
698
+ background: var(--green);
699
+ }
700
+ .progress.has-failures .progress-fill {
701
+ background: var(--red);
702
+ }
703
+
704
+ main {
705
+ padding: 16px 20px;
706
+ }
707
+ .waiting {
708
+ text-align: center;
709
+ color: var(--text2);
710
+ padding: 60px 20px;
711
+ }
712
+ .waiting h2 {
713
+ font-size: 16px;
714
+ font-weight: 500;
715
+ margin-bottom: 8px;
716
+ color: var(--text);
717
+ }
718
+ .waiting code {
719
+ background: var(--bg3);
720
+ padding: 2px 6px;
721
+ border-radius: 4px;
722
+ }
723
+
724
+ .scene {
725
+ border: 1px solid var(--border);
726
+ border-radius: 8px;
727
+ background: var(--bg2);
728
+ margin-bottom: 14px;
729
+ overflow: hidden;
730
+ }
731
+ .scene.failed {
732
+ border-color: var(--red);
733
+ }
734
+ .scene-head {
735
+ display: flex;
736
+ align-items: center;
737
+ gap: 10px;
738
+ padding: 10px 14px;
739
+ border-bottom: 1px solid var(--border);
740
+ background: var(--bg3);
741
+ }
742
+ .scene-status {
743
+ font-weight: 700;
744
+ }
745
+ .scene-status.completed {
746
+ color: var(--green);
747
+ }
748
+ .scene-status.failed,
749
+ .scene-status.timeout {
750
+ color: var(--red);
751
+ }
752
+ .scene-status.running {
753
+ color: var(--amber);
754
+ }
755
+ .scene-name {
756
+ font-weight: 600;
757
+ }
758
+ .scene-file {
759
+ color: var(--text2);
760
+ font-size: 11px;
761
+ }
762
+ .scene-team {
763
+ font-size: 11px;
764
+ color: var(--blue);
765
+ border: 1px solid var(--border);
766
+ padding: 1px 6px;
767
+ border-radius: 10px;
768
+ }
769
+ .scene-dur {
770
+ color: var(--text2);
771
+ font-size: 11px;
772
+ margin-left: auto;
773
+ }
774
+ .copy-btn {
775
+ padding: 3px 7px;
776
+ font-size: 11px;
777
+ }
778
+ .copy-btn.copied {
779
+ color: var(--green);
780
+ border-color: var(--green);
781
+ }
782
+
783
+ .lanes {
784
+ padding: 8px 14px;
785
+ display: flex;
786
+ flex-direction: column;
787
+ gap: 6px;
788
+ }
789
+ .lane {
790
+ display: flex;
791
+ align-items: flex-start;
792
+ gap: 8px;
793
+ }
794
+ .lane-actor {
795
+ color: var(--text2);
796
+ min-width: 90px;
797
+ font-size: 11px;
798
+ padding-top: 3px;
799
+ }
800
+ .lane-items {
801
+ display: flex;
802
+ flex-wrap: wrap;
803
+ gap: 4px;
804
+ }
805
+ .pill {
806
+ font-size: 11px;
807
+ padding: 2px 7px;
808
+ border-radius: 4px;
809
+ border: 1px solid var(--border);
810
+ background: var(--bg3);
811
+ color: var(--text);
812
+ }
813
+ .pill.running {
814
+ border-color: var(--amber);
815
+ color: var(--amber);
816
+ }
817
+ .pill.success {
818
+ border-color: var(--green);
819
+ }
820
+ .pill.slow {
821
+ border-color: var(--amber);
822
+ }
823
+ .pill.error {
824
+ border-color: var(--red);
825
+ color: var(--red);
826
+ }
827
+ .pill .tgt {
828
+ color: var(--text2);
829
+ }
830
+
831
+ .assertions {
832
+ padding: 0 14px 10px;
833
+ display: flex;
834
+ flex-direction: column;
835
+ gap: 3px;
836
+ }
837
+ .assert {
838
+ font-size: 12px;
839
+ display: flex;
840
+ gap: 6px;
841
+ align-items: baseline;
842
+ }
843
+ .assert .mark {
844
+ font-weight: 700;
845
+ }
846
+ .assert.ok .mark {
847
+ color: var(--green);
848
+ }
849
+ .assert.bad .mark {
850
+ color: var(--red);
851
+ }
852
+ .assert .who {
853
+ color: var(--text2);
854
+ }
855
+
856
+ .scene-error {
857
+ margin: 0 14px 12px;
858
+ padding: 8px 10px;
859
+ border-radius: 6px;
860
+ background: rgba(239, 68, 68, 0.1);
861
+ border: 1px solid var(--red);
862
+ color: var(--red);
863
+ font-size: 12px;
864
+ white-space: pre-wrap;
865
+ cursor: pointer;
866
+ }
867
+ }
868
+ }
869
+
870
+ @keyframes pulse {
871
+ 0%,
872
+ 100% {
873
+ opacity: 1;
874
+ }
875
+ 50% {
876
+ opacity: 0.55;
877
+ }
878
+ }