@takodotid/circuit 0.19.0 → 0.21.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 (54) hide show
  1. package/README.md +2 -2
  2. package/dist/chunks/index-n4zpd25n.js +936 -0
  3. package/dist/chunks/{index-dczpxzzm.js → index-v8zgr7wm.js} +33 -14
  4. package/dist/chunks/{new-7n8qvtjd.js → new-ny89s7y7.js} +4 -6
  5. package/dist/chunks/server-zapfzhv4.js +244 -0
  6. package/dist/cli/index.js +855 -803
  7. package/dist/index.js +20 -21
  8. package/dist/presets/index.js +20 -21
  9. package/dist/types/adapters/devices/derive.d.ts +2 -0
  10. package/dist/types/core/across.d.ts +4 -0
  11. package/dist/types/core/addr.d.ts +10 -0
  12. package/dist/types/core/addresses.d.ts +46 -0
  13. package/dist/types/core/files.d.ts +6 -0
  14. package/dist/types/core/graph.d.ts +31 -1
  15. package/dist/types/core/schema-docs.d.ts +16 -0
  16. package/package.json +7 -7
  17. package/src/adapters/devices/derive.ts +32 -0
  18. package/src/adapters/devices/raisecom-ros/index.ts +23 -3
  19. package/src/adapters/devices/routeros/index.ts +10 -0
  20. package/src/adapters/devices/vrp/index.ts +10 -1
  21. package/src/cli/graph.ts +1 -15
  22. package/src/cli/index.ts +154 -20
  23. package/src/cli/rename.ts +129 -0
  24. package/src/core/across.ts +322 -0
  25. package/src/core/addr.ts +29 -0
  26. package/src/core/addresses.ts +316 -0
  27. package/src/core/files.ts +32 -0
  28. package/src/core/graph.ts +27 -6
  29. package/src/core/schema-docs.ts +166 -0
  30. package/src/core/validate.ts +2 -52
  31. package/src/studio/data.ts +255 -0
  32. package/src/studio/git.ts +119 -0
  33. package/src/studio/public/app.js +165 -0
  34. package/src/studio/public/compare.js +243 -0
  35. package/src/studio/public/diff.js +97 -0
  36. package/src/studio/public/dom.js +108 -0
  37. package/src/studio/public/evaluate.js +648 -0
  38. package/src/studio/public/export.js +122 -0
  39. package/src/studio/public/favicon.svg +12 -0
  40. package/src/studio/public/graph.js +983 -0
  41. package/src/studio/public/index.html +71 -0
  42. package/src/studio/public/net.js +128 -0
  43. package/src/studio/public/special-gothic-latin-ext.woff2 +0 -0
  44. package/src/studio/public/special-gothic-latin.woff2 +0 -0
  45. package/src/studio/public/special-gothic-license.txt +93 -0
  46. package/src/studio/public/style.css +1334 -0
  47. package/src/studio/public/tables.js +1223 -0
  48. package/src/studio/public/tools.js +578 -0
  49. package/src/studio/server.ts +219 -0
  50. package/templates/project/AGENTS.md +2 -0
  51. package/dist/chunks/index-fq57hya4.js +0 -20
  52. package/dist/chunks/studio-femhydvd.js +0 -764
  53. package/src/cli/studio-page.ts +0 -876
  54. package/src/cli/studio.ts +0 -126
