@guanjinke/artifact-client 1.5.1 → 1.6.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 (72) hide show
  1. package/README.md +30 -1
  2. package/dist/cli/setup.d.ts.map +1 -1
  3. package/dist/cli/setup.js +6 -5
  4. package/dist/cli/setup.js.map +1 -1
  5. package/dist/cli/stored-client.d.ts +24 -0
  6. package/dist/cli/stored-client.d.ts.map +1 -0
  7. package/dist/cli/stored-client.js +59 -0
  8. package/dist/cli/stored-client.js.map +1 -0
  9. package/dist/cli/web.d.ts +23 -0
  10. package/dist/cli/web.d.ts.map +1 -0
  11. package/dist/cli/web.js +99 -0
  12. package/dist/cli/web.js.map +1 -0
  13. package/dist/environment/builder.d.ts.map +1 -1
  14. package/dist/environment/builder.js +21 -10
  15. package/dist/environment/builder.js.map +1 -1
  16. package/dist/environment/catalog.d.ts.map +1 -1
  17. package/dist/environment/catalog.js +18 -0
  18. package/dist/environment/catalog.js.map +1 -1
  19. package/dist/environment/config.d.ts.map +1 -1
  20. package/dist/environment/config.js +3 -1
  21. package/dist/environment/config.js.map +1 -1
  22. package/dist/environment/manager.d.ts +9 -0
  23. package/dist/environment/manager.d.ts.map +1 -1
  24. package/dist/environment/manager.js +32 -3
  25. package/dist/environment/manager.js.map +1 -1
  26. package/dist/environment/plan.d.ts +36 -0
  27. package/dist/environment/plan.d.ts.map +1 -1
  28. package/dist/environment/plan.js +52 -9
  29. package/dist/environment/plan.js.map +1 -1
  30. package/dist/environment/types.d.ts +7 -2
  31. package/dist/environment/types.d.ts.map +1 -1
  32. package/dist/environment/update.d.ts +2 -0
  33. package/dist/environment/update.d.ts.map +1 -1
  34. package/dist/environment/update.js +19 -10
  35. package/dist/environment/update.js.map +1 -1
  36. package/dist/index.d.ts +7 -0
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +12 -1
  39. package/dist/index.js.map +1 -1
  40. package/dist/web/api.d.ts +23 -0
  41. package/dist/web/api.d.ts.map +1 -0
  42. package/dist/web/api.js +273 -0
  43. package/dist/web/api.js.map +1 -0
  44. package/dist/web/assets.d.ts +26 -0
  45. package/dist/web/assets.d.ts.map +1 -0
  46. package/dist/web/assets.js +87 -0
  47. package/dist/web/assets.js.map +1 -0
  48. package/dist/web/http.d.ts +41 -0
  49. package/dist/web/http.d.ts.map +1 -0
  50. package/dist/web/http.js +167 -0
  51. package/dist/web/http.js.map +1 -0
  52. package/dist/web/jobs.d.ts +69 -0
  53. package/dist/web/jobs.d.ts.map +1 -0
  54. package/dist/web/jobs.js +110 -0
  55. package/dist/web/jobs.js.map +1 -0
  56. package/dist/web/providers.d.ts +9 -0
  57. package/dist/web/providers.d.ts.map +1 -0
  58. package/dist/web/providers.js +17 -0
  59. package/dist/web/providers.js.map +1 -0
  60. package/dist/web/server.d.ts +41 -0
  61. package/dist/web/server.d.ts.map +1 -0
  62. package/dist/web/server.js +134 -0
  63. package/dist/web/server.js.map +1 -0
  64. package/dist/web/static/app.js +59 -0
  65. package/dist/web/static/favicon.svg +12 -0
  66. package/dist/web/static/index.html +15 -0
  67. package/dist/web/static/styles.css +1263 -0
  68. package/dist/web/types.d.ts +52 -0
  69. package/dist/web/types.d.ts.map +1 -0
  70. package/dist/web/types.js +3 -0
  71. package/dist/web/types.js.map +1 -0
  72. package/package.json +9 -2
@@ -0,0 +1,1263 @@
1
+ /* AFM Environment Manager — dark glassmorphism theme.
2
+ Everything is class-driven: the page is served under a strict CSP that forbids
3
+ inline <style> and style="" attributes. */
4
+
5
+ :root {
6
+ --bg: #05070d;
7
+ --glass: rgba(255, 255, 255, .055);
8
+ --glass-strong: rgba(255, 255, 255, .09);
9
+ --border: rgba(255, 255, 255, .10);
10
+ --border-strong: rgba(255, 255, 255, .18);
11
+ --text: #eaeefb;
12
+ --muted: #98a2bd;
13
+ --accent: #4d6bfe;
14
+ --accent-soft: rgba(77, 107, 254, .16);
15
+ --cyan: #35d0e8;
16
+ --ok: #3ddc97;
17
+ --warn: #ffc24b;
18
+ --err: #ff6b81;
19
+ --radius: 18px;
20
+ /* One height for every single-line control, so a <select> and an <input> in
21
+ the same row cannot disagree. */
22
+ --control-height: 38px;
23
+ --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Courier New", monospace;
24
+ --panel: linear-gradient(160deg, var(--glass-strong), var(--glass));
25
+ --panel-shadow: 0 18px 50px rgba(0, 0, 0, .45), inset 0 1px 0 rgba(255, 255, 255, .06);
26
+ }
27
+
28
+ *,
29
+ *::before,
30
+ *::after {
31
+ box-sizing: border-box;
32
+ }
33
+
34
+ html,
35
+ body {
36
+ margin: 0;
37
+ padding: 0;
38
+ }
39
+
40
+ body {
41
+ min-height: 100vh;
42
+ background:
43
+ radial-gradient(900px 520px at 12% -8%, rgba(77, 107, 254, .30), transparent 60%),
44
+ radial-gradient(800px 480px at 88% -4%, rgba(53, 208, 232, .20), transparent 55%),
45
+ var(--bg);
46
+ background-repeat: no-repeat;
47
+ background-attachment: fixed;
48
+ color: var(--text);
49
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", Roboto, Helvetica, Arial, sans-serif;
50
+ font-size: 14px;
51
+ line-height: 1.55;
52
+ -webkit-font-smoothing: antialiased;
53
+ text-rendering: optimizeLegibility;
54
+ }
55
+
56
+ h1,
57
+ h2,
58
+ h3 {
59
+ line-height: 1.25;
60
+ }
61
+
62
+ p {
63
+ margin: 0;
64
+ }
65
+
66
+ button {
67
+ font: inherit;
68
+ }
69
+
70
+ code,
71
+ .mono,
72
+ pre {
73
+ font-family: var(--mono);
74
+ }
75
+
76
+ a {
77
+ color: var(--cyan);
78
+ }
79
+
80
+ ::selection {
81
+ background: var(--accent-soft);
82
+ }
83
+
84
+ :focus-visible {
85
+ outline: 2px solid rgba(77, 107, 254, .4);
86
+ outline-offset: 2px;
87
+ border-radius: 10px;
88
+ }
89
+
90
+ .sr-only {
91
+ position: absolute;
92
+ width: 1px;
93
+ height: 1px;
94
+ padding: 0;
95
+ margin: -1px;
96
+ overflow: hidden;
97
+ clip: rect(0 0 0 0);
98
+ white-space: nowrap;
99
+ border: 0;
100
+ }
101
+
102
+ .muted { color: var(--muted); }
103
+ .small { font-size: 12px; }
104
+ .strong { font-weight: 650; }
105
+ .err-text { color: var(--err); }
106
+ .warn-text { color: var(--warn); }
107
+ .danger-text { color: #ffd7dc; }
108
+
109
+ /* ------------------------------------------------------------------ layout */
110
+
111
+ .app {
112
+ min-height: 100vh;
113
+ max-width: 1440px;
114
+ margin: 0 auto;
115
+ padding: 26px 22px 40px;
116
+ display: grid;
117
+ gap: 18px;
118
+ align-content: start;
119
+ }
120
+
121
+ .app-main {
122
+ display: grid;
123
+ gap: 18px;
124
+ }
125
+
126
+ .app-footer {
127
+ display: flex;
128
+ flex-wrap: wrap;
129
+ gap: 8px 16px;
130
+ justify-content: space-between;
131
+ padding: 4px 2px;
132
+ color: var(--muted);
133
+ font-size: 11.5px;
134
+ }
135
+
136
+ .toolbar {
137
+ display: flex;
138
+ flex-wrap: wrap;
139
+ align-items: center;
140
+ gap: 12px;
141
+ }
142
+
143
+ /* ------------------------------------------------------------------ header */
144
+
145
+ .topbar {
146
+ display: grid;
147
+ gap: 14px;
148
+ padding: 18px 20px;
149
+ background: var(--panel);
150
+ backdrop-filter: blur(18px) saturate(140%);
151
+ -webkit-backdrop-filter: blur(18px) saturate(140%);
152
+ border: 1px solid var(--border);
153
+ border-radius: var(--radius);
154
+ box-shadow: var(--panel-shadow);
155
+ }
156
+
157
+ .brand {
158
+ display: flex;
159
+ align-items: center;
160
+ gap: 12px;
161
+ min-width: 0;
162
+ }
163
+
164
+ .brand-mark {
165
+ flex: 0 0 auto;
166
+ display: grid;
167
+ place-items: center;
168
+ width: 42px;
169
+ height: 42px;
170
+ border-radius: 13px;
171
+ background: linear-gradient(135deg, rgba(77, 107, 254, .95), rgba(53, 208, 232, .8));
172
+ color: #f8faff;
173
+ font-weight: 700;
174
+ font-size: 13px;
175
+ letter-spacing: .04em;
176
+ box-shadow: 0 10px 24px rgba(77, 107, 254, .35);
177
+ }
178
+
179
+ .brand-mark.big {
180
+ width: 56px;
181
+ height: 56px;
182
+ border-radius: 17px;
183
+ font-size: 15px;
184
+ }
185
+
186
+ .brand-name {
187
+ margin: 0;
188
+ font-size: 18px;
189
+ font-weight: 680;
190
+ letter-spacing: .01em;
191
+ }
192
+
193
+ .brand-sub {
194
+ margin: 4px 0 0;
195
+ color: var(--muted);
196
+ font-size: 12.5px;
197
+ }
198
+
199
+ .topbar-meta {
200
+ display: flex;
201
+ flex-wrap: wrap;
202
+ gap: 10px;
203
+ }
204
+
205
+ .meta-chip {
206
+ display: grid;
207
+ gap: 4px;
208
+ min-width: 150px;
209
+ padding: 8px 12px;
210
+ background: rgba(255, 255, 255, .04);
211
+ border: 1px solid var(--border);
212
+ border-radius: 12px;
213
+ font-size: 12px;
214
+ }
215
+
216
+ .meta-label {
217
+ color: var(--muted);
218
+ font-size: 11px;
219
+ letter-spacing: .06em;
220
+ text-transform: uppercase;
221
+ }
222
+
223
+ .meta-value {
224
+ word-break: break-all;
225
+ }
226
+
227
+ .providers {
228
+ display: flex;
229
+ gap: 8px;
230
+ }
231
+
232
+ .provider-badge {
233
+ display: inline-flex;
234
+ align-items: center;
235
+ gap: 6px;
236
+ padding: 3px 10px;
237
+ border: 1px solid var(--border);
238
+ border-radius: 999px;
239
+ font-size: 11.5px;
240
+ letter-spacing: .06em;
241
+ text-transform: uppercase;
242
+ }
243
+
244
+ .provider-badge .dot {
245
+ width: 7px;
246
+ height: 7px;
247
+ border-radius: 50%;
248
+ background: currentColor;
249
+ box-shadow: 0 0 8px currentColor;
250
+ }
251
+
252
+ .provider-badge.on {
253
+ color: var(--ok);
254
+ border-color: rgba(61, 220, 151, .40);
255
+ background: rgba(61, 220, 151, .12);
256
+ }
257
+
258
+ .provider-badge.off {
259
+ color: var(--warn);
260
+ border-color: rgba(255, 194, 75, .35);
261
+ background: rgba(255, 194, 75, .10);
262
+ }
263
+
264
+ .provider-hint {
265
+ grid-column: 1 / -1;
266
+ margin: 0;
267
+ padding: 10px 12px;
268
+ color: #ffe0ab;
269
+ background: rgba(255, 194, 75, .09);
270
+ border: 1px solid rgba(255, 194, 75, .28);
271
+ border-radius: 12px;
272
+ font-size: 12.5px;
273
+ line-height: 1.7;
274
+ }
275
+
276
+ .provider-hint .code {
277
+ margin: 0 4px;
278
+ }
279
+
280
+ /* ----------------------------------------------------------------- banners */
281
+
282
+ .banner {
283
+ display: flex;
284
+ align-items: center;
285
+ justify-content: space-between;
286
+ gap: 12px;
287
+ padding: 12px 16px;
288
+ background: var(--panel);
289
+ backdrop-filter: blur(18px) saturate(140%);
290
+ -webkit-backdrop-filter: blur(18px) saturate(140%);
291
+ border: 1px solid var(--border);
292
+ border-radius: 14px;
293
+ box-shadow: 0 12px 34px rgba(0, 0, 0, .35);
294
+ }
295
+
296
+ .banner.error {
297
+ border-color: rgba(255, 107, 129, .42);
298
+ background: linear-gradient(160deg, rgba(255, 107, 129, .16), rgba(255, 107, 129, .07));
299
+ }
300
+
301
+ .banner.success {
302
+ border-color: rgba(61, 220, 151, .40);
303
+ background: linear-gradient(160deg, rgba(61, 220, 151, .14), rgba(61, 220, 151, .06));
304
+ }
305
+
306
+ .banner.info {
307
+ border-color: rgba(77, 107, 254, .40);
308
+ background: linear-gradient(160deg, rgba(77, 107, 254, .16), rgba(77, 107, 254, .06));
309
+ }
310
+
311
+ .banner-text {
312
+ font-size: 13px;
313
+ line-height: 1.6;
314
+ }
315
+
316
+ .banner-actions {
317
+ display: flex;
318
+ flex: 0 0 auto;
319
+ gap: 8px;
320
+ }
321
+
322
+ /* ------------------------------------------------------------------ panels */
323
+
324
+ .panel {
325
+ display: grid;
326
+ gap: 14px;
327
+ padding: 18px 20px;
328
+ background: var(--panel);
329
+ backdrop-filter: blur(18px) saturate(140%);
330
+ -webkit-backdrop-filter: blur(18px) saturate(140%);
331
+ border: 1px solid var(--border);
332
+ border-radius: var(--radius);
333
+ box-shadow: var(--panel-shadow);
334
+ }
335
+
336
+ .panel-head {
337
+ display: flex;
338
+ align-items: center;
339
+ gap: 10px;
340
+ }
341
+
342
+ .panel-title {
343
+ margin: 0;
344
+ font-size: 15px;
345
+ font-weight: 650;
346
+ }
347
+
348
+ .count {
349
+ padding: 1px 8px;
350
+ color: var(--muted);
351
+ background: rgba(255, 255, 255, .05);
352
+ border: 1px solid var(--border);
353
+ border-radius: 999px;
354
+ font-size: 11.5px;
355
+ }
356
+
357
+ .panel-actions {
358
+ display: flex;
359
+ align-items: center;
360
+ gap: 8px;
361
+ margin-left: auto;
362
+ }
363
+
364
+ /* ----------------------------------------------------------------- buttons */
365
+
366
+ .btn {
367
+ display: inline-flex;
368
+ align-items: center;
369
+ justify-content: center;
370
+ gap: 6px;
371
+ padding: 8px 14px;
372
+ color: var(--text);
373
+ background: linear-gradient(160deg, rgba(255, 255, 255, .10), rgba(255, 255, 255, .045));
374
+ border: 1px solid var(--border-strong);
375
+ border-radius: 10px;
376
+ font-size: 13px;
377
+ font-weight: 500;
378
+ cursor: pointer;
379
+ transition: transform .16s ease, border-color .16s ease, box-shadow .16s ease, background-color .16s ease;
380
+ }
381
+
382
+ .btn:hover:not(:disabled) {
383
+ transform: translateY(-1px);
384
+ border-color: rgba(255, 255, 255, .30);
385
+ }
386
+
387
+ .btn:active:not(:disabled) {
388
+ transform: translateY(0);
389
+ }
390
+
391
+ .btn.primary {
392
+ background: linear-gradient(135deg, var(--accent), #3f7ef7);
393
+ border-color: rgba(155, 175, 255, .45);
394
+ box-shadow: 0 10px 26px rgba(77, 107, 254, .35);
395
+ }
396
+
397
+ .btn.primary:hover:not(:disabled) {
398
+ box-shadow: 0 14px 30px rgba(77, 107, 254, .45);
399
+ }
400
+
401
+ .btn.danger {
402
+ color: #ffd9de;
403
+ background: linear-gradient(160deg, rgba(255, 107, 129, .22), rgba(255, 107, 129, .12));
404
+ border-color: rgba(255, 107, 129, .45);
405
+ }
406
+
407
+ .btn.ghost {
408
+ background: transparent;
409
+ border-color: var(--border);
410
+ }
411
+
412
+ .btn.small {
413
+ padding: 6px 10px;
414
+ border-radius: 9px;
415
+ font-size: 12px;
416
+ }
417
+
418
+ .btn:disabled {
419
+ opacity: .45;
420
+ cursor: not-allowed;
421
+ transform: none;
422
+ box-shadow: none;
423
+ }
424
+
425
+ /* ------------------------------------------------------------------- pills */
426
+
427
+ .pill {
428
+ display: inline-flex;
429
+ align-items: center;
430
+ padding: 3px 10px;
431
+ border: 1px solid var(--border-strong);
432
+ border-radius: 999px;
433
+ font-size: 10.5px;
434
+ font-weight: 650;
435
+ letter-spacing: .09em;
436
+ text-transform: uppercase;
437
+ white-space: nowrap;
438
+ }
439
+
440
+ .pill.s-ok,
441
+ .pill.s-job-succeeded {
442
+ color: var(--ok);
443
+ background: rgba(61, 220, 151, .14);
444
+ border-color: rgba(61, 220, 151, .38);
445
+ }
446
+
447
+ .pill.s-accent,
448
+ .pill.s-job-running {
449
+ color: #b9c6ff;
450
+ background: var(--accent-soft);
451
+ border-color: rgba(77, 107, 254, .45);
452
+ }
453
+
454
+ .pill.s-warn,
455
+ .pill.s-job-queued {
456
+ color: var(--warn);
457
+ background: rgba(255, 194, 75, .14);
458
+ border-color: rgba(255, 194, 75, .38);
459
+ }
460
+
461
+ .pill.s-warn-muted {
462
+ color: var(--warn);
463
+ background: rgba(255, 194, 75, .08);
464
+ border-color: rgba(255, 194, 75, .22);
465
+ }
466
+
467
+ .pill.s-err,
468
+ .pill.s-job-failed {
469
+ color: var(--err);
470
+ background: rgba(255, 107, 129, .14);
471
+ border-color: rgba(255, 107, 129, .40);
472
+ }
473
+
474
+ .pill.s-muted {
475
+ color: var(--muted);
476
+ background: rgba(255, 255, 255, .05);
477
+ border-color: var(--border-strong);
478
+ }
479
+
480
+ /* ---------------------------------------------------------- environments */
481
+
482
+ /* One compact row per environment instead of a card grid: every row shows the
483
+ same columns, so a long list stays scannable and uses the width. */
484
+ .env-list {
485
+ display: grid;
486
+ gap: 0;
487
+ margin: 0;
488
+ padding: 0;
489
+ list-style: none;
490
+ }
491
+
492
+ .env-row {
493
+ display: grid;
494
+ gap: 4px;
495
+ padding: 12px 4px;
496
+ border-top: 1px solid var(--border);
497
+ transition: background-color .16s ease;
498
+ }
499
+
500
+ .env-row:first-child {
501
+ border-top: none;
502
+ }
503
+
504
+ .env-row:hover {
505
+ background: rgba(255, 255, 255, .028);
506
+ }
507
+
508
+ .env-row-main {
509
+ display: grid;
510
+ grid-template-columns: minmax(170px, 1fr) minmax(200px, auto) auto minmax(180px, 2fr) auto;
511
+ align-items: center;
512
+ gap: 10px 16px;
513
+ }
514
+
515
+ .env-id {
516
+ display: flex;
517
+ align-items: center;
518
+ gap: 8px;
519
+ min-width: 0;
520
+ }
521
+
522
+ .env-name {
523
+ font-size: 14.5px;
524
+ font-weight: 650;
525
+ overflow: hidden;
526
+ text-overflow: ellipsis;
527
+ white-space: nowrap;
528
+ }
529
+
530
+ .env-site {
531
+ color: var(--cyan);
532
+ font-size: 12.5px;
533
+ text-decoration: none;
534
+ border-bottom: 1px dashed rgba(53, 208, 232, .35);
535
+ white-space: nowrap;
536
+ }
537
+
538
+ .env-site:hover {
539
+ color: #8ee7f5;
540
+ border-bottom-color: rgba(142, 231, 245, .7);
541
+ }
542
+
543
+ .env-site.muted {
544
+ color: var(--muted);
545
+ border-bottom-color: var(--border);
546
+ }
547
+
548
+ .env-meta {
549
+ color: var(--muted);
550
+ font-size: 12px;
551
+ white-space: nowrap;
552
+ }
553
+
554
+ .env-chips {
555
+ min-width: 0;
556
+ overflow: hidden;
557
+ max-height: 22px;
558
+ }
559
+
560
+ .env-row-sub {
561
+ display: flex;
562
+ flex-wrap: wrap;
563
+ align-items: center;
564
+ gap: 6px 14px;
565
+ min-width: 0;
566
+ font-size: 11.5px;
567
+ }
568
+
569
+ .env-root {
570
+ margin: 0;
571
+ color: var(--muted);
572
+ font-size: 11.5px;
573
+ overflow: hidden;
574
+ text-overflow: ellipsis;
575
+ white-space: nowrap;
576
+ max-width: 100%;
577
+ }
578
+
579
+ .env-problem {
580
+ margin: 0;
581
+ padding: 2px 8px;
582
+ color: #ffd7dc;
583
+ background: rgba(255, 107, 129, .10);
584
+ border: 1px solid rgba(255, 107, 129, .30);
585
+ border-radius: 8px;
586
+ font-size: 11.5px;
587
+ line-height: 1.6;
588
+ word-break: break-word;
589
+ }
590
+
591
+ .env-actions {
592
+ display: flex;
593
+ flex-wrap: wrap;
594
+ justify-content: flex-end;
595
+ gap: 6px;
596
+ }
597
+
598
+ .card {
599
+ background: var(--panel);
600
+ backdrop-filter: blur(18px) saturate(140%);
601
+ -webkit-backdrop-filter: blur(18px) saturate(140%);
602
+ border: 1px solid var(--border);
603
+ border-radius: var(--radius);
604
+ box-shadow: var(--panel-shadow);
605
+ transition: transform .18s ease, border-color .18s ease, box-shadow .18s ease;
606
+ }
607
+
608
+ /* ------------------------------------------------------------------- chips */
609
+
610
+ .chips {
611
+ display: flex;
612
+ flex-wrap: wrap;
613
+ align-items: center;
614
+ gap: 6px;
615
+ }
616
+
617
+ .chips.inline {
618
+ display: inline-flex;
619
+ }
620
+
621
+ .chip-tag {
622
+ display: inline-flex;
623
+ align-items: center;
624
+ padding: 2px 9px;
625
+ color: var(--text);
626
+ background: rgba(255, 255, 255, .06);
627
+ border: 1px solid var(--border);
628
+ border-radius: 999px;
629
+ font-size: 11.5px;
630
+ font-family: var(--mono);
631
+ }
632
+
633
+ .chip-tag.local {
634
+ color: var(--cyan);
635
+ background: rgba(53, 208, 232, .12);
636
+ border-color: rgba(53, 208, 232, .35);
637
+ letter-spacing: .04em;
638
+ text-transform: uppercase;
639
+ }
640
+
641
+ .chip-tag.warn {
642
+ color: var(--warn);
643
+ background: rgba(255, 194, 75, .12);
644
+ border-color: rgba(255, 194, 75, .35);
645
+ }
646
+
647
+ .chip-row {
648
+ display: flex;
649
+ flex-wrap: wrap;
650
+ gap: 8px;
651
+ margin-top: 2px;
652
+ }
653
+
654
+ .chip {
655
+ padding: 4px 11px;
656
+ color: var(--text);
657
+ background: rgba(255, 255, 255, .06);
658
+ border: 1px solid var(--border);
659
+ border-radius: 999px;
660
+ font-family: var(--mono);
661
+ font-size: 12px;
662
+ cursor: pointer;
663
+ transition: transform .14s ease, border-color .14s ease, background-color .14s ease;
664
+ }
665
+
666
+ .chip:hover:not(:disabled) {
667
+ transform: translateY(-1px);
668
+ border-color: var(--border-strong);
669
+ background: rgba(255, 255, 255, .10);
670
+ }
671
+
672
+ .chip.active,
673
+ .chip.accent {
674
+ color: #cdd7ff;
675
+ background: var(--accent-soft);
676
+ border-color: rgba(77, 107, 254, .48);
677
+ }
678
+
679
+ .chip:disabled {
680
+ opacity: .4;
681
+ cursor: not-allowed;
682
+ }
683
+
684
+ /* -------------------------------------------------------------------- jobs */
685
+
686
+ .jobs {
687
+ display: grid;
688
+ gap: 12px;
689
+ }
690
+
691
+ .job {
692
+ display: grid;
693
+ gap: 10px;
694
+ padding: 12px 14px;
695
+ background: rgba(255, 255, 255, .035);
696
+ border: 1px solid var(--border);
697
+ border-radius: 14px;
698
+ }
699
+
700
+ .job.running {
701
+ border-color: rgba(53, 208, 232, .34);
702
+ }
703
+
704
+ .job.queued {
705
+ border-color: rgba(255, 194, 75, .28);
706
+ }
707
+
708
+ .job.failed {
709
+ border-color: rgba(255, 107, 129, .32);
710
+ }
711
+
712
+ .job-head {
713
+ display: flex;
714
+ flex-wrap: wrap;
715
+ align-items: center;
716
+ gap: 8px 10px;
717
+ font-size: 12.5px;
718
+ }
719
+
720
+ .job-kind {
721
+ color: var(--muted);
722
+ }
723
+
724
+ .job-target {
725
+ font-size: 13.5px;
726
+ }
727
+
728
+ .job-time {
729
+ margin-left: auto;
730
+ color: var(--muted);
731
+ font-size: 11.5px;
732
+ }
733
+
734
+ .job-log {
735
+ display: grid;
736
+ gap: 4px;
737
+ max-height: 220px;
738
+ overflow-y: auto;
739
+ padding: 10px 12px;
740
+ background: rgba(0, 0, 0, .35);
741
+ border: 1px solid var(--border);
742
+ border-radius: 12px;
743
+ font-size: 12px;
744
+ }
745
+
746
+ .log-line {
747
+ display: grid;
748
+ grid-template-columns: 66px minmax(0, 1fr);
749
+ gap: 10px;
750
+ margin: 0;
751
+ align-items: start;
752
+ }
753
+
754
+ .log-time {
755
+ color: rgba(152, 162, 189, .75);
756
+ }
757
+
758
+ .log-text {
759
+ font-family: var(--mono);
760
+ white-space: pre-wrap;
761
+ word-break: break-word;
762
+ }
763
+
764
+ .job-error {
765
+ margin: 0;
766
+ padding: 8px 10px;
767
+ color: #ffd7dc;
768
+ background: rgba(255, 107, 129, .12);
769
+ border: 1px solid rgba(255, 107, 129, .38);
770
+ border-radius: 12px;
771
+ font-size: 12.5px;
772
+ line-height: 1.6;
773
+ }
774
+
775
+ .job-details summary {
776
+ padding: 2px 0;
777
+ color: var(--muted);
778
+ font-size: 12.5px;
779
+ cursor: pointer;
780
+ }
781
+
782
+ .job-details[open] summary {
783
+ margin-bottom: 10px;
784
+ }
785
+
786
+ /* ----------------------------------------------------------------- results */
787
+
788
+ .result {
789
+ display: grid;
790
+ gap: 8px;
791
+ font-size: 12.5px;
792
+ }
793
+
794
+ .result-title {
795
+ font-size: 12.5px;
796
+ font-weight: 650;
797
+ }
798
+
799
+ .result-list {
800
+ display: grid;
801
+ gap: 6px;
802
+ margin: 0;
803
+ padding: 0;
804
+ list-style: none;
805
+ color: var(--muted);
806
+ }
807
+
808
+ .result-list li {
809
+ display: flex;
810
+ flex-wrap: wrap;
811
+ align-items: baseline;
812
+ gap: 8px;
813
+ }
814
+
815
+ .result-arrow {
816
+ display: inline-flex;
817
+ align-items: baseline;
818
+ gap: 4px;
819
+ }
820
+
821
+ .result-line {
822
+ color: var(--muted);
823
+ }
824
+
825
+ .warn-list {
826
+ color: var(--warn);
827
+ }
828
+
829
+ .callout {
830
+ padding: 10px 12px;
831
+ border-radius: 12px;
832
+ font-size: 12.5px;
833
+ line-height: 1.6;
834
+ }
835
+
836
+ .callout.warn {
837
+ color: #ffe0ab;
838
+ background: rgba(255, 194, 75, .10);
839
+ border: 1px solid rgba(255, 194, 75, .30);
840
+ }
841
+
842
+ .callout.info {
843
+ color: #c9d4ff;
844
+ background: var(--accent-soft);
845
+ border: 1px solid rgba(77, 107, 254, .35);
846
+ }
847
+
848
+ .raw {
849
+ font-size: 12px;
850
+ color: var(--muted);
851
+ }
852
+
853
+ .raw summary {
854
+ cursor: pointer;
855
+ }
856
+
857
+ .pre {
858
+ max-height: 260px;
859
+ overflow: auto;
860
+ margin: 8px 0 0;
861
+ padding: 10px;
862
+ background: rgba(0, 0, 0, .35);
863
+ border: 1px solid var(--border);
864
+ border-radius: 10px;
865
+ font-size: 11.5px;
866
+ line-height: 1.5;
867
+ white-space: pre-wrap;
868
+ word-break: break-word;
869
+ }
870
+
871
+ /* ------------------------------------------------------------------- forms */
872
+
873
+ .form {
874
+ display: grid;
875
+ gap: 16px;
876
+ }
877
+
878
+ .field {
879
+ display: grid;
880
+ gap: 6px;
881
+ /* Never stretch a control to fill a row: a taller neighbour must not change
882
+ the height of this field's input. */
883
+ align-content: start;
884
+ }
885
+
886
+ /* A row of fields plus a note that belongs to the row as a whole. */
887
+ .field-block {
888
+ display: grid;
889
+ gap: 6px;
890
+ }
891
+
892
+ .field-row {
893
+ display: grid;
894
+ grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
895
+ gap: 14px;
896
+ align-items: start;
897
+ }
898
+
899
+ .label {
900
+ color: var(--muted);
901
+ font-size: 12.5px;
902
+ font-weight: 600;
903
+ letter-spacing: .04em;
904
+ }
905
+
906
+ .helper {
907
+ color: var(--muted);
908
+ font-size: 12px;
909
+ line-height: 1.6;
910
+ }
911
+
912
+ .hint {
913
+ margin: 0;
914
+ color: var(--muted);
915
+ font-size: 12px;
916
+ line-height: 1.6;
917
+ word-break: break-all;
918
+ }
919
+
920
+ .fieldset {
921
+ display: grid;
922
+ gap: 10px;
923
+ margin: 0;
924
+ padding: 12px 14px;
925
+ border: 1px solid var(--border);
926
+ border-radius: 14px;
927
+ }
928
+
929
+ .input,
930
+ .select,
931
+ .textarea {
932
+ width: 100%;
933
+ padding: 10px 12px;
934
+ color: var(--text);
935
+ background: rgba(4, 7, 14, .55);
936
+ border: 1px solid var(--border);
937
+ border-radius: 12px;
938
+ font-size: 13px;
939
+ transition: border-color .16s ease, box-shadow .16s ease;
940
+ }
941
+
942
+ .input::placeholder,
943
+ .textarea::placeholder {
944
+ color: rgba(152, 162, 189, .65);
945
+ }
946
+
947
+ .input:focus,
948
+ .select:focus,
949
+ .textarea:focus {
950
+ border-color: rgba(77, 107, 254, .65);
951
+ box-shadow: 0 0 0 3px var(--accent-soft);
952
+ outline: none;
953
+ }
954
+
955
+ .textarea {
956
+ resize: vertical;
957
+ min-height: 76px;
958
+ line-height: 1.6;
959
+ }
960
+
961
+ /* A <select> is intrinsically a little taller than an <input>; pin both to the
962
+ same height so a row of controls lines up exactly. `box-sizing: border-box`
963
+ is global, so this includes the padding and the border. */
964
+ .input,
965
+ .select {
966
+ height: var(--control-height);
967
+ line-height: 1.25;
968
+ }
969
+
970
+ .select {
971
+ appearance: none;
972
+ background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%), linear-gradient(135deg, var(--muted) 50%, transparent 50%);
973
+ background-position: calc(100% - 18px) 50%, calc(100% - 13px) 50%;
974
+ background-size: 5px 5px, 5px 5px;
975
+ background-repeat: no-repeat;
976
+ }
977
+
978
+ .checks,
979
+ .radios {
980
+ display: flex;
981
+ flex-wrap: wrap;
982
+ gap: 10px 18px;
983
+ }
984
+
985
+ .checkbox,
986
+ .radio {
987
+ display: inline-flex;
988
+ align-items: center;
989
+ gap: 8px;
990
+ font-size: 13px;
991
+ cursor: pointer;
992
+ }
993
+
994
+ .checkbox input,
995
+ .radio input {
996
+ width: 16px;
997
+ height: 16px;
998
+ accent-color: var(--accent);
999
+ cursor: pointer;
1000
+ }
1001
+
1002
+ .radio.disabled {
1003
+ opacity: .55;
1004
+ cursor: not-allowed;
1005
+ }
1006
+
1007
+ .radio.disabled input {
1008
+ cursor: not-allowed;
1009
+ }
1010
+
1011
+ .checkbox.solo {
1012
+ padding: 10px 12px;
1013
+ background: rgba(255, 255, 255, .03);
1014
+ border: 1px solid var(--border);
1015
+ border-radius: 12px;
1016
+ }
1017
+
1018
+ .form-error {
1019
+ padding: 10px 12px;
1020
+ color: #ffd7dc;
1021
+ background: rgba(255, 107, 129, .12);
1022
+ border: 1px solid rgba(255, 107, 129, .40);
1023
+ border-radius: 12px;
1024
+ font-size: 12.5px;
1025
+ line-height: 1.6;
1026
+ }
1027
+
1028
+ .status-strip {
1029
+ display: flex;
1030
+ align-items: center;
1031
+ gap: 10px;
1032
+ padding: 8px 12px;
1033
+ color: var(--muted);
1034
+ background: rgba(255, 255, 255, .04);
1035
+ border: 1px solid var(--border);
1036
+ border-radius: 12px;
1037
+ font-size: 12.5px;
1038
+ }
1039
+
1040
+ .spec-forms {
1041
+ margin: 4px 0 0;
1042
+ padding-left: 18px;
1043
+ color: var(--muted);
1044
+ font-size: 12px;
1045
+ line-height: 1.75;
1046
+ }
1047
+
1048
+ .code {
1049
+ padding: 1px 6px;
1050
+ background: rgba(255, 255, 255, .07);
1051
+ border: 1px solid var(--border);
1052
+ border-radius: 6px;
1053
+ font-size: 12px;
1054
+ }
1055
+
1056
+ /* ------------------------------------------------------------------ modals */
1057
+
1058
+ .scrim {
1059
+ position: fixed;
1060
+ inset: 0;
1061
+ z-index: 50;
1062
+ display: grid;
1063
+ place-items: center;
1064
+ padding: 24px;
1065
+ overflow-y: auto;
1066
+ background: rgba(3, 5, 10, .62);
1067
+ backdrop-filter: blur(6px);
1068
+ -webkit-backdrop-filter: blur(6px);
1069
+ }
1070
+
1071
+ .modal {
1072
+ display: flex;
1073
+ flex-direction: column;
1074
+ width: min(680px, 100%);
1075
+ max-height: 88vh;
1076
+ overflow: hidden;
1077
+ background: linear-gradient(160deg, rgba(255, 255, 255, .105), rgba(255, 255, 255, .045));
1078
+ backdrop-filter: blur(22px) saturate(150%);
1079
+ -webkit-backdrop-filter: blur(22px) saturate(150%);
1080
+ border: 1px solid var(--border-strong);
1081
+ border-radius: 22px;
1082
+ box-shadow: 0 30px 80px rgba(0, 0, 0, .60), inset 0 1px 0 rgba(255, 255, 255, .08);
1083
+ }
1084
+
1085
+ .modal-head {
1086
+ display: flex;
1087
+ align-items: center;
1088
+ justify-content: space-between;
1089
+ gap: 12px;
1090
+ padding: 18px 22px;
1091
+ border-bottom: 1px solid var(--border);
1092
+ }
1093
+
1094
+ .modal-title {
1095
+ margin: 0;
1096
+ font-size: 16px;
1097
+ font-weight: 650;
1098
+ word-break: break-all;
1099
+ }
1100
+
1101
+ .modal-body {
1102
+ overflow-y: auto;
1103
+ padding: 20px 22px 22px;
1104
+ }
1105
+
1106
+ .modal-foot {
1107
+ display: flex;
1108
+ justify-content: flex-end;
1109
+ gap: 10px;
1110
+ padding-top: 4px;
1111
+ }
1112
+
1113
+ /* ------------------------------------------------------------ empty/loading */
1114
+
1115
+ .empty {
1116
+ padding: 28px;
1117
+ color: var(--muted);
1118
+ border: 1px dashed var(--border);
1119
+ border-radius: var(--radius);
1120
+ text-align: center;
1121
+ }
1122
+
1123
+ .empty-title {
1124
+ margin: 0 0 6px;
1125
+ color: var(--text);
1126
+ font-weight: 650;
1127
+ }
1128
+
1129
+ .auth-screen {
1130
+ min-height: 100vh;
1131
+ display: grid;
1132
+ place-items: center;
1133
+ padding: 24px;
1134
+ }
1135
+
1136
+ .auth-card {
1137
+ display: grid;
1138
+ gap: 14px;
1139
+ justify-items: start;
1140
+ max-width: 640px;
1141
+ padding: 30px;
1142
+ }
1143
+
1144
+ .auth-title {
1145
+ margin: 0;
1146
+ font-size: 19px;
1147
+ font-weight: 680;
1148
+ }
1149
+
1150
+ .auth-body {
1151
+ display: grid;
1152
+ gap: 12px;
1153
+ color: var(--text);
1154
+ font-size: 13.5px;
1155
+ line-height: 1.75;
1156
+ }
1157
+
1158
+ .loading-bar {
1159
+ width: 100%;
1160
+ max-width: 260px;
1161
+ height: 3px;
1162
+ overflow: hidden;
1163
+ background: rgba(255, 255, 255, .08);
1164
+ border-radius: 999px;
1165
+ }
1166
+
1167
+ .loading-bar span {
1168
+ display: block;
1169
+ width: 40%;
1170
+ height: 100%;
1171
+ background: linear-gradient(90deg, var(--accent), var(--cyan));
1172
+ border-radius: 999px;
1173
+ animation: loading-slide 1.2s ease-in-out infinite;
1174
+ }
1175
+
1176
+ @keyframes loading-slide {
1177
+ 0% { transform: translateX(-60%); }
1178
+ 100% { transform: translateX(260%); }
1179
+ }
1180
+
1181
+ /* -------------------------------------------------------------- scrollbars */
1182
+
1183
+ .job-log::-webkit-scrollbar,
1184
+ .modal-body::-webkit-scrollbar,
1185
+ .pre::-webkit-scrollbar,
1186
+ .scrim::-webkit-scrollbar {
1187
+ width: 10px;
1188
+ height: 10px;
1189
+ }
1190
+
1191
+ .job-log::-webkit-scrollbar-thumb,
1192
+ .modal-body::-webkit-scrollbar-thumb,
1193
+ .pre::-webkit-scrollbar-thumb,
1194
+ .scrim::-webkit-scrollbar-thumb {
1195
+ background: rgba(255, 255, 255, .14);
1196
+ background-clip: content-box;
1197
+ border: 3px solid transparent;
1198
+ border-radius: 999px;
1199
+ }
1200
+
1201
+ .job-log::-webkit-scrollbar-track,
1202
+ .modal-body::-webkit-scrollbar-track,
1203
+ .pre::-webkit-scrollbar-track,
1204
+ .scrim::-webkit-scrollbar-track {
1205
+ background: transparent;
1206
+ }
1207
+
1208
+ /* --------------------------------------------------------------- responsive */
1209
+
1210
+ @media (min-width: 861px) {
1211
+ .topbar {
1212
+ grid-template-columns: minmax(0, 1fr) auto;
1213
+ align-items: center;
1214
+ }
1215
+ }
1216
+
1217
+ @media (max-width: 860px) {
1218
+ .app {
1219
+ padding: 18px 14px 30px;
1220
+ }
1221
+
1222
+ /* One column per row: the link, modules and actions stack instead of
1223
+ squeezing into a single line. */
1224
+ .env-row-main {
1225
+ grid-template-columns: minmax(0, 1fr);
1226
+ align-items: start;
1227
+ gap: 6px;
1228
+ }
1229
+
1230
+ .env-actions {
1231
+ justify-content: flex-start;
1232
+ }
1233
+
1234
+ .env-chips {
1235
+ max-height: none;
1236
+ }
1237
+
1238
+ .job-time {
1239
+ margin-left: 0;
1240
+ }
1241
+
1242
+ .log-line {
1243
+ grid-template-columns: 56px minmax(0, 1fr);
1244
+ }
1245
+
1246
+ .scrim {
1247
+ padding: 14px;
1248
+ }
1249
+
1250
+ .modal {
1251
+ max-height: 92vh;
1252
+ }
1253
+ }
1254
+
1255
+ @media (prefers-reduced-motion: reduce) {
1256
+ *,
1257
+ *::before,
1258
+ *::after {
1259
+ animation-duration: .01ms !important;
1260
+ animation-iteration-count: 1 !important;
1261
+ transition-duration: .01ms !important;
1262
+ }
1263
+ }