api-tracer-kit 1.0.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 (47) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/LICENSE +21 -0
  3. package/README.md +466 -0
  4. package/cli/bin/api-tracer.mjs +266 -0
  5. package/cli/config.mjs +224 -0
  6. package/cli/import.mjs +231 -0
  7. package/cli/index.mjs +10 -0
  8. package/cli/presets.mjs +212 -0
  9. package/cli/report.mjs +346 -0
  10. package/cli/scan.mjs +142 -0
  11. package/cli/server.mjs +1576 -0
  12. package/cli/shape.mjs +90 -0
  13. package/cli/test.mjs +342 -0
  14. package/cli/web/app.css +1424 -0
  15. package/cli/web/app.js +2260 -0
  16. package/cli/web/favicon.svg +5 -0
  17. package/cli/web/index.html +159 -0
  18. package/cli/web/logo.svg +7 -0
  19. package/dist/axios.cjs +856 -0
  20. package/dist/axios.cjs.map +1 -0
  21. package/dist/axios.d.cts +27 -0
  22. package/dist/axios.d.ts +27 -0
  23. package/dist/axios.js +853 -0
  24. package/dist/axios.js.map +1 -0
  25. package/dist/index.cjs +872 -0
  26. package/dist/index.cjs.map +1 -0
  27. package/dist/index.d.cts +74 -0
  28. package/dist/index.d.ts +74 -0
  29. package/dist/index.js +857 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/react.cjs +896 -0
  32. package/dist/react.cjs.map +1 -0
  33. package/dist/react.d.cts +22 -0
  34. package/dist/react.d.ts +22 -0
  35. package/dist/react.js +893 -0
  36. package/dist/react.js.map +1 -0
  37. package/dist/tracer-BUWdU2lG.d.ts +76 -0
  38. package/dist/tracer-DG2YUqK0.d.cts +76 -0
  39. package/dist/types-Bl2-K6_g.d.cts +111 -0
  40. package/dist/types-Bl2-K6_g.d.ts +111 -0
  41. package/dist/ui.cjs +1162 -0
  42. package/dist/ui.cjs.map +1 -0
  43. package/dist/ui.d.cts +16 -0
  44. package/dist/ui.d.ts +16 -0
  45. package/dist/ui.js +1157 -0
  46. package/dist/ui.js.map +1 -0
  47. package/package.json +92 -0
@@ -0,0 +1,1424 @@
1
+ /* API Tracker — dark, dense, readable. Class names match app.js exactly. */
2
+
3
+ /*
4
+ * No bundled font files: shipping a typeface inside a dev-tool package is a
5
+ * licensing question nobody wants, and a system stack renders this UI just as
6
+ * well. Inter is still named first, so anyone who has it installed gets it.
7
+ */
8
+
9
+ :root {
10
+ /* surfaces, darkest first */
11
+ --bg: #0e1116;
12
+ --panel: #151920;
13
+ --panel-2: #1c212a;
14
+ --panel-3: #232936;
15
+ --line: #2a313d;
16
+ --line-soft: #21272f;
17
+
18
+ --text: #e6eaf0;
19
+ --text-dim: #b3bcca;
20
+ --muted: #7d8797;
21
+
22
+ --accent: #6ea8fe;
23
+ --accent-soft: rgba(110, 168, 254, 0.12);
24
+ --pass: #45c08a;
25
+ --pass-soft: rgba(69, 192, 138, 0.12);
26
+ --fail: #e56b7a;
27
+ --fail-soft: rgba(229, 107, 122, 0.12);
28
+ --warn: #e0a458;
29
+ --warn-soft: rgba(224, 164, 88, 0.12);
30
+
31
+ --get: #45c08a;
32
+ --post: #e0a458;
33
+ --put: #6ea8fe;
34
+ --delete: #e56b7a;
35
+
36
+ /* one spacing scale, used everywhere */
37
+ --s1: 4px;
38
+ --s2: 8px;
39
+ --s3: 12px;
40
+ --s4: 16px;
41
+ --s5: 22px;
42
+ --s6: 32px;
43
+
44
+ --r1: 6px;
45
+ --r2: 9px;
46
+ --r3: 12px;
47
+
48
+ --sans: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
49
+ --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
50
+
51
+ --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 24px rgba(0, 0, 0, 0.22);
52
+
53
+ /* editor, VS Code Dark+ */
54
+ --code-bg: #1a1f27;
55
+ --code-gutter: #545d6b;
56
+ --t-key: #9cdcfe;
57
+ --t-str: #ce9178;
58
+ --t-num: #b5cea8;
59
+ --t-lit: #569cd6;
60
+ --t-punct: #c8cdd6;
61
+
62
+ /* both editor layers must agree exactly or the caret drifts off the glyphs */
63
+ --code-size: 12.5px;
64
+ --code-line: 1.7;
65
+ --code-pad: 10px;
66
+ }
67
+
68
+ * {
69
+ box-sizing: border-box;
70
+ }
71
+
72
+ body {
73
+ margin: 0;
74
+ height: 100vh;
75
+ display: flex;
76
+ flex-direction: column;
77
+ background: var(--bg);
78
+ color: var(--text);
79
+ font-family: var(--sans);
80
+ font-size: 13px;
81
+ line-height: 1.55;
82
+ -webkit-font-smoothing: antialiased;
83
+ text-rendering: optimizeLegibility;
84
+ }
85
+
86
+ code,
87
+ pre,
88
+ input,
89
+ textarea,
90
+ select,
91
+ .mono {
92
+ font-family: var(--mono);
93
+ font-variant-ligatures: none;
94
+ }
95
+
96
+ code {
97
+ background: var(--panel-3);
98
+ border-radius: 4px;
99
+ padding: 1px 5px;
100
+ font-size: 0.92em;
101
+ color: var(--text-dim);
102
+ }
103
+
104
+ b,
105
+ strong {
106
+ font-weight: 600;
107
+ }
108
+
109
+ .muted {
110
+ color: var(--muted);
111
+ }
112
+ .spacer {
113
+ flex: 1;
114
+ }
115
+ .inline {
116
+ display: inline-flex;
117
+ align-items: center;
118
+ gap: var(--s2);
119
+ color: var(--muted);
120
+ }
121
+
122
+ ::-webkit-scrollbar {
123
+ width: 10px;
124
+ height: 10px;
125
+ }
126
+ ::-webkit-scrollbar-thumb {
127
+ background: var(--panel-3);
128
+ border-radius: 6px;
129
+ border: 2px solid var(--panel);
130
+ }
131
+ ::-webkit-scrollbar-thumb:hover {
132
+ background: #333c4b;
133
+ }
134
+
135
+ /* ------------------------------------------------------------------ chrome */
136
+
137
+ header {
138
+ display: flex;
139
+ align-items: center;
140
+ gap: var(--s4);
141
+ padding: var(--s3) var(--s5);
142
+ border-bottom: 1px solid var(--line);
143
+ background: var(--panel);
144
+ flex-wrap: wrap;
145
+ }
146
+
147
+ .brand {
148
+ background: none;
149
+ border: none;
150
+ border-radius: var(--r1);
151
+ padding: var(--s1) var(--s2) var(--s1) 0;
152
+ cursor: pointer;
153
+ text-align: left;
154
+ display: flex;
155
+ align-items: center;
156
+ gap: var(--s3);
157
+ padding-right: var(--s4);
158
+ border-right: 1px solid var(--line);
159
+ }
160
+ .brand .logo {
161
+ /* the app's own wordmark (public/images/conx.svg, the white-ink variant --
162
+ conx-dark.svg is dark ink, meant for light backgrounds) */
163
+ height: 26px;
164
+ width: auto;
165
+ display: block;
166
+ }
167
+ .brand strong {
168
+ display: block;
169
+ font-size: 14px;
170
+ letter-spacing: -0.01em;
171
+ line-height: 1.25;
172
+ }
173
+ .brand span {
174
+ display: block;
175
+ font-size: 11px;
176
+ line-height: 1.3;
177
+ }
178
+
179
+ main {
180
+ flex: 1;
181
+ display: flex;
182
+ min-height: 0;
183
+ }
184
+
185
+ aside {
186
+ width: 312px;
187
+ flex: none;
188
+ border-right: 1px solid var(--line);
189
+ background: var(--panel);
190
+ display: flex;
191
+ flex-direction: column;
192
+ min-height: 0;
193
+ }
194
+
195
+ aside > #search {
196
+ margin: var(--s3) var(--s3) var(--s2);
197
+ }
198
+
199
+ .filters {
200
+ display: flex;
201
+ gap: var(--s2);
202
+ padding: 0 var(--s3) var(--s3);
203
+ }
204
+ .filters select {
205
+ flex: 1;
206
+ min-width: 0;
207
+ }
208
+
209
+ #tree {
210
+ flex: 1;
211
+ overflow: auto;
212
+ padding: var(--s1) 0 var(--s6);
213
+ }
214
+
215
+ #panel {
216
+ flex: 1;
217
+ overflow: auto;
218
+ padding: var(--s5) var(--s6) var(--s6);
219
+ min-width: 0;
220
+ }
221
+
222
+ /* ------------------------------------------------------------------ inputs */
223
+
224
+ input,
225
+ select,
226
+ textarea,
227
+ button {
228
+ background: var(--panel-2);
229
+ color: var(--text);
230
+ border: 1px solid var(--line);
231
+ border-radius: var(--r1);
232
+ padding: 6px 10px;
233
+ font-size: 12px;
234
+ font-family: inherit;
235
+ line-height: 1.5;
236
+ transition: border-color 0.12s ease, background 0.12s ease, color 0.12s ease;
237
+ }
238
+
239
+ input::placeholder,
240
+ textarea::placeholder {
241
+ color: #5d6674;
242
+ }
243
+
244
+ input:focus,
245
+ textarea:focus,
246
+ select:focus {
247
+ outline: none;
248
+ border-color: var(--accent);
249
+ box-shadow: 0 0 0 3px var(--accent-soft);
250
+ }
251
+
252
+ select {
253
+ appearance: none;
254
+ padding-right: 26px;
255
+ background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
256
+ linear-gradient(135deg, var(--muted) 50%, transparent 50%);
257
+ background-position: calc(100% - 14px) 52%, calc(100% - 9px) 52%;
258
+ background-size: 5px 5px, 5px 5px;
259
+ background-repeat: no-repeat;
260
+ }
261
+
262
+ input[type="checkbox"],
263
+ input[type="radio"] {
264
+ accent-color: var(--accent);
265
+ width: 13px;
266
+ height: 13px;
267
+ }
268
+
269
+ button {
270
+ cursor: pointer;
271
+ font-weight: 500;
272
+ }
273
+ button:hover:not(:disabled) {
274
+ border-color: #3c4655;
275
+ background: var(--panel-3);
276
+ }
277
+ button:disabled {
278
+ opacity: 0.4;
279
+ cursor: not-allowed;
280
+ }
281
+ button:focus-visible {
282
+ outline: none;
283
+ border-color: var(--accent);
284
+ box-shadow: 0 0 0 3px var(--accent-soft);
285
+ }
286
+
287
+ button.primary {
288
+ background: var(--accent);
289
+ color: #0b0f16;
290
+ border-color: var(--accent);
291
+ font-weight: 600;
292
+ padding: 6px 18px;
293
+ }
294
+ button.primary:hover:not(:disabled) {
295
+ background: #8ab9ff;
296
+ border-color: #8ab9ff;
297
+ }
298
+
299
+ button.link {
300
+ background: none;
301
+ border: none;
302
+ color: var(--accent);
303
+ padding: 2px 4px;
304
+ font-weight: 500;
305
+ }
306
+ button.link:hover:not(:disabled) {
307
+ background: none;
308
+ color: #9cc4ff;
309
+ text-decoration: underline;
310
+ }
311
+
312
+ /* ------------------------------------------------------------------ health */
313
+
314
+ .health {
315
+ display: flex;
316
+ gap: var(--s3);
317
+ align-items: center;
318
+ font-size: 12px;
319
+ color: var(--text-dim);
320
+ }
321
+ .bar {
322
+ width: 130px;
323
+ height: 6px;
324
+ border-radius: 999px;
325
+ background: var(--panel-3);
326
+ overflow: hidden;
327
+ display: flex;
328
+ }
329
+ .bar i {
330
+ display: block;
331
+ height: 100%;
332
+ }
333
+ .bar .p {
334
+ background: var(--pass);
335
+ }
336
+ .bar .f {
337
+ background: var(--fail);
338
+ }
339
+
340
+ #live-btn.on {
341
+ border-color: rgba(69, 192, 138, 0.45);
342
+ background: var(--pass-soft);
343
+ color: var(--pass);
344
+ }
345
+ #live-btn.on::before {
346
+ content: "\25CF";
347
+ margin-right: var(--s2);
348
+ animation: pulse 1.6s ease-in-out infinite;
349
+ }
350
+ @keyframes pulse {
351
+ 50% {
352
+ opacity: 0.25;
353
+ }
354
+ }
355
+
356
+ /* -------------------------------------------------------------------- tree */
357
+
358
+ .module,
359
+ .endpoint {
360
+ width: 100%;
361
+ background: none;
362
+ border: none;
363
+ border-radius: 0;
364
+ text-align: left;
365
+ display: flex;
366
+ align-items: center;
367
+ gap: var(--s2);
368
+ cursor: pointer;
369
+ transition: background 0.1s ease;
370
+ }
371
+ .module:hover,
372
+ .endpoint:hover {
373
+ background: var(--panel-2);
374
+ border-color: transparent;
375
+ }
376
+ .module:focus-visible,
377
+ .endpoint:focus-visible {
378
+ outline: none;
379
+ box-shadow: inset 0 0 0 1px var(--accent);
380
+ }
381
+
382
+ .module {
383
+ padding: var(--s2) var(--s4);
384
+ color: var(--text-dim);
385
+ font-weight: 600;
386
+ font-size: 12.5px;
387
+ }
388
+ .module.active {
389
+ color: var(--text);
390
+ background: var(--panel-2);
391
+ }
392
+ .module.odd {
393
+ color: var(--warn);
394
+ }
395
+ .module .n {
396
+ margin-left: auto;
397
+ font-size: 10.5px;
398
+ font-weight: 500;
399
+ color: var(--muted);
400
+ padding-left: var(--s2);
401
+ }
402
+ .module .n.pass {
403
+ color: var(--pass);
404
+ }
405
+ .module .n.fail {
406
+ color: var(--fail);
407
+ }
408
+
409
+ .endpoint {
410
+ padding: 5px var(--s4) 5px var(--s5);
411
+ border-left: 2px solid transparent;
412
+ }
413
+ .endpoint.active {
414
+ background: var(--accent-soft);
415
+ border-left-color: var(--accent);
416
+ }
417
+ .endpoint .nm {
418
+ font-family: var(--mono);
419
+ font-size: 11.5px;
420
+ overflow: hidden;
421
+ text-overflow: ellipsis;
422
+ white-space: nowrap;
423
+ color: var(--text-dim);
424
+ }
425
+ .endpoint.active .nm {
426
+ color: var(--text);
427
+ }
428
+ .tick {
429
+ color: var(--pass);
430
+ font-size: 7px;
431
+ margin-left: auto;
432
+ padding-left: var(--s1);
433
+ }
434
+
435
+ .method {
436
+ font-size: 9.5px;
437
+ font-weight: 700;
438
+ width: 46px;
439
+ flex: none;
440
+ text-align: right;
441
+ letter-spacing: 0.05em;
442
+ font-family: var(--sans);
443
+ }
444
+ .m-GET {
445
+ color: var(--get);
446
+ }
447
+ .m-POST {
448
+ color: var(--post);
449
+ }
450
+ .m-PUT,
451
+ .m-PATCH {
452
+ color: var(--put);
453
+ }
454
+ .m-DELETE {
455
+ color: var(--delete);
456
+ }
457
+
458
+ .dot {
459
+ width: 7px;
460
+ height: 7px;
461
+ border-radius: 50%;
462
+ flex: none;
463
+ background: #39414f;
464
+ }
465
+ .dot.pass {
466
+ background: var(--pass);
467
+ }
468
+ .dot.fail {
469
+ background: var(--fail);
470
+ }
471
+ .dot.drift {
472
+ background: var(--warn);
473
+ }
474
+ .dot.regressed {
475
+ box-shadow: 0 0 0 2px var(--bg), 0 0 0 3px var(--warn);
476
+ }
477
+
478
+ .crumbs {
479
+ display: flex;
480
+ align-items: center;
481
+ gap: var(--s2);
482
+ font-size: 12px;
483
+ color: var(--muted);
484
+ margin-bottom: var(--s3);
485
+ }
486
+ .crumbs .here {
487
+ color: var(--text-dim);
488
+ font-family: var(--mono);
489
+ font-size: 11.5px;
490
+ }
491
+ .crumbs button.link {
492
+ padding: 0;
493
+ }
494
+
495
+ /* ------------------------------------------------------------------ detail */
496
+
497
+ h1 {
498
+ margin: 0 0 var(--s2);
499
+ font-size: 19px;
500
+ font-weight: 600;
501
+ letter-spacing: -0.015em;
502
+ font-family: var(--mono);
503
+ word-break: break-word;
504
+ display: flex;
505
+ align-items: baseline;
506
+ gap: var(--s3);
507
+ }
508
+ h1 .method {
509
+ width: auto;
510
+ font-size: 11px;
511
+ }
512
+ h2 {
513
+ margin: 0 0 var(--s4);
514
+ font-size: 15px;
515
+ font-weight: 600;
516
+ }
517
+
518
+ .sub {
519
+ color: var(--muted);
520
+ display: flex;
521
+ gap: var(--s2);
522
+ align-items: center;
523
+ flex-wrap: wrap;
524
+ margin-bottom: var(--s4);
525
+ font-size: 12px;
526
+ row-gap: var(--s2);
527
+ }
528
+
529
+ .badge {
530
+ display: inline-flex;
531
+ align-items: center;
532
+ border: 1px solid var(--line);
533
+ background: var(--panel-2);
534
+ border-radius: 999px;
535
+ padding: 2px 10px;
536
+ font-size: 11px;
537
+ line-height: 1.6;
538
+ white-space: nowrap;
539
+ color: var(--text-dim);
540
+ }
541
+ .badge.warn {
542
+ border-color: rgba(224, 164, 88, 0.4);
543
+ background: var(--warn-soft);
544
+ color: var(--warn);
545
+ }
546
+ .badge.pass {
547
+ border-color: rgba(69, 192, 138, 0.4);
548
+ background: var(--pass-soft);
549
+ color: var(--pass);
550
+ }
551
+ .badge.fail {
552
+ border-color: rgba(229, 107, 122, 0.4);
553
+ background: var(--fail-soft);
554
+ color: var(--fail);
555
+ }
556
+
557
+ .urlbar {
558
+ display: flex;
559
+ gap: var(--s3);
560
+ align-items: baseline;
561
+ background: var(--panel);
562
+ border: 1px solid var(--line);
563
+ border-radius: var(--r2);
564
+ padding: var(--s3) var(--s4);
565
+ margin-bottom: var(--s3);
566
+ font-size: 12px;
567
+ word-break: break-all;
568
+ line-height: 1.6;
569
+ }
570
+
571
+ .row {
572
+ display: flex;
573
+ gap: var(--s2);
574
+ align-items: center;
575
+ margin-bottom: var(--s3);
576
+ flex-wrap: wrap;
577
+ }
578
+ .row.end {
579
+ justify-content: flex-end;
580
+ margin-bottom: 0;
581
+ margin-top: var(--s4);
582
+ }
583
+
584
+ fieldset {
585
+ border: 1px solid var(--line);
586
+ border-radius: var(--r2);
587
+ padding: var(--s3) var(--s4) var(--s4);
588
+ margin: 0 0 var(--s3);
589
+ }
590
+ legend {
591
+ color: var(--muted);
592
+ padding: 0 var(--s2);
593
+ font-size: 10px;
594
+ font-weight: 600;
595
+ text-transform: uppercase;
596
+ letter-spacing: 0.09em;
597
+ }
598
+
599
+ .holes {
600
+ display: flex;
601
+ gap: var(--s4);
602
+ flex-wrap: wrap;
603
+ row-gap: var(--s2);
604
+ }
605
+ .holes label {
606
+ display: flex;
607
+ gap: var(--s2);
608
+ align-items: center;
609
+ color: var(--muted);
610
+ font-size: 12px;
611
+ }
612
+
613
+ textarea {
614
+ width: 100%;
615
+ min-height: 140px;
616
+ resize: vertical;
617
+ line-height: 1.6;
618
+ padding: var(--s3);
619
+ }
620
+ textarea.invalid {
621
+ border-color: var(--fail);
622
+ }
623
+
624
+ .editor-bar {
625
+ display: flex;
626
+ gap: var(--s2);
627
+ align-items: center;
628
+ margin-bottom: var(--s2);
629
+ color: var(--muted);
630
+ font-size: 11px;
631
+ flex-wrap: wrap;
632
+ row-gap: var(--s2);
633
+ }
634
+ .editor-bar .err {
635
+ color: var(--fail);
636
+ }
637
+
638
+ /* ----------------------------------------------------------- request tabs */
639
+
640
+ .reqtabs {
641
+ display: flex;
642
+ gap: var(--s1);
643
+ align-items: center;
644
+ border-bottom: 1px solid var(--line);
645
+ padding: 0 var(--s1);
646
+ }
647
+ .reqtabs button {
648
+ background: none;
649
+ border: none;
650
+ border-bottom: 2px solid transparent;
651
+ border-radius: 0;
652
+ padding: var(--s2) var(--s3);
653
+ color: var(--muted);
654
+ font-size: 12px;
655
+ font-weight: 500;
656
+ margin-bottom: -1px;
657
+ }
658
+ .reqtabs button:hover:not(:disabled) {
659
+ color: var(--text);
660
+ background: none;
661
+ }
662
+ .reqtabs button.on {
663
+ color: var(--accent);
664
+ border-bottom-color: var(--accent);
665
+ }
666
+ .reqtabs b {
667
+ color: var(--text);
668
+ font-weight: 600;
669
+ margin-left: var(--s1);
670
+ }
671
+ .reqtabs > .muted {
672
+ font-size: 11px;
673
+ font-family: var(--mono);
674
+ padding-right: var(--s2);
675
+ }
676
+ .dotmark {
677
+ display: inline-block;
678
+ width: 6px;
679
+ height: 6px;
680
+ border-radius: 50%;
681
+ background: var(--pass);
682
+ margin-left: var(--s1);
683
+ vertical-align: middle;
684
+ }
685
+
686
+ .reqpane {
687
+ border: 1px solid var(--line);
688
+ border-top: none;
689
+ border-radius: 0 0 var(--r2) var(--r2);
690
+ padding: var(--s4);
691
+ margin-bottom: var(--s4);
692
+ min-height: 132px;
693
+ background: var(--panel);
694
+ }
695
+ .reqpane > p:last-child {
696
+ margin-bottom: 0;
697
+ }
698
+
699
+ .bodytypes {
700
+ gap: var(--s2);
701
+ margin-bottom: var(--s3);
702
+ }
703
+ .radio {
704
+ display: inline-flex;
705
+ align-items: center;
706
+ gap: var(--s2);
707
+ padding: 4px 12px;
708
+ border: 1px solid var(--line);
709
+ border-radius: 999px;
710
+ cursor: pointer;
711
+ color: var(--muted);
712
+ font-size: 11px;
713
+ background: var(--panel-2);
714
+ }
715
+ .radio:hover {
716
+ color: var(--text-dim);
717
+ }
718
+ .radio.on {
719
+ border-color: var(--accent);
720
+ background: var(--accent-soft);
721
+ color: var(--accent);
722
+ }
723
+ .radio input {
724
+ margin: 0;
725
+ }
726
+
727
+ table.kv {
728
+ width: 100%;
729
+ border-collapse: separate;
730
+ border-spacing: 0 var(--s1);
731
+ }
732
+ table.kv td {
733
+ padding: 0 var(--s2) 0 0;
734
+ }
735
+ table.kv input:not([type="checkbox"]) {
736
+ width: 100%;
737
+ }
738
+ table.kv td.k {
739
+ width: 34%;
740
+ }
741
+ table.kv td.x {
742
+ width: 28px;
743
+ text-align: center;
744
+ }
745
+
746
+ /* ------------------------------------------------------------------ editor */
747
+
748
+ /*
749
+ * A textarea cannot colour its own text, so a highlighted <pre> sits behind a
750
+ * transparent textarea. Font, size, padding and line-height are shared through
751
+ * variables because any mismatch shows up as a caret sliding off the glyphs.
752
+ */
753
+ .code-edit {
754
+ position: relative;
755
+ display: flex;
756
+ background: var(--code-bg);
757
+ border: 1px solid var(--line);
758
+ border-radius: var(--r1);
759
+ overflow: hidden;
760
+ }
761
+ .code-edit:focus-within {
762
+ border-color: var(--accent);
763
+ box-shadow: 0 0 0 3px var(--accent-soft);
764
+ }
765
+
766
+ .code-edit .gutter,
767
+ .code-edit .hl,
768
+ .code-edit textarea {
769
+ font-family: var(--mono);
770
+ font-size: var(--code-size);
771
+ line-height: var(--code-line);
772
+ padding: var(--code-pad) 0;
773
+ margin: 0;
774
+ border: 0;
775
+ tab-size: 2;
776
+ }
777
+
778
+ .code-edit .gutter {
779
+ flex: none;
780
+ width: 42px;
781
+ padding-right: var(--s3);
782
+ text-align: right;
783
+ color: var(--code-gutter);
784
+ background: var(--code-bg);
785
+ border-right: 1px solid var(--line-soft);
786
+ overflow: hidden;
787
+ user-select: none;
788
+ z-index: 2;
789
+ }
790
+
791
+ .code-edit .hl,
792
+ .code-edit textarea {
793
+ padding-left: var(--s3);
794
+ padding-right: var(--s3);
795
+ white-space: pre;
796
+ overflow: auto;
797
+ word-wrap: normal;
798
+ }
799
+
800
+ .code-edit .hl {
801
+ position: absolute;
802
+ inset: 0 0 0 42px;
803
+ color: var(--t-punct);
804
+ pointer-events: none;
805
+ overflow: hidden;
806
+ z-index: 0;
807
+ }
808
+
809
+ .code-edit textarea {
810
+ position: relative;
811
+ flex: 1;
812
+ min-width: 0;
813
+ min-height: 0;
814
+ background: transparent;
815
+ color: transparent;
816
+ caret-color: var(--text);
817
+ resize: vertical;
818
+ z-index: 1;
819
+ }
820
+ .code-edit textarea:focus {
821
+ outline: none;
822
+ box-shadow: none;
823
+ }
824
+ .code-edit textarea::selection {
825
+ background: rgba(110, 168, 254, 0.32);
826
+ }
827
+ .code-edit textarea.invalid {
828
+ border: 0;
829
+ }
830
+ .code-edit:has(textarea.invalid) {
831
+ border-color: var(--fail);
832
+ }
833
+
834
+ .t-key {
835
+ color: var(--t-key);
836
+ }
837
+ .t-str {
838
+ color: var(--t-str);
839
+ }
840
+ .t-num {
841
+ color: var(--t-num);
842
+ }
843
+ .t-lit {
844
+ color: var(--t-lit);
845
+ }
846
+
847
+ /* ---------------------------------------------------------------- response */
848
+
849
+ .status {
850
+ display: flex;
851
+ gap: var(--s4);
852
+ align-items: center;
853
+ margin-bottom: var(--s3);
854
+ flex-wrap: wrap;
855
+ row-gap: var(--s2);
856
+ }
857
+ .status .code {
858
+ font-weight: 700;
859
+ font-size: 16px;
860
+ font-family: var(--mono);
861
+ }
862
+ .pass {
863
+ color: var(--pass);
864
+ }
865
+ .fail {
866
+ color: var(--fail);
867
+ }
868
+ .warn {
869
+ color: var(--warn);
870
+ }
871
+
872
+ pre.body {
873
+ background: var(--code-bg);
874
+ border: 1px solid var(--line);
875
+ border-radius: var(--r2);
876
+ padding: var(--s4);
877
+ max-height: 440px;
878
+ overflow: auto;
879
+ margin: 0;
880
+ white-space: pre-wrap;
881
+ word-break: break-word;
882
+ font-size: var(--code-size);
883
+ line-height: var(--code-line);
884
+ color: var(--t-punct);
885
+ }
886
+
887
+ .tabs {
888
+ display: flex;
889
+ gap: var(--s1);
890
+ }
891
+ .tabs button.on {
892
+ color: var(--accent);
893
+ border-color: var(--accent);
894
+ background: var(--accent-soft);
895
+ }
896
+
897
+ .drift-box,
898
+ .hint {
899
+ border-radius: var(--r2);
900
+ padding: var(--s3) var(--s4);
901
+ margin-bottom: var(--s3);
902
+ font-size: 12px;
903
+ line-height: 1.6;
904
+ }
905
+ .drift-box {
906
+ border: 1px solid rgba(224, 164, 88, 0.45);
907
+ background: var(--warn-soft);
908
+ }
909
+ .drift-box .mono {
910
+ color: var(--text);
911
+ }
912
+ .hint {
913
+ border: 1px solid rgba(110, 168, 254, 0.45);
914
+ background: var(--accent-soft);
915
+ display: flex;
916
+ gap: var(--s2);
917
+ align-items: center;
918
+ flex-wrap: wrap;
919
+ }
920
+
921
+ /* ---------------------------------------------------------------- overview */
922
+
923
+ table.list {
924
+ width: 100%;
925
+ border-collapse: collapse;
926
+ font-size: 12px;
927
+ }
928
+ table.list th {
929
+ text-align: left;
930
+ color: var(--muted);
931
+ font-weight: 600;
932
+ border-bottom: 1px solid var(--line);
933
+ padding: var(--s2) var(--s3);
934
+ font-size: 10px;
935
+ text-transform: uppercase;
936
+ letter-spacing: 0.09em;
937
+ }
938
+ table.list td {
939
+ padding: var(--s2) var(--s3);
940
+ border-bottom: 1px solid var(--line-soft);
941
+ vertical-align: middle;
942
+ }
943
+ table.list tr {
944
+ cursor: pointer;
945
+ }
946
+ table.list tbody tr:hover td {
947
+ background: var(--panel-2);
948
+ }
949
+ table.list td.path {
950
+ font-family: var(--mono);
951
+ color: var(--muted);
952
+ word-break: break-all;
953
+ }
954
+ table.list td .badge {
955
+ margin-left: var(--s2);
956
+ }
957
+
958
+ .empty {
959
+ color: var(--text-dim);
960
+ text-align: center;
961
+ margin-top: 12vh;
962
+ max-width: 720px;
963
+ margin-left: auto;
964
+ margin-right: auto;
965
+ }
966
+ .empty h2 {
967
+ color: var(--text);
968
+ font-size: 20px;
969
+ letter-spacing: -0.015em;
970
+ }
971
+ .empty p {
972
+ margin: var(--s3) 0;
973
+ }
974
+
975
+ .coverage {
976
+ margin-top: var(--s6);
977
+ padding-top: var(--s5);
978
+ border-top: 1px solid var(--line);
979
+ }
980
+ .coverage p {
981
+ margin: var(--s2) 0;
982
+ }
983
+ .coverage .bar {
984
+ margin: 0 auto var(--s3);
985
+ height: 8px;
986
+ width: 240px;
987
+ }
988
+
989
+ .report {
990
+ border: 1px solid var(--line);
991
+ border-radius: var(--r2);
992
+ padding: var(--s3) var(--s4);
993
+ margin: var(--s3) 0 0;
994
+ font-size: 12px;
995
+ background: var(--panel-2);
996
+ }
997
+ .report ul {
998
+ margin: var(--s2) 0 0;
999
+ padding-left: var(--s5);
1000
+ max-height: 150px;
1001
+ overflow: auto;
1002
+ }
1003
+ .report li {
1004
+ margin: 2px 0;
1005
+ }
1006
+
1007
+ /* ------------------------------------------------------------------ report */
1008
+
1009
+ .cards {
1010
+ display: grid;
1011
+ grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
1012
+ gap: var(--s2);
1013
+ margin-bottom: var(--s5);
1014
+ }
1015
+ .card {
1016
+ border: 1px solid var(--line);
1017
+ border-radius: var(--r2);
1018
+ padding: var(--s3) var(--s4);
1019
+ background: var(--panel);
1020
+ }
1021
+ .card b {
1022
+ display: block;
1023
+ font-size: 22px;
1024
+ font-weight: 600;
1025
+ line-height: 1.2;
1026
+ letter-spacing: -0.02em;
1027
+ }
1028
+ .card b small {
1029
+ font-size: 12px;
1030
+ font-weight: 500;
1031
+ color: var(--muted);
1032
+ margin-left: var(--s1);
1033
+ }
1034
+ .card span {
1035
+ font-size: 11px;
1036
+ color: var(--muted);
1037
+ }
1038
+
1039
+ details.rsec {
1040
+ border: 1px solid var(--line);
1041
+ border-radius: var(--r2);
1042
+ margin-bottom: var(--s3);
1043
+ background: var(--panel);
1044
+ }
1045
+ details.rsec > summary {
1046
+ cursor: pointer;
1047
+ padding: var(--s3) var(--s4);
1048
+ font-weight: 600;
1049
+ font-size: 13px;
1050
+ list-style: none;
1051
+ }
1052
+ details.rsec > summary::-webkit-details-marker {
1053
+ display: none;
1054
+ }
1055
+ details.rsec > summary::before {
1056
+ content: "\25B8";
1057
+ color: var(--muted);
1058
+ margin-right: var(--s2);
1059
+ }
1060
+ details.rsec[open] > summary::before {
1061
+ content: "\25BE";
1062
+ }
1063
+ details.rsec > summary b {
1064
+ color: var(--muted);
1065
+ font-weight: 500;
1066
+ margin-left: var(--s1);
1067
+ }
1068
+ .rsec-body {
1069
+ padding: 0 var(--s4) var(--s4);
1070
+ overflow-x: auto;
1071
+ }
1072
+ .rsec-body > p:first-child {
1073
+ margin-top: 0;
1074
+ }
1075
+
1076
+ table.list td.msg {
1077
+ max-width: 460px;
1078
+ }
1079
+
1080
+ dl.rlist {
1081
+ margin: 0;
1082
+ }
1083
+ dl.rlist dt {
1084
+ font-size: 12px;
1085
+ color: var(--text-dim);
1086
+ margin-top: var(--s3);
1087
+ }
1088
+ dl.rlist dt b {
1089
+ color: var(--muted);
1090
+ font-weight: 500;
1091
+ }
1092
+ dl.rlist dd {
1093
+ margin: var(--s1) 0 0;
1094
+ font-size: 11.5px;
1095
+ line-height: 1.9;
1096
+ }
1097
+
1098
+ ul.plain {
1099
+ list-style: none;
1100
+ margin: 0;
1101
+ padding: 0;
1102
+ font-size: 12px;
1103
+ }
1104
+ ul.plain li {
1105
+ padding: 3px 0;
1106
+ display: flex;
1107
+ gap: var(--s2);
1108
+ align-items: baseline;
1109
+ flex-wrap: wrap;
1110
+ }
1111
+
1112
+ .rsec-body button.link.mono {
1113
+ font-family: var(--mono);
1114
+ font-size: 11.5px;
1115
+ }
1116
+ .rsec-body .bar {
1117
+ height: 6px;
1118
+ }
1119
+
1120
+ /* ----------------------------------------------------------------- dialogs */
1121
+
1122
+ dialog {
1123
+ background: var(--panel);
1124
+ color: var(--text);
1125
+ border: 1px solid var(--line);
1126
+ border-radius: var(--r3);
1127
+ padding: var(--s5) var(--s6);
1128
+ width: min(580px, 92vw);
1129
+ box-shadow: var(--shadow);
1130
+ }
1131
+ dialog::backdrop {
1132
+ background: rgba(6, 8, 11, 0.72);
1133
+ }
1134
+ dialog h3 {
1135
+ margin: 0 0 var(--s3);
1136
+ font-size: 16px;
1137
+ font-weight: 600;
1138
+ }
1139
+ dialog p {
1140
+ margin: var(--s3) 0;
1141
+ font-size: 12px;
1142
+ line-height: 1.65;
1143
+ }
1144
+ dialog textarea {
1145
+ min-height: 84px;
1146
+ margin: var(--s2) 0;
1147
+ }
1148
+ dialog .row {
1149
+ margin-bottom: 0;
1150
+ }
1151
+
1152
+ .login-form {
1153
+ display: flex;
1154
+ flex-direction: column;
1155
+ gap: var(--s3);
1156
+ margin: var(--s4) 0;
1157
+ }
1158
+ .login-form label {
1159
+ display: flex;
1160
+ flex-direction: column;
1161
+ gap: var(--s1);
1162
+ font-size: 12px;
1163
+ color: var(--muted);
1164
+ }
1165
+ .login-form input,
1166
+ .login-form select {
1167
+ font-family: var(--sans);
1168
+ font-size: 13px;
1169
+ padding: 8px 10px;
1170
+ }
1171
+ .login-form .err {
1172
+ color: var(--fail);
1173
+ font-size: 12px;
1174
+ }
1175
+
1176
+ #paste-fallback {
1177
+ border-top: 1px solid var(--line);
1178
+ padding-top: var(--s3);
1179
+ margin-top: var(--s4);
1180
+ }
1181
+ #paste-fallback summary {
1182
+ cursor: pointer;
1183
+ font-size: 12px;
1184
+ list-style: none;
1185
+ }
1186
+ #paste-fallback summary::-webkit-details-marker {
1187
+ display: none;
1188
+ }
1189
+ #paste-fallback summary::before {
1190
+ content: "\25B8";
1191
+ margin-right: var(--s2);
1192
+ }
1193
+ #paste-fallback[open] summary::before {
1194
+ content: "\25BE";
1195
+ }
1196
+ .paste-body {
1197
+ padding-top: var(--s2);
1198
+ }
1199
+
1200
+ .steps {
1201
+ padding-left: var(--s5);
1202
+ margin: var(--s3) 0;
1203
+ font-size: 12px;
1204
+ line-height: 1.7;
1205
+ }
1206
+ .steps li {
1207
+ margin: var(--s1) 0;
1208
+ }
1209
+
1210
+ .snippet {
1211
+ background: var(--panel-3);
1212
+ border: 1px solid var(--line);
1213
+ border-radius: var(--r1);
1214
+ padding: var(--s3);
1215
+ font-size: 11px;
1216
+ overflow-x: auto;
1217
+ margin: var(--s2) 0 0;
1218
+ line-height: 1.6;
1219
+ }
1220
+
1221
+ .opt {
1222
+ display: flex;
1223
+ align-items: center;
1224
+ gap: var(--s2);
1225
+ margin: var(--s2) 0;
1226
+ font-size: 12px;
1227
+ cursor: pointer;
1228
+ }
1229
+ .report .badge {
1230
+ margin-right: var(--s1);
1231
+ }
1232
+ .report ul code {
1233
+ font-size: 11px;
1234
+ }
1235
+
1236
+ .lastrun {
1237
+ margin-top: var(--s5);
1238
+ border: 1px solid var(--line);
1239
+ border-radius: var(--r2);
1240
+ padding: var(--s3) var(--s4);
1241
+ text-align: left;
1242
+ background: var(--panel);
1243
+ }
1244
+ .lastrun-head {
1245
+ display: flex;
1246
+ align-items: center;
1247
+ gap: var(--s3);
1248
+ font-size: 12px;
1249
+ padding-bottom: var(--s2);
1250
+ border-bottom: 1px solid var(--line-soft);
1251
+ flex-wrap: wrap;
1252
+ }
1253
+ .lastrun ul.ran {
1254
+ margin-top: var(--s1);
1255
+ max-height: 260px;
1256
+ }
1257
+
1258
+ /* ------------------------------------------------------------ run progress */
1259
+
1260
+ .progress-head {
1261
+ display: flex;
1262
+ align-items: center;
1263
+ gap: var(--s3);
1264
+ font-size: 12px;
1265
+ margin-bottom: var(--s2);
1266
+ }
1267
+ .progress .bar {
1268
+ width: 100%;
1269
+ height: 8px;
1270
+ margin-bottom: var(--s3);
1271
+ }
1272
+ .progress-now {
1273
+ display: flex;
1274
+ align-items: center;
1275
+ gap: var(--s2);
1276
+ font-size: 12px;
1277
+ padding: var(--s2) var(--s3);
1278
+ background: var(--panel-2);
1279
+ border-radius: var(--r1);
1280
+ min-height: 34px;
1281
+ }
1282
+ .progress-now .mono {
1283
+ font-size: 11.5px;
1284
+ overflow: hidden;
1285
+ text-overflow: ellipsis;
1286
+ white-space: nowrap;
1287
+ }
1288
+
1289
+ .spin {
1290
+ width: 11px;
1291
+ height: 11px;
1292
+ flex: none;
1293
+ border: 2px solid var(--line);
1294
+ border-top-color: var(--accent);
1295
+ border-radius: 50%;
1296
+ animation: spin 0.7s linear infinite;
1297
+ }
1298
+ @keyframes spin {
1299
+ to {
1300
+ transform: rotate(360deg);
1301
+ }
1302
+ }
1303
+
1304
+ ul.ran {
1305
+ list-style: none;
1306
+ margin: var(--s3) 0 0;
1307
+ padding: 0;
1308
+ max-height: 190px;
1309
+ overflow: auto;
1310
+ font-size: 11.5px;
1311
+ }
1312
+ ul.ran li {
1313
+ border-bottom: 1px solid var(--line-soft);
1314
+ }
1315
+ ul.ran li:last-child {
1316
+ border-bottom: none;
1317
+ }
1318
+
1319
+ .ran-row {
1320
+ display: flex;
1321
+ align-items: center;
1322
+ gap: var(--s2);
1323
+ width: 100%;
1324
+ padding: 4px var(--s2);
1325
+ background: none;
1326
+ border: none;
1327
+ border-radius: var(--r1);
1328
+ text-align: left;
1329
+ font-size: 11.5px;
1330
+ color: var(--text-dim);
1331
+ }
1332
+ .ran-row:hover:not(:disabled) {
1333
+ background: var(--panel-3);
1334
+ border: none;
1335
+ }
1336
+ .ran-row .mono {
1337
+ overflow: hidden;
1338
+ text-overflow: ellipsis;
1339
+ white-space: nowrap;
1340
+ }
1341
+ .ran-row.pass .outcome {
1342
+ color: var(--pass);
1343
+ }
1344
+ .ran-row.fail .outcome {
1345
+ color: var(--fail);
1346
+ }
1347
+ .ran-row .go {
1348
+ color: var(--muted);
1349
+ font-size: 14px;
1350
+ line-height: 1;
1351
+ opacity: 0;
1352
+ }
1353
+ .ran-row:hover .go {
1354
+ opacity: 1;
1355
+ }
1356
+
1357
+ #vars-list .row {
1358
+ margin-bottom: var(--s2);
1359
+ }
1360
+
1361
+ /* ----------------------------------------------------------------- overlay */
1362
+
1363
+ #drop {
1364
+ position: fixed;
1365
+ inset: 0;
1366
+ display: none;
1367
+ place-items: center;
1368
+ background: rgba(14, 17, 22, 0.94);
1369
+ border: 2px dashed var(--accent);
1370
+ color: var(--accent);
1371
+ font-size: 20px;
1372
+ font-weight: 500;
1373
+ z-index: 40;
1374
+ }
1375
+ #drop.on {
1376
+ display: grid;
1377
+ }
1378
+
1379
+ #toast {
1380
+ position: fixed;
1381
+ bottom: var(--s5);
1382
+ left: 50%;
1383
+ transform: translateX(-50%) translateY(6px);
1384
+ background: var(--panel-3);
1385
+ border: 1px solid var(--line);
1386
+ border-radius: var(--r2);
1387
+ padding: var(--s3) var(--s5);
1388
+ opacity: 0;
1389
+ transition: opacity 0.18s ease, transform 0.18s ease;
1390
+ pointer-events: none;
1391
+ z-index: 50;
1392
+ max-width: 72vw;
1393
+ font-size: 12px;
1394
+ box-shadow: var(--shadow);
1395
+ }
1396
+ #toast.on {
1397
+ opacity: 1;
1398
+ transform: translateX(-50%) translateY(0);
1399
+ }
1400
+
1401
+ /* -------------------------------------------------------------- responsive */
1402
+
1403
+ @media (max-width: 860px) {
1404
+ main {
1405
+ flex-direction: column;
1406
+ }
1407
+ aside {
1408
+ width: auto;
1409
+ max-height: 42vh;
1410
+ border-right: none;
1411
+ border-bottom: 1px solid var(--line);
1412
+ }
1413
+ #panel {
1414
+ padding: var(--s4);
1415
+ }
1416
+ header {
1417
+ gap: var(--s3);
1418
+ padding: var(--s3) var(--s4);
1419
+ }
1420
+ .brand {
1421
+ border-right: none;
1422
+ padding-right: 0;
1423
+ }
1424
+ }