@@ -0,0 +1,1334 @@
1
+ /* The documentation site's cobalt, white and highlighter yellow, set in Special Gothic, at working density. */
2
+
3
+ @font-face {
4
+ font-family: "Special Gothic";
5
+ font-style: normal;
6
+ font-weight: 400 700;
7
+ font-stretch: 75% 125%;
8
+ font-display: swap;
9
+ src: url("special-gothic-latin-ext.woff2") format("woff2");
10
+ unicode-range:
11
+ U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
12
+ U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
13
+ }
14
+
15
+ @font-face {
16
+ font-family: "Special Gothic";
17
+ font-style: normal;
18
+ font-weight: 400 700;
19
+ font-stretch: 75% 125%;
20
+ font-display: swap;
21
+ src: url("special-gothic-latin.woff2") format("woff2");
22
+ unicode-range:
23
+ U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191,
24
+ U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
25
+ }
26
+
27
+ :root {
28
+ /* The shell: the same in both themes. */
29
+ --cobalt: #0a47b8;
30
+ --cobalt-deep: #08337f;
31
+ --navy: #0a1a36;
32
+ --yellow: #f8e347;
33
+ --yellow-deep: #e8cd1c;
34
+ --on-cobalt: #ffffff;
35
+ --on-cobalt-soft: #c9d7f5;
36
+
37
+ /* The page. */
38
+ --bg: #eff2f9;
39
+ --surface: #ffffff;
40
+ --surface-raised: #f4f6fb;
41
+ --line: #d3d9e7;
42
+ --text: #11141b;
43
+ --muted: #49536a;
44
+ --edge: #7b86a0;
45
+ --accent: #0a47b8;
46
+ --accent-strong: #f8e347;
47
+ --on-accent: #11141b;
48
+ --selected: #fff7c2;
49
+ --error: #c0262c;
50
+ --error-strong: #c0262c;
51
+ --warning: #b35a00;
52
+ --warning-strong: #f0a030;
53
+ --ok: #11733b;
54
+ --segment: rgba(248, 227, 71, 0.32);
55
+ --dots: rgba(10, 71, 184, 0.14);
56
+ --heading-dots: var(--cobalt);
57
+
58
+ --font:
59
+ "Special Gothic", "Segoe UI", system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
60
+ --display: "Special Gothic", "Segoe UI", system-ui, sans-serif;
61
+ --mono: ui-monospace, "Cascadia Code", "SFMono-Regular", Menlo, Consolas, monospace;
62
+ }
63
+
64
+ @media (prefers-color-scheme: dark) {
65
+ :root {
66
+ --bg: #0b1122;
67
+ --surface: #121a30;
68
+ --surface-raised: #182241;
69
+ --line: #26314d;
70
+ --text: #f1f4fb;
71
+ --muted: #a9b4cc;
72
+ --edge: #7c89a8;
73
+ --accent: #9cb9ff;
74
+ --on-accent: #11141b;
75
+ --selected: #3a3510;
76
+ --error: #ff8086;
77
+ --error-strong: #c0262c;
78
+ --warning: #ffad4f;
79
+ --warning-strong: #e08a1e;
80
+ --ok: #5fd38f;
81
+ --segment: rgba(248, 227, 71, 0.16);
82
+ --dots: rgba(156, 185, 255, 0.12);
83
+ }
84
+ }
85
+
86
+ * {
87
+ box-sizing: border-box;
88
+ }
89
+
90
+ html,
91
+ body {
92
+ margin: 0;
93
+ height: 100%;
94
+ background: var(--bg);
95
+ color: var(--text);
96
+ font: 15px/1.5 var(--font);
97
+ scrollbar-color: var(--edge) transparent;
98
+ }
99
+
100
+ body {
101
+ display: flex;
102
+ flex-direction: column;
103
+ }
104
+
105
+ ::selection {
106
+ color: #11141b;
107
+ background: var(--yellow);
108
+ }
109
+
110
+ :focus-visible {
111
+ outline: 3px solid var(--accent);
112
+ outline-offset: 2px;
113
+ }
114
+
115
+ /* On the cobalt rail a cobalt ring would vanish, so it is yellow there. */
116
+ header :focus-visible {
117
+ outline-color: var(--yellow);
118
+ }
119
+
120
+ button {
121
+ min-height: 36px;
122
+ padding: 6px 12px;
123
+ border: 1.5px solid var(--edge);
124
+ border-radius: 2px;
125
+ background: var(--surface);
126
+ color: inherit;
127
+ font: inherit;
128
+ font-weight: 600;
129
+ cursor: pointer;
130
+ }
131
+
132
+ button:hover {
133
+ border-color: var(--accent);
134
+ color: var(--accent);
135
+ }
136
+
137
+ button[aria-pressed="true"] {
138
+ border-color: var(--cobalt);
139
+ background: var(--cobalt);
140
+ color: #ffffff;
141
+ }
142
+
143
+ input,
144
+ select,
145
+ button {
146
+ box-sizing: border-box;
147
+ }
148
+
149
+ code {
150
+ font-family: var(--mono);
151
+ font-size: 0.92em;
152
+ }
153
+
154
+ a {
155
+ color: var(--accent);
156
+ font-weight: 600;
157
+ text-decoration: underline dotted 1.5px;
158
+ text-underline-offset: 3px;
159
+ cursor: pointer;
160
+ }
161
+
162
+ a:hover {
163
+ text-decoration-style: solid;
164
+ }
165
+
166
+ .muted {
167
+ color: var(--muted);
168
+ }
169
+
170
+ /* The rail: the wordmark, the views and the search, on cobalt. */
171
+
172
+ header {
173
+ display: flex;
174
+ flex-wrap: wrap;
175
+ align-items: stretch;
176
+ gap: 0;
177
+ padding: 0;
178
+ background: var(--cobalt);
179
+ color: var(--on-cobalt);
180
+ }
181
+
182
+ header .brand {
183
+ display: flex;
184
+ align-items: center;
185
+ gap: 10px;
186
+ padding: 0 18px;
187
+ color: inherit;
188
+ text-decoration: none;
189
+ }
190
+
191
+ header .brand:hover .wordmark {
192
+ color: var(--yellow);
193
+ }
194
+
195
+ header .wordmark {
196
+ font-family: var(--display);
197
+ font-size: 21px;
198
+ font-weight: 700;
199
+ font-stretch: 125%;
200
+ letter-spacing: 0.04em;
201
+ text-transform: uppercase;
202
+ }
203
+
204
+ header .product {
205
+ padding: 2px 7px;
206
+ border-radius: 2px;
207
+ background: var(--yellow);
208
+ color: #0a1a36;
209
+ font-size: 11px;
210
+ font-weight: 700;
211
+ font-stretch: 118%;
212
+ letter-spacing: 0.1em;
213
+ text-transform: uppercase;
214
+ }
215
+
216
+ nav,
217
+ .actions {
218
+ display: flex;
219
+ flex-wrap: wrap;
220
+ gap: 6px;
221
+ }
222
+
223
+ header > nav {
224
+ gap: 0;
225
+ padding-inline: 6px;
226
+ }
227
+
228
+ header > nav button {
229
+ position: relative;
230
+ min-height: 52px;
231
+ padding: 0 11px;
232
+ border: 0;
233
+ border-radius: 0;
234
+ background: transparent;
235
+ color: var(--on-cobalt-soft);
236
+ font-size: 12.5px;
237
+ font-weight: 700;
238
+ font-stretch: 112%;
239
+ letter-spacing: 0.06em;
240
+ text-transform: uppercase;
241
+ }
242
+
243
+ header > nav button:hover {
244
+ color: #ffffff;
245
+ background: rgba(255, 255, 255, 0.08);
246
+ }
247
+
248
+ header > nav button[aria-pressed="true"] {
249
+ color: #ffffff;
250
+ background: transparent;
251
+ }
252
+
253
+ header > nav button[aria-pressed="true"]::after {
254
+ content: "";
255
+ position: absolute;
256
+ right: 9px;
257
+ bottom: 0;
258
+ left: 9px;
259
+ height: 4px;
260
+ background: var(--yellow);
261
+ }
262
+
263
+ .count {
264
+ margin-left: 6px;
265
+ padding: 0 5px;
266
+ border-radius: 2px;
267
+ background: var(--yellow);
268
+ color: #0a1a36;
269
+ font-size: 11px;
270
+ letter-spacing: 0;
271
+ }
272
+
273
+ .count:empty {
274
+ display: none;
275
+ }
276
+
277
+ header input[type="search"] {
278
+ width: 230px;
279
+ max-width: 100%;
280
+ min-height: 36px;
281
+ margin: 8px 16px 8px auto;
282
+ padding: 4px 12px;
283
+ border: 1.5px solid rgba(201, 215, 245, 0.7);
284
+ border-radius: 2px;
285
+ background: rgba(8, 28, 64, 0.35);
286
+ color: #ffffff;
287
+ font: inherit;
288
+ }
289
+
290
+ header input[type="search"]::placeholder {
291
+ color: var(--on-cobalt-soft);
292
+ }
293
+
294
+ header input[type="search"]:focus {
295
+ border-color: #ffffff;
296
+ outline: 3px solid var(--yellow);
297
+ outline-offset: 1px;
298
+ }
299
+
300
+ /* The status rail: the network's health in counts, each with a mark as well as a colour. */
301
+
302
+ .status-rail {
303
+ order: 10;
304
+ display: flex;
305
+ flex: 1 0 100%;
306
+ align-items: center;
307
+ min-height: 38px;
308
+ padding: 0 18px;
309
+ background: var(--surface);
310
+ border-bottom: 1px solid var(--line);
311
+ color: var(--text);
312
+ }
313
+
314
+ .status-rail .summary {
315
+ display: flex;
316
+ flex-wrap: wrap;
317
+ align-items: center;
318
+ color: var(--muted);
319
+ font-size: 13px;
320
+ }
321
+
322
+ .summary-item {
323
+ display: inline-flex;
324
+ align-items: center;
325
+ min-height: 24px;
326
+ padding: 0 12px;
327
+ border-left: 1px solid var(--line);
328
+ color: var(--text);
329
+ font-size: 13px;
330
+ font-weight: 700;
331
+ }
332
+
333
+ .summary-item:first-child {
334
+ padding-left: 0;
335
+ border-left: 0;
336
+ }
337
+
338
+ .summary-item::before {
339
+ content: "";
340
+ width: 8px;
341
+ height: 8px;
342
+ margin-right: 7px;
343
+ border: 1.5px solid currentColor;
344
+ border-radius: 50%;
345
+ }
346
+
347
+ .summary-item.error {
348
+ color: var(--error);
349
+ }
350
+
351
+ .summary-item.warning,
352
+ .summary-item.changed {
353
+ color: var(--warning);
354
+ }
355
+
356
+ .summary-item.error::before,
357
+ .summary-item.warning::before,
358
+ .summary-item.changed::before {
359
+ background: currentColor;
360
+ }
361
+
362
+ .summary-item.changed::before {
363
+ border-radius: 1px;
364
+ }
365
+
366
+ .summary-item.quiet {
367
+ color: var(--muted);
368
+ font-weight: 600;
369
+ }
370
+
371
+ .status-rail .live {
372
+ display: inline-flex;
373
+ align-items: center;
374
+ margin-left: auto;
375
+ white-space: nowrap;
376
+ color: var(--text);
377
+ font-size: 13px;
378
+ font-weight: 700;
379
+ text-transform: capitalize;
380
+ }
381
+
382
+ .status-rail .live::before {
383
+ content: "";
384
+ display: inline-block;
385
+ width: 8px;
386
+ height: 8px;
387
+ margin-right: 7px;
388
+ border-radius: 50%;
389
+ background: var(--warning);
390
+ }
391
+
392
+ .status-rail .live.on::before {
393
+ background: var(--ok);
394
+ }
395
+
396
+ .banner {
397
+ margin: 0;
398
+ padding: 10px 18px;
399
+ background: var(--error-strong);
400
+ color: #ffffff;
401
+ white-space: pre-wrap;
402
+ font: 12px/1.45 var(--mono);
403
+ max-height: 30vh;
404
+ overflow: auto;
405
+ }
406
+
407
+ main {
408
+ flex: 1;
409
+ display: flex;
410
+ flex-direction: column;
411
+ min-height: 0;
412
+ }
413
+
414
+ .view {
415
+ flex: 1;
416
+ display: flex;
417
+ flex-direction: column;
418
+ min-height: 0;
419
+ }
420
+
421
+ .view[hidden] {
422
+ display: none;
423
+ }
424
+
425
+ /* The graph's toolbar: the layers as one segmented control, the actions to the right. */
426
+
427
+ .toolbar {
428
+ display: flex;
429
+ flex-wrap: wrap;
430
+ align-items: center;
431
+ justify-content: space-between;
432
+ gap: 8px 16px;
433
+ min-height: 52px;
434
+ padding: 8px 18px;
435
+ background: var(--surface);
436
+ border-bottom: 1px solid var(--line);
437
+ }
438
+
439
+ .toolbar nav {
440
+ gap: 0;
441
+ border: 1.5px solid var(--text);
442
+ border-radius: 2px;
443
+ }
444
+
445
+ .toolbar nav button {
446
+ min-height: 32px;
447
+ padding: 4px 12px;
448
+ border: 0;
449
+ border-radius: 0;
450
+ background: transparent;
451
+ font-size: 13px;
452
+ }
453
+
454
+ .toolbar nav button + button {
455
+ border-left: 1.5px solid var(--line);
456
+ }
457
+
458
+ .toolbar nav button[aria-pressed="true"] {
459
+ background: var(--cobalt);
460
+ color: #ffffff;
461
+ }
462
+
463
+ .toolbar nav button .count,
464
+ .toolbar nav button small {
465
+ background: none;
466
+ color: inherit;
467
+ opacity: 0.7;
468
+ }
469
+
470
+ .toolbar .actions {
471
+ align-items: center;
472
+ gap: 6px;
473
+ }
474
+
475
+ .toolbar .actions button,
476
+ .menu summary {
477
+ min-height: 34px;
478
+ padding: 4px 12px;
479
+ font-size: 13px;
480
+ }
481
+
482
+ .menu {
483
+ position: relative;
484
+ }
485
+
486
+ .menu summary {
487
+ display: flex;
488
+ align-items: center;
489
+ gap: 8px;
490
+ border: 1.5px solid var(--edge);
491
+ border-radius: 2px;
492
+ background: var(--surface);
493
+ font-weight: 600;
494
+ list-style: none;
495
+ cursor: pointer;
496
+ }
497
+
498
+ .menu summary::-webkit-details-marker {
499
+ display: none;
500
+ }
501
+
502
+ .menu summary::after {
503
+ content: "";
504
+ width: 7px;
505
+ height: 7px;
506
+ margin-top: -4px;
507
+ border-right: 1.5px solid currentColor;
508
+ border-bottom: 1.5px solid currentColor;
509
+ transform: rotate(45deg);
510
+ }
511
+
512
+ .menu summary:hover {
513
+ border-color: var(--accent);
514
+ color: var(--accent);
515
+ }
516
+
517
+ .menu-items {
518
+ position: absolute;
519
+ top: calc(100% + 4px);
520
+ right: 0;
521
+ z-index: 5;
522
+ display: grid;
523
+ min-width: 120px;
524
+ padding: 4px;
525
+ border: 1.5px solid var(--text);
526
+ border-radius: 2px;
527
+ background: var(--surface);
528
+ box-shadow: 0 8px 20px rgba(10, 26, 54, 0.18);
529
+ }
530
+
531
+ .menu-items button {
532
+ justify-content: flex-start;
533
+ border: 0;
534
+ text-align: left;
535
+ }
536
+
537
+ /* The drawing board. */
538
+
539
+ .stage {
540
+ flex: 1;
541
+ display: flex;
542
+ min-height: 0;
543
+ background-color: var(--bg);
544
+ background-image: radial-gradient(circle, var(--dots) 1.2px, transparent 1.6px);
545
+ background-size: 24px 24px;
546
+ }
547
+
548
+ svg.canvas {
549
+ flex: 1;
550
+ min-width: 0;
551
+ height: 100%;
552
+ cursor: grab;
553
+ touch-action: none;
554
+ user-select: none;
555
+ }
556
+
557
+ svg.canvas.panning {
558
+ cursor: grabbing;
559
+ }
560
+
561
+ .edge path {
562
+ fill: none;
563
+ stroke: var(--edge);
564
+ stroke-width: 2;
565
+ }
566
+
567
+ .edge text {
568
+ font-size: 12.5px;
569
+ fill: var(--muted);
570
+ paint-order: stroke;
571
+ stroke: var(--bg);
572
+ stroke-width: 5px;
573
+ stroke-linejoin: round;
574
+ }
575
+
576
+ .edge .hit {
577
+ stroke: transparent;
578
+ stroke-width: 12;
579
+ cursor: pointer;
580
+ }
581
+
582
+ .edge.one-sided path:not(.hit) {
583
+ stroke: var(--warning);
584
+ stroke-width: 3;
585
+ stroke-dasharray: 4 4;
586
+ }
587
+
588
+ .edge.selected path:not(.hit),
589
+ .edge.lit path:not(.hit) {
590
+ stroke: var(--accent);
591
+ stroke-width: 4;
592
+ }
593
+
594
+ .edge.selected text,
595
+ .edge.lit text {
596
+ fill: var(--text);
597
+ }
598
+
599
+ .edge.added path:not(.hit) {
600
+ stroke: var(--ok);
601
+ stroke-width: 4;
602
+ }
603
+
604
+ .edge.gone path:not(.hit) {
605
+ stroke: var(--error);
606
+ stroke-width: 4;
607
+ stroke-dasharray: 10 5;
608
+ }
609
+
610
+ .node {
611
+ cursor: pointer;
612
+ }
613
+
614
+ .node > rect {
615
+ fill: var(--surface);
616
+ stroke: var(--text);
617
+ stroke-width: 2;
618
+ }
619
+
620
+ .node.segment > rect {
621
+ fill: var(--segment);
622
+ stroke: var(--yellow-deep);
623
+ }
624
+
625
+ .node.outside > rect {
626
+ fill: var(--bg);
627
+ stroke: var(--edge);
628
+ stroke-dasharray: 2 4;
629
+ }
630
+
631
+ .node.error > rect {
632
+ stroke: var(--error);
633
+ stroke-width: 4;
634
+ }
635
+
636
+ .node.warning > rect {
637
+ stroke: var(--warning);
638
+ stroke-width: 4;
639
+ stroke-dasharray: 10 3;
640
+ }
641
+
642
+ .node.selected > rect {
643
+ fill: var(--selected);
644
+ stroke: var(--cobalt);
645
+ stroke-width: 4;
646
+ }
647
+
648
+ .node .chassis > rect:first-child {
649
+ fill: var(--cobalt);
650
+ stroke: none;
651
+ }
652
+
653
+ .node .chassis .socket {
654
+ fill: #ffffff;
655
+ stroke: none;
656
+ }
657
+
658
+ .node.selected .chassis .socket {
659
+ fill: var(--yellow);
660
+ }
661
+
662
+ .node text {
663
+ fill: var(--text);
664
+ font-size: 14px;
665
+ text-anchor: middle;
666
+ dominant-baseline: central;
667
+ pointer-events: none;
668
+ }
669
+
670
+ .node.device text {
671
+ text-anchor: start;
672
+ }
673
+
674
+ .node text.name {
675
+ font-size: 18px;
676
+ font-weight: 700;
677
+ }
678
+
679
+ .node text.sub {
680
+ fill: var(--muted);
681
+ font-size: 13px;
682
+ }
683
+
684
+ .node.segment text,
685
+ .node.outside text {
686
+ font-family: var(--mono);
687
+ font-size: 12px;
688
+ }
689
+
690
+ .dim {
691
+ opacity: 0.18;
692
+ }
693
+
694
+ svg.far .edge text {
695
+ display: none;
696
+ }
697
+
698
+ /* The side panel: what is selected, or the layer's legend. */
699
+
700
+ aside {
701
+ width: 390px;
702
+ overflow: auto;
703
+ padding: 20px 22px 32px;
704
+ background: var(--surface);
705
+ border-left: 1px solid var(--line);
706
+ }
707
+
708
+ aside h2 {
709
+ margin: 0 0 8px;
710
+ font-family: var(--display);
711
+ font-size: 26px;
712
+ font-weight: 700;
713
+ letter-spacing: -0.015em;
714
+ line-height: 1.15;
715
+ overflow-wrap: anywhere;
716
+ display: table;
717
+ padding-bottom: 12px;
718
+ background: radial-gradient(circle, var(--heading-dots) 48%, transparent 52%) left bottom / 14px 7px space no-repeat;
719
+ }
720
+
721
+ aside h3,
722
+ .tables h3 {
723
+ margin: 26px 0 8px;
724
+ color: var(--text);
725
+ font-size: 11px;
726
+ font-weight: 700;
727
+ font-stretch: 115%;
728
+ letter-spacing: 0.1em;
729
+ text-transform: uppercase;
730
+ }
731
+
732
+ aside ul {
733
+ list-style: none;
734
+ margin: 0;
735
+ padding: 0;
736
+ }
737
+
738
+ aside li {
739
+ padding: 3px 0;
740
+ overflow-wrap: anywhere;
741
+ }
742
+
743
+ aside .kind {
744
+ color: var(--muted);
745
+ font-size: 12px;
746
+ }
747
+
748
+ aside pre {
749
+ margin: 0;
750
+ padding: 10px;
751
+ border-radius: 2px;
752
+ background: var(--navy);
753
+ color: #e6ecfa;
754
+ font: 12px/1.5 var(--mono);
755
+ white-space: pre-wrap;
756
+ overflow-wrap: anywhere;
757
+ }
758
+
759
+ .legend li {
760
+ display: flex;
761
+ align-items: center;
762
+ gap: 10px;
763
+ }
764
+
765
+ .legend svg {
766
+ flex: none;
767
+ }
768
+
769
+ /* Marks that pair a colour with a word. */
770
+
771
+ .badge {
772
+ display: inline-block;
773
+ margin-right: 6px;
774
+ padding: 1px 6px;
775
+ border-radius: 2px;
776
+ font-family: var(--font);
777
+ font-size: 11px;
778
+ font-weight: 700;
779
+ font-stretch: 112%;
780
+ letter-spacing: 0.06em;
781
+ text-transform: uppercase;
782
+ }
783
+
784
+ .badge.error {
785
+ background: var(--error-strong);
786
+ color: #ffffff;
787
+ }
788
+
789
+ .badge.warning {
790
+ background: var(--warning-strong);
791
+ color: #11141b;
792
+ }
793
+
794
+ .chips {
795
+ display: flex;
796
+ flex-wrap: wrap;
797
+ gap: 6px;
798
+ }
799
+
800
+ .chips button {
801
+ min-height: 28px;
802
+ padding: 2px 9px;
803
+ font-size: 12px;
804
+ }
805
+
806
+ /* The tables. */
807
+
808
+ .tables {
809
+ flex: 1;
810
+ overflow: auto;
811
+ padding: 28px clamp(16px, 4vw, 56px) 64px;
812
+ }
813
+
814
+ .tables h2 {
815
+ margin: 6px 0 18px;
816
+ font-family: var(--display);
817
+ font-size: clamp(30px, 3.2vw, 44px);
818
+ font-weight: 700;
819
+ letter-spacing: -0.03em;
820
+ line-height: 1.2;
821
+ display: table;
822
+ padding-bottom: 14px;
823
+ background: radial-gradient(circle, var(--heading-dots) 48%, transparent 52%) left bottom / 18px 9px space no-repeat;
824
+ }
825
+
826
+ .tables h2 .badge {
827
+ margin-left: 10px;
828
+ vertical-align: middle;
829
+ letter-spacing: 0.06em;
830
+ }
831
+
832
+ @media (prefers-color-scheme: dark) {
833
+ :root {
834
+ --heading-dots: var(--accent);
835
+ }
836
+ }
837
+
838
+ .tables h4 {
839
+ margin: 16px 0 6px;
840
+ font-family: var(--mono);
841
+ font-size: 13px;
842
+ font-weight: 600;
843
+ }
844
+
845
+ .tables p {
846
+ margin: 4px 0 8px;
847
+ }
848
+
849
+ .tables .crumbs {
850
+ margin: 0;
851
+ font-size: 13px;
852
+ }
853
+
854
+ .tables .findings {
855
+ list-style: none;
856
+ margin: 0;
857
+ padding: 0;
858
+ }
859
+
860
+ .tables .findings li {
861
+ padding: 3px 0;
862
+ }
863
+
864
+ .tables .tabs {
865
+ display: flex;
866
+ flex-wrap: wrap;
867
+ gap: 0;
868
+ margin: 18px 0 14px;
869
+ border-bottom: 1.5px solid var(--line);
870
+ }
871
+
872
+ .tabs button {
873
+ position: relative;
874
+ min-height: 40px;
875
+ padding: 8px 12px 10px;
876
+ border: 0;
877
+ border-radius: 0;
878
+ background: transparent;
879
+ color: var(--muted);
880
+ font-size: 14px;
881
+ }
882
+
883
+ .tabs button:hover {
884
+ color: var(--text);
885
+ background: var(--surface-raised);
886
+ }
887
+
888
+ .tabs button[aria-pressed="true"] {
889
+ background: transparent;
890
+ color: var(--text);
891
+ }
892
+
893
+ .tabs button[aria-pressed="true"]::after {
894
+ content: "";
895
+ position: absolute;
896
+ right: 8px;
897
+ bottom: -1.5px;
898
+ left: 8px;
899
+ height: 4px;
900
+ background: var(--cobalt);
901
+ }
902
+
903
+ .table-block {
904
+ max-width: 100%;
905
+ overflow-x: auto;
906
+ }
907
+
908
+ table.data tbody th,
909
+ table.data td:first-child {
910
+ white-space: nowrap;
911
+ }
912
+
913
+ .table-actions {
914
+ margin: 0 0 6px;
915
+ font-size: 13px;
916
+ text-align: right;
917
+ }
918
+
919
+ table.data {
920
+ width: 100%;
921
+ border: 0;
922
+ border-collapse: collapse;
923
+ background: var(--surface);
924
+ font-size: 13.5px;
925
+ font-variant-numeric: tabular-nums;
926
+ }
927
+
928
+ table.data th,
929
+ table.data td {
930
+ padding: 8px 12px;
931
+ border-bottom: 1px solid var(--line);
932
+ overflow-wrap: break-word;
933
+ text-align: left;
934
+ vertical-align: top;
935
+ }
936
+
937
+ table.data thead th {
938
+ border-bottom: 2px solid var(--text);
939
+ background: var(--surface);
940
+ color: var(--text);
941
+ font-size: 11px;
942
+ font-weight: 700;
943
+ font-stretch: 112%;
944
+ letter-spacing: 0.08em;
945
+ text-transform: uppercase;
946
+ white-space: nowrap;
947
+ }
948
+
949
+ table.data th[title] {
950
+ cursor: help;
951
+ text-decoration: underline dotted;
952
+ text-underline-offset: 3px;
953
+ }
954
+
955
+ table.data tbody tr:hover > td,
956
+ table.data tbody tr:hover > th {
957
+ background: var(--surface-raised);
958
+ }
959
+
960
+ table.data tr.muted td {
961
+ color: var(--muted);
962
+ }
963
+
964
+ table.settings {
965
+ width: auto;
966
+ min-width: min(560px, 100%);
967
+ }
968
+
969
+ table.settings > tbody > tr > th {
970
+ width: 1%;
971
+ color: var(--muted);
972
+ font-weight: 600;
973
+ white-space: nowrap;
974
+ }
975
+
976
+ table.data table.data {
977
+ margin: 0;
978
+ }
979
+
980
+ tr.flash td {
981
+ background: var(--selected) !important;
982
+ transition: background 0.4s;
983
+ }
984
+
985
+ pre.code {
986
+ margin: 0;
987
+ padding: 14px;
988
+ overflow: auto;
989
+ border-radius: 2px;
990
+ background: var(--navy);
991
+ color: #e6ecfa;
992
+ font: 12.5px/1.55 var(--mono);
993
+ white-space: pre;
994
+ }
995
+
996
+ /* A device's front panel: a square per port, by speed. Each use has its own fill and edge, not colour alone. */
997
+
998
+ .faceplate {
999
+ display: flex;
1000
+ flex-wrap: wrap;
1001
+ gap: 16px 28px;
1002
+ max-width: 100%;
1003
+ margin: 8px 0;
1004
+ padding: 18px;
1005
+ overflow-x: auto;
1006
+ border: 2px solid var(--text);
1007
+ border-radius: 2px;
1008
+ background: var(--surface);
1009
+ }
1010
+
1011
+ .faceplate .speed {
1012
+ display: flex;
1013
+ align-items: center;
1014
+ gap: 10px;
1015
+ min-width: max-content;
1016
+ }
1017
+
1018
+ .faceplate .speed-name {
1019
+ min-width: 36px;
1020
+ color: var(--muted);
1021
+ font-family: var(--mono);
1022
+ font-size: 12px;
1023
+ }
1024
+
1025
+ .faceplate .ports {
1026
+ display: grid;
1027
+ grid-template-rows: repeat(2, auto);
1028
+ grid-auto-flow: column;
1029
+ gap: 4px;
1030
+ }
1031
+
1032
+ .port {
1033
+ position: relative;
1034
+ display: inline-block;
1035
+ width: 28px;
1036
+ height: 24px;
1037
+ padding: 0;
1038
+ border: 1.5px solid var(--edge);
1039
+ border-radius: 2px;
1040
+ background: var(--surface);
1041
+ color: var(--text);
1042
+ font: 600 11px var(--font);
1043
+ }
1044
+
1045
+ .port.shut {
1046
+ color: var(--muted);
1047
+ background: repeating-linear-gradient(135deg, var(--surface) 0 3px, var(--line) 3px 5px);
1048
+ }
1049
+
1050
+ .port.access {
1051
+ background: var(--surface);
1052
+ border: 2px solid var(--cobalt);
1053
+ }
1054
+
1055
+ .port.trunk {
1056
+ background: var(--yellow);
1057
+ border: 2px solid var(--text);
1058
+ color: #11141b;
1059
+ }
1060
+
1061
+ .port.routed {
1062
+ background: var(--surface);
1063
+ border: 3px double var(--text);
1064
+ }
1065
+
1066
+ .port.lag {
1067
+ border: 2px dashed var(--cobalt);
1068
+ }
1069
+
1070
+ .port.linked::after {
1071
+ content: "";
1072
+ position: absolute;
1073
+ top: 2px;
1074
+ right: 2px;
1075
+ width: 5px;
1076
+ height: 5px;
1077
+ border-radius: 50%;
1078
+ background: var(--ok);
1079
+ }
1080
+
1081
+ .faceplate-legend {
1082
+ display: flex;
1083
+ flex-wrap: wrap;
1084
+ gap: 6px 16px;
1085
+ margin: 0 0 12px;
1086
+ padding: 0;
1087
+ color: var(--muted);
1088
+ font-size: 12px;
1089
+ list-style: none;
1090
+ }
1091
+
1092
+ .faceplate-legend li {
1093
+ display: flex;
1094
+ align-items: center;
1095
+ gap: 6px;
1096
+ }
1097
+
1098
+ .faceplate-legend .port {
1099
+ width: 18px;
1100
+ height: 14px;
1101
+ }
1102
+
1103
+ /* A line diff: removed lines, added lines, and runs of the same folded away. */
1104
+
1105
+ pre.diff .removed,
1106
+ p .removed {
1107
+ color: var(--error);
1108
+ }
1109
+
1110
+ pre.diff .added,
1111
+ p .added {
1112
+ color: var(--ok);
1113
+ }
1114
+
1115
+ pre.code.diff .removed {
1116
+ color: #ff9ca0;
1117
+ }
1118
+
1119
+ pre.code.diff .added {
1120
+ color: #7fe0a6;
1121
+ }
1122
+
1123
+ pre.diff .fold {
1124
+ display: block;
1125
+ margin: 4px 0;
1126
+ color: var(--muted);
1127
+ font-style: italic;
1128
+ }
1129
+
1130
+ pre.code.diff .fold {
1131
+ color: #9db3e0;
1132
+ }
1133
+
1134
+ .added {
1135
+ color: var(--ok);
1136
+ }
1137
+
1138
+ .removed {
1139
+ color: var(--error);
1140
+ }
1141
+
1142
+ details.change {
1143
+ margin: 6px 0;
1144
+ }
1145
+
1146
+ details.change summary {
1147
+ padding: 4px 0;
1148
+ font-weight: 600;
1149
+ cursor: pointer;
1150
+ }
1151
+
1152
+ /* The tools' forms: a field per line on a narrow screen, several side by side on a wide one. */
1153
+
1154
+ .tool-form {
1155
+ display: flex;
1156
+ flex-wrap: wrap;
1157
+ align-items: flex-start;
1158
+ gap: 12px 16px;
1159
+ margin: 12px 0 18px;
1160
+ padding: 16px;
1161
+ border: 2px solid var(--text);
1162
+ border-radius: 2px;
1163
+ background: var(--surface);
1164
+ }
1165
+
1166
+ .tool-form > button {
1167
+ margin-top: 24px;
1168
+ }
1169
+
1170
+ .field {
1171
+ display: flex;
1172
+ flex-direction: column;
1173
+ gap: 4px;
1174
+ color: var(--muted);
1175
+ font-size: 13px;
1176
+ font-weight: 600;
1177
+ }
1178
+
1179
+ .field input[type="text"],
1180
+ .field select {
1181
+ min-width: 150px;
1182
+ min-height: 36px;
1183
+ padding: 5px 9px;
1184
+ border: 1.5px solid var(--edge);
1185
+ border-radius: 2px;
1186
+ background: var(--bg);
1187
+ color: var(--text);
1188
+ font: inherit;
1189
+ font-weight: 400;
1190
+ }
1191
+
1192
+ .field input[type="text"]:focus,
1193
+ .field select:focus {
1194
+ border-color: var(--cobalt);
1195
+ }
1196
+
1197
+ .field small {
1198
+ font-size: 11px;
1199
+ font-weight: 400;
1200
+ }
1201
+
1202
+ .flags {
1203
+ display: flex;
1204
+ flex-wrap: wrap;
1205
+ gap: 4px 12px;
1206
+ color: var(--text);
1207
+ }
1208
+
1209
+ .flag {
1210
+ display: flex;
1211
+ align-items: center;
1212
+ gap: 4px;
1213
+ }
1214
+
1215
+ button.primary {
1216
+ border-color: var(--text);
1217
+ background: var(--yellow);
1218
+ color: #11141b;
1219
+ }
1220
+
1221
+ button.primary:hover {
1222
+ background: var(--yellow-deep);
1223
+ color: #11141b;
1224
+ }
1225
+
1226
+ .verdict {
1227
+ font-weight: 700;
1228
+ }
1229
+
1230
+ .verdict.accept {
1231
+ color: var(--ok);
1232
+ }
1233
+
1234
+ .verdict.drop,
1235
+ .verdict.reject {
1236
+ color: var(--error);
1237
+ }
1238
+
1239
+ .verdict.unknown {
1240
+ color: var(--warning);
1241
+ }
1242
+
1243
+ /* Narrower windows: the search drops under the views, then the panel moves under the graph. */
1244
+
1245
+ @media (max-width: 1180px) {
1246
+ header input[type="search"] {
1247
+ order: 5;
1248
+ width: calc(100% - 36px);
1249
+ margin: 0 18px 10px;
1250
+ }
1251
+ }
1252
+
1253
+ @media (max-width: 760px) {
1254
+ header .brand {
1255
+ width: 100%;
1256
+ min-height: 52px;
1257
+ }
1258
+
1259
+ header > nav {
1260
+ width: 100%;
1261
+ overflow-x: auto;
1262
+ flex-wrap: nowrap;
1263
+ }
1264
+
1265
+ header > nav button {
1266
+ flex: none;
1267
+ }
1268
+
1269
+ .status-rail {
1270
+ align-items: flex-start;
1271
+ gap: 8px;
1272
+ padding-block: 8px;
1273
+ }
1274
+
1275
+ .summary-item,
1276
+ .summary-item:first-child {
1277
+ min-height: 28px;
1278
+ padding: 0 14px 0 0;
1279
+ border-left: 0;
1280
+ }
1281
+
1282
+ .toolbar nav {
1283
+ flex-wrap: nowrap;
1284
+ max-width: 100%;
1285
+ overflow-x: auto;
1286
+ }
1287
+
1288
+ .toolbar nav button {
1289
+ flex: none;
1290
+ }
1291
+
1292
+ .stage {
1293
+ flex-direction: column;
1294
+ }
1295
+
1296
+ svg.canvas {
1297
+ flex: 1 1 auto;
1298
+ width: 100%;
1299
+ min-height: 320px;
1300
+ }
1301
+
1302
+ aside {
1303
+ width: auto;
1304
+ height: 42%;
1305
+ border-top: 2px solid var(--text);
1306
+ border-left: 0;
1307
+ }
1308
+
1309
+ .tables {
1310
+ padding: 20px 16px 40px;
1311
+ }
1312
+
1313
+ .port {
1314
+ width: 44px;
1315
+ height: 40px;
1316
+ font-size: 13px;
1317
+ }
1318
+
1319
+ .faceplate-legend .port {
1320
+ width: 24px;
1321
+ height: 18px;
1322
+ }
1323
+ }
1324
+
1325
+ @media (prefers-reduced-motion: reduce) {
1326
+ *,
1327
+ *::before,
1328
+ *::after {
1329
+ scroll-behavior: auto !important;
1330
+ transition-duration: 0.01ms !important;
1331
+ animation-duration: 0.01ms !important;
1332
+ animation-iteration-count: 1 !important;
1333
+ }
1334
+ }