@emeryld/rrroutes-contract 2.2.7 → 2.2.8
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.
- package/dist/docs/serializer.d.ts +1 -1
- package/dist/index.cjs +691 -817
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +691 -817
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -422,6 +422,7 @@ var CSS_STYLES = `:root {
|
|
|
422
422
|
--bg-card: rgba(15, 23, 42, 0.6);
|
|
423
423
|
--bg-card-hover: rgba(30, 41, 59, 0.7);
|
|
424
424
|
--bg-glass: rgba(15, 23, 42, 0.90);
|
|
425
|
+
--bg-panel: #0f172a;
|
|
425
426
|
--border-subtle: rgba(148, 163, 184, 0.2);
|
|
426
427
|
|
|
427
428
|
--text-main: #f8fafc;
|
|
@@ -430,6 +431,8 @@ var CSS_STYLES = `:root {
|
|
|
430
431
|
|
|
431
432
|
--accent-primary: #a855f7;
|
|
432
433
|
--accent-glow: rgba(168, 85, 247, 0.25);
|
|
434
|
+
--accent-success: #4ade80;
|
|
435
|
+
--accent-error: #f87171;
|
|
433
436
|
|
|
434
437
|
/* Method Colors */
|
|
435
438
|
--method-get: #4ade80; --method-get-bg: rgba(34, 197, 94, 0.15);
|
|
@@ -440,29 +443,27 @@ var CSS_STYLES = `:root {
|
|
|
440
443
|
|
|
441
444
|
--radius-card: 12px;
|
|
442
445
|
--shadow-card: 0 4px 20px rgba(0, 0, 0, 0.4);
|
|
446
|
+
--shadow-panel: -5px 0 30px rgba(0,0,0,0.5);
|
|
443
447
|
|
|
444
448
|
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
445
449
|
|
|
446
450
|
/* Tag Pastel Palette */
|
|
447
|
-
--tag-0-bg: rgba(254, 202, 202, 0.2); --tag-0-fg: #fca5a5;
|
|
448
|
-
--tag-1-bg: rgba(253, 230, 138, 0.2); --tag-1-fg: #fcd34d;
|
|
449
|
-
--tag-2-bg: rgba(187, 247, 208, 0.2); --tag-2-fg: #86efac;
|
|
450
|
-
--tag-3-bg: rgba(165, 243, 252, 0.2); --tag-3-fg: #67e8f9;
|
|
451
|
-
--tag-4-bg: rgba(191, 219, 254, 0.2); --tag-4-fg: #93c5fd;
|
|
452
|
-
--tag-5-bg: rgba(233, 213, 255, 0.2); --tag-5-fg: #d8b4fe;
|
|
453
|
-
--tag-6-bg: rgba(251, 207, 232, 0.2); --tag-6-fg: #f9a8d4;
|
|
451
|
+
--tag-0-bg: rgba(254, 202, 202, 0.2); --tag-0-fg: #fca5a5;
|
|
452
|
+
--tag-1-bg: rgba(253, 230, 138, 0.2); --tag-1-fg: #fcd34d;
|
|
453
|
+
--tag-2-bg: rgba(187, 247, 208, 0.2); --tag-2-fg: #86efac;
|
|
454
|
+
--tag-3-bg: rgba(165, 243, 252, 0.2); --tag-3-fg: #67e8f9;
|
|
455
|
+
--tag-4-bg: rgba(191, 219, 254, 0.2); --tag-4-fg: #93c5fd;
|
|
456
|
+
--tag-5-bg: rgba(233, 213, 255, 0.2); --tag-5-fg: #d8b4fe;
|
|
457
|
+
--tag-6-bg: rgba(251, 207, 232, 0.2); --tag-6-fg: #f9a8d4;
|
|
454
458
|
}
|
|
455
459
|
|
|
456
460
|
* { box-sizing: border-box; }
|
|
457
461
|
|
|
458
462
|
html, body {
|
|
459
|
-
height: 100%;
|
|
460
|
-
margin: 0;
|
|
461
|
-
padding: 0;
|
|
463
|
+
height: 100%; margin: 0; padding: 0;
|
|
462
464
|
background-color: var(--bg-root);
|
|
463
465
|
color: var(--text-main);
|
|
464
466
|
font-family: system-ui, -apple-system, sans-serif;
|
|
465
|
-
-webkit-font-smoothing: antialiased;
|
|
466
467
|
}
|
|
467
468
|
|
|
468
469
|
body {
|
|
@@ -473,345 +474,91 @@ body {
|
|
|
473
474
|
}
|
|
474
475
|
|
|
475
476
|
.page {
|
|
476
|
-
min-height: 100vh;
|
|
477
|
-
|
|
478
|
-
max-width: 1600px;
|
|
479
|
-
margin: 0 auto;
|
|
480
|
-
display: flex;
|
|
481
|
-
flex-direction: column;
|
|
482
|
-
gap: 24px;
|
|
477
|
+
min-height: 100vh; padding: 20px 40px 60px; max-width: 1600px;
|
|
478
|
+
margin: 0 auto; display: flex; flex-direction: column; gap: 24px;
|
|
483
479
|
}
|
|
484
480
|
|
|
485
|
-
/*
|
|
486
|
-
.header {
|
|
487
|
-
display: flex;
|
|
488
|
-
justify-content: space-between;
|
|
489
|
-
align-items: flex-end;
|
|
490
|
-
padding-bottom: 10px;
|
|
491
|
-
border-bottom: 1px solid var(--border-subtle);
|
|
492
|
-
}
|
|
481
|
+
/* Header */
|
|
482
|
+
.header { display: flex; justify-content: space-between; padding-bottom: 10px; border-bottom: 1px solid var(--border-subtle); }
|
|
493
483
|
.header-title h1 {
|
|
494
|
-
font-size: 28px;
|
|
495
|
-
|
|
496
|
-
font-weight: 700;
|
|
497
|
-
letter-spacing: -0.02em;
|
|
498
|
-
background: linear-gradient(to right, #e2e8f0, #a855f7);
|
|
499
|
-
-webkit-background-clip: text;
|
|
500
|
-
-webkit-text-fill-color: transparent;
|
|
484
|
+
font-size: 28px; margin: 0; font-weight: 700; letter-spacing: -0.02em;
|
|
485
|
+
background: linear-gradient(to right, #e2e8f0, #a855f7); -webkit-background-clip: text; -webkit-text-fill-color: transparent;
|
|
501
486
|
}
|
|
502
487
|
|
|
503
|
-
/*
|
|
488
|
+
/* Controls */
|
|
504
489
|
.controls-container {
|
|
505
|
-
position: sticky;
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
backdrop-filter: blur(16px);
|
|
510
|
-
border: 1px solid var(--border-subtle);
|
|
511
|
-
border-radius: var(--radius-card);
|
|
512
|
-
padding: 12px 16px;
|
|
513
|
-
display: flex;
|
|
514
|
-
flex-direction: column;
|
|
515
|
-
gap: 12px;
|
|
490
|
+
position: sticky; top: 0; z-index: 50;
|
|
491
|
+
background: var(--bg-glass); backdrop-filter: blur(16px);
|
|
492
|
+
border: 1px solid var(--border-subtle); border-radius: var(--radius-card);
|
|
493
|
+
padding: 12px 16px; display: flex; flex-direction: column; gap: 12px;
|
|
516
494
|
box-shadow: 0 10px 30px -10px rgba(0,0,0,0.5);
|
|
517
495
|
}
|
|
518
|
-
|
|
519
|
-
.
|
|
520
|
-
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
.schema-branch {
|
|
524
|
-
opacity: 0.6;
|
|
525
|
-
font-family: var(--font-mono);
|
|
526
|
-
margin-right: 2px;
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
.schema-meta code {
|
|
530
|
-
font-family: var(--font-mono);
|
|
531
|
-
font-size: 11px;
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
/* Top Row: Filters */
|
|
535
|
-
.filters-row {
|
|
536
|
-
display: flex;
|
|
537
|
-
align-items: flex-start;
|
|
538
|
-
gap: 16px;
|
|
539
|
-
width: 100%;
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
/* New Wrapper for Search and Methods */
|
|
543
|
-
.left-column {
|
|
544
|
-
display: flex;
|
|
545
|
-
flex-direction: column;
|
|
546
|
-
gap: 16px;
|
|
547
|
-
flex-basis: 300px;
|
|
548
|
-
min-width: 250px;
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
.search-box {
|
|
552
|
-
width: 100%;
|
|
553
|
-
position: relative;
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
/* Tag Filter Group */
|
|
557
|
-
.filter-group.tag-filters-container {
|
|
558
|
-
flex: 1;
|
|
559
|
-
min-width: 200px;
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
/* Method Filter Group */
|
|
563
|
-
.filter-group.method-filters-container {
|
|
564
|
-
}
|
|
565
|
-
|
|
496
|
+
.filters-row { display: flex; align-items: flex-start; gap: 16px; width: 100%; }
|
|
497
|
+
.left-column { display: flex; flex-direction: column; gap: 16px; flex-basis: 300px; min-width: 250px; }
|
|
498
|
+
.search-box { width: 100%; position: relative; }
|
|
566
499
|
.search-input {
|
|
567
|
-
width: 100%;
|
|
568
|
-
|
|
569
|
-
border: 1px solid var(--border-subtle);
|
|
570
|
-
color: var(--text-main);
|
|
571
|
-
padding: 8px 12px 8px 32px;
|
|
572
|
-
border-radius: 6px;
|
|
573
|
-
font-size: 13px;
|
|
574
|
-
transition: all 0.2s;
|
|
575
|
-
}
|
|
576
|
-
.search-input:focus {
|
|
577
|
-
outline: none;
|
|
578
|
-
border-color: var(--accent-primary);
|
|
579
|
-
box-shadow: 0 0 0 2px var(--accent-glow);
|
|
580
|
-
}
|
|
581
|
-
.search-icon {
|
|
582
|
-
position: absolute;
|
|
583
|
-
left: 10px;
|
|
584
|
-
top: 50%;
|
|
585
|
-
transform: translateY(-50%);
|
|
586
|
-
color: var(--text-muted);
|
|
587
|
-
pointer-events: none;
|
|
588
|
-
font-size: 12px;
|
|
500
|
+
width: 100%; background: rgba(2, 6, 23, 0.6); border: 1px solid var(--border-subtle);
|
|
501
|
+
color: var(--text-main); padding: 8px 12px 8px 32px; border-radius: 6px; font-size: 13px;
|
|
589
502
|
}
|
|
503
|
+
.search-input:focus { outline: none; border-color: var(--accent-primary); }
|
|
504
|
+
.search-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--text-muted); font-size: 12px; }
|
|
505
|
+
.filter-group { display: flex; flex-direction: column; gap: 6px; }
|
|
506
|
+
.filter-label { font-size: 10px; text-transform: uppercase; color: var(--text-muted); font-weight: 700; }
|
|
507
|
+
.checkbox-group { display: flex; flex-wrap: wrap; gap: 6px; }
|
|
590
508
|
|
|
591
|
-
|
|
592
|
-
display: flex;
|
|
593
|
-
flex-direction: column;
|
|
594
|
-
gap: 6px;
|
|
595
|
-
}
|
|
596
|
-
.filter-label {
|
|
597
|
-
font-size: 10px;
|
|
598
|
-
text-transform: uppercase;
|
|
599
|
-
letter-spacing: 0.05em;
|
|
600
|
-
color: var(--text-muted);
|
|
601
|
-
font-weight: 700;
|
|
602
|
-
}
|
|
603
|
-
.checkbox-group {
|
|
604
|
-
display: flex;
|
|
605
|
-
flex-wrap: wrap;
|
|
606
|
-
gap: 6px;
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
/* Bottom Row: Group Overview Chips */
|
|
610
|
-
.overview-row {
|
|
611
|
-
display: flex;
|
|
612
|
-
flex-wrap: wrap;
|
|
613
|
-
gap: 8px;
|
|
614
|
-
padding-top: 8px;
|
|
615
|
-
border-top: 1px solid var(--border-subtle);
|
|
616
|
-
align-items: center;
|
|
617
|
-
}
|
|
618
|
-
.overview-label {
|
|
619
|
-
font-size: 10px;
|
|
620
|
-
text-transform: uppercase;
|
|
621
|
-
color: var(--text-muted);
|
|
622
|
-
font-weight: 700;
|
|
623
|
-
margin-right: 4px;
|
|
624
|
-
}
|
|
625
|
-
.schema-indent {
|
|
626
|
-
display: inline-block;
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
.schema-branch {
|
|
630
|
-
opacity: 0.6;
|
|
631
|
-
font-family: var(--font-mono);
|
|
632
|
-
margin-right: 2px;
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
/* Chips/Pills */
|
|
636
|
-
.pill-checkbox { cursor: pointer; user-select: none; }
|
|
509
|
+
/* Pills */
|
|
637
510
|
.pill-checkbox input { display: none; }
|
|
638
511
|
.pill-checkbox span {
|
|
639
|
-
display: inline-block;
|
|
640
|
-
|
|
641
|
-
border-radius: 4px;
|
|
642
|
-
font-size: 11px;
|
|
643
|
-
font-weight: 600;
|
|
644
|
-
background: rgba(30, 41, 59, 0.5);
|
|
645
|
-
color: var(--text-muted);
|
|
646
|
-
border: 1px solid transparent;
|
|
647
|
-
transition: all 0.15s;
|
|
648
|
-
}
|
|
649
|
-
.pill-checkbox input:checked + span {
|
|
650
|
-
background: rgba(168, 85, 247, 0.15);
|
|
651
|
-
color: var(--accent-primary);
|
|
652
|
-
border-color: var(--accent-primary);
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
/* Tag filter pills */
|
|
656
|
-
.tag-filter-pill {
|
|
657
|
-
display: inline-block;
|
|
658
|
-
padding: 3px 8px;
|
|
659
|
-
border-radius: 4px;
|
|
660
|
-
font-size: 11px;
|
|
661
|
-
font-weight: 600;
|
|
662
|
-
background: rgba(30, 41, 59, 0.5);
|
|
663
|
-
color: var(--text-muted);
|
|
664
|
-
border: 1px solid transparent;
|
|
665
|
-
transition: all 0.15s;
|
|
666
|
-
}
|
|
667
|
-
.pill-checkbox.colored-tag input:checked + .tag-filter-pill {
|
|
668
|
-
border-color: var(--accent-primary);
|
|
669
|
-
box-shadow: 0 0 0 1px var(--accent-glow);
|
|
512
|
+
display: inline-block; padding: 3px 8px; border-radius: 4px; font-size: 11px; font-weight: 600;
|
|
513
|
+
background: rgba(30, 41, 59, 0.5); color: var(--text-muted); border: 1px solid transparent; cursor: pointer;
|
|
670
514
|
}
|
|
515
|
+
.pill-checkbox input:checked + span { background: rgba(168, 85, 247, 0.15); color: var(--accent-primary); border-color: var(--accent-primary); }
|
|
516
|
+
.pill-checkbox.colored-tag input:checked + .tag-filter-pill { border-color: var(--accent-primary); box-shadow: 0 0 0 1px var(--accent-glow); }
|
|
671
517
|
|
|
672
|
-
/*
|
|
518
|
+
/* Overview */
|
|
519
|
+
.overview-row { display: flex; flex-wrap: wrap; gap: 8px; padding-top: 8px; border-top: 1px solid var(--border-subtle); align-items: center; }
|
|
520
|
+
.overview-label { font-size: 10px; text-transform: uppercase; color: var(--text-muted); font-weight: 700; }
|
|
673
521
|
.group-chip {
|
|
674
|
-
font-size: 11px;
|
|
675
|
-
|
|
676
|
-
color: var(--text-accent);
|
|
677
|
-
background: rgba(168, 85, 247, 0.08);
|
|
678
|
-
padding: 3px 8px;
|
|
679
|
-
border-radius: 4px;
|
|
680
|
-
transition: all 0.2s;
|
|
681
|
-
border: 1px solid transparent;
|
|
682
|
-
}
|
|
683
|
-
.group-chip:hover {
|
|
684
|
-
background: rgba(168, 85, 247, 0.2);
|
|
685
|
-
border-color: var(--accent-primary);
|
|
686
|
-
color: #fff;
|
|
522
|
+
font-size: 11px; text-decoration: none; color: var(--text-accent); background: rgba(168, 85, 247, 0.08);
|
|
523
|
+
padding: 3px 8px; border-radius: 4px; border: 1px solid transparent;
|
|
687
524
|
}
|
|
525
|
+
.group-chip:hover { background: rgba(168, 85, 247, 0.2); border-color: var(--accent-primary); color: #fff; }
|
|
688
526
|
|
|
689
|
-
/*
|
|
527
|
+
/* Endpoint Cards */
|
|
690
528
|
.api-group { margin-bottom: 40px; scroll-margin-top: 180px; }
|
|
691
|
-
.group-header {
|
|
692
|
-
|
|
693
|
-
align-items: center;
|
|
694
|
-
justify-content: space-between;
|
|
695
|
-
margin-bottom: 16px;
|
|
696
|
-
padding-bottom: 8px;
|
|
697
|
-
border-bottom: 1px solid var(--border-subtle);
|
|
698
|
-
}
|
|
699
|
-
.group-title {
|
|
700
|
-
font-size: 20px;
|
|
701
|
-
font-weight: 600;
|
|
702
|
-
color: var(--text-main);
|
|
703
|
-
margin: 0;
|
|
704
|
-
}
|
|
529
|
+
.group-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; border-bottom: 1px solid var(--border-subtle); }
|
|
530
|
+
.group-title { font-size: 20px; font-weight: 600; margin: 0; padding-bottom: 8px; }
|
|
705
531
|
.group-actions button {
|
|
706
|
-
background: transparent;
|
|
707
|
-
|
|
708
|
-
color: var(--text-muted);
|
|
709
|
-
font-size: 11px;
|
|
710
|
-
padding: 4px 10px;
|
|
711
|
-
border-radius: 4px;
|
|
712
|
-
cursor: pointer;
|
|
713
|
-
margin-left: 8px;
|
|
714
|
-
transition: all 0.2s;
|
|
715
|
-
}
|
|
716
|
-
.group-actions button:hover {
|
|
717
|
-
color: var(--text-main);
|
|
718
|
-
background: rgba(255,255,255,0.05);
|
|
532
|
+
background: transparent; border: 1px solid var(--border-subtle); color: var(--text-muted);
|
|
533
|
+
font-size: 11px; padding: 4px 10px; border-radius: 4px; cursor: pointer; margin-left: 8px;
|
|
719
534
|
}
|
|
720
|
-
|
|
721
|
-
.cards-list { display: flex; flex-direction: column; gap: 12px; }
|
|
722
|
-
|
|
723
|
-
/* --- Endpoint Card --- */
|
|
724
535
|
.endpoint-card {
|
|
725
|
-
background: var(--bg-card);
|
|
726
|
-
|
|
727
|
-
border-radius: var(--radius-card);
|
|
728
|
-
overflow: hidden;
|
|
729
|
-
transition: all 0.2s ease;
|
|
730
|
-
cursor: pointer;
|
|
731
|
-
backdrop-filter: blur(10px);
|
|
732
|
-
}
|
|
733
|
-
.endpoint-card:hover {
|
|
734
|
-
background: var(--bg-card-hover);
|
|
735
|
-
border-color: rgba(148, 163, 184, 0.4);
|
|
736
|
-
box-shadow: var(--shadow-card);
|
|
737
|
-
}
|
|
738
|
-
.endpoint-card[data-expanded="true"] {
|
|
739
|
-
background: rgba(30, 41, 59, 0.6);
|
|
740
|
-
border-color: var(--accent-primary);
|
|
741
|
-
box-shadow: 0 0 0 1px var(--accent-glow), var(--shadow-card);
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
/* Card Header */
|
|
745
|
-
.card-header {
|
|
746
|
-
padding: 12px 16px;
|
|
747
|
-
display: flex;
|
|
748
|
-
align-items: center;
|
|
749
|
-
gap: 12px;
|
|
750
|
-
flex-wrap: wrap;
|
|
536
|
+
background: var(--bg-card); border: 1px solid var(--border-subtle); border-radius: var(--radius-card);
|
|
537
|
+
overflow: hidden; transition: all 0.2s ease; margin-bottom: 12px;
|
|
751
538
|
}
|
|
539
|
+
.endpoint-card:hover { border-color: rgba(148, 163, 184, 0.4); box-shadow: var(--shadow-card); }
|
|
540
|
+
.card-header { padding: 12px 16px; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; cursor: pointer; }
|
|
541
|
+
.header-spacer { flex: 1; }
|
|
542
|
+
.card-body { display: none; padding: 0 16px 16px; border-top: 1px solid var(--border-subtle); margin-top: 4px; }
|
|
543
|
+
.endpoint-card[data-expanded="true"] .card-body { display: block; animation: slideDown 0.2s ease-out; }
|
|
544
|
+
.endpoint-card[data-expanded="true"] .expand-icon { transform: rotate(180deg); color: var(--text-main); }
|
|
545
|
+
@keyframes slideDown { from { opacity: 0; transform: translateY(-5px); } to { opacity: 1; transform: translateY(0); } }
|
|
752
546
|
|
|
753
|
-
/*
|
|
754
|
-
.method-badge {
|
|
755
|
-
font-size: 11px;
|
|
756
|
-
font-weight: 800;
|
|
757
|
-
text-transform: uppercase;
|
|
758
|
-
padding: 4px 8px;
|
|
759
|
-
border-radius: 4px;
|
|
760
|
-
min-width: 55px;
|
|
761
|
-
text-align: center;
|
|
762
|
-
letter-spacing: 0.05em;
|
|
763
|
-
flex-shrink: 0;
|
|
764
|
-
}
|
|
547
|
+
/* Method Badges */
|
|
548
|
+
.method-badge { font-size: 11px; font-weight: 800; text-transform: uppercase; padding: 4px 8px; border-radius: 4px; min-width: 55px; text-align: center; }
|
|
765
549
|
.m-GET { background: var(--method-get-bg); color: var(--method-get); border: 1px solid rgba(74, 222, 128, 0.3); }
|
|
766
550
|
.m-POST { background: var(--method-post-bg); color: var(--method-post); border: 1px solid rgba(96, 165, 250, 0.3); }
|
|
767
551
|
.m-PUT { background: var(--method-put-bg); color: var(--method-put); border: 1px solid rgba(250, 204, 21, 0.3); }
|
|
768
552
|
.m-PATCH { background: var(--method-patch-bg); color: var(--method-patch); border: 1px solid rgba(45, 212, 191, 0.3); }
|
|
769
553
|
.m-DELETE { background: var(--method-delete-bg); color: var(--method-delete); border: 1px solid rgba(248, 113, 113, 0.3); }
|
|
770
554
|
|
|
771
|
-
|
|
772
|
-
.path-container {
|
|
773
|
-
|
|
774
|
-
font-size: 13px;
|
|
775
|
-
color: var(--text-main);
|
|
776
|
-
display: inline-flex;
|
|
777
|
-
align-items: center;
|
|
778
|
-
gap: 8px;
|
|
779
|
-
cursor: pointer;
|
|
780
|
-
padding: 2px 6px;
|
|
781
|
-
border-radius: 4px;
|
|
782
|
-
transition: background 0.2s;
|
|
783
|
-
}
|
|
784
|
-
.path-container:hover {
|
|
785
|
-
background: rgba(255,255,255,0.05);
|
|
786
|
-
}
|
|
787
|
-
.path-text {
|
|
788
|
-
font-weight: 500;
|
|
789
|
-
}
|
|
790
|
-
.copy-icon {
|
|
791
|
-
opacity: 0;
|
|
792
|
-
color: var(--text-muted);
|
|
793
|
-
font-size: 10px;
|
|
794
|
-
transition: opacity 0.2s;
|
|
795
|
-
}
|
|
555
|
+
.path-container { font-family: var(--font-mono); font-size: 13px; display: inline-flex; align-items: center; gap: 8px; padding: 2px 6px; border-radius: 4px; }
|
|
556
|
+
.path-container:hover { background: rgba(255,255,255,0.05); }
|
|
557
|
+
.copy-icon { opacity: 0; font-size: 10px; }
|
|
796
558
|
.path-container:hover .copy-icon { opacity: 1; }
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
display: flex;
|
|
801
|
-
align-items: center;
|
|
802
|
-
gap: 6px;
|
|
803
|
-
flex-wrap: wrap;
|
|
804
|
-
}
|
|
805
|
-
.status-badge {
|
|
806
|
-
font-size: 9px;
|
|
807
|
-
text-transform: uppercase;
|
|
808
|
-
padding: 2px 6px;
|
|
809
|
-
border-radius: 3px;
|
|
810
|
-
border: 1px solid transparent;
|
|
811
|
-
font-weight: 600;
|
|
812
|
-
letter-spacing: 0.03em;
|
|
813
|
-
}
|
|
814
|
-
/* Tag Colors mapped via JS classes */
|
|
559
|
+
.tags-container { display: flex; gap: 6px; }
|
|
560
|
+
.status-badge { font-size: 9px; text-transform: uppercase; padding: 2px 6px; border-radius: 3px; font-weight: 600; }
|
|
561
|
+
/* Tag colors */
|
|
815
562
|
.tag-0 { background: var(--tag-0-bg); color: var(--tag-0-fg); border-color: var(--tag-0-fg); }
|
|
816
563
|
.tag-1 { background: var(--tag-1-bg); color: var(--tag-1-fg); border-color: var(--tag-1-fg); }
|
|
817
564
|
.tag-2 { background: var(--tag-2-bg); color: var(--tag-2-fg); border-color: var(--tag-2-fg); }
|
|
@@ -819,651 +566,784 @@ body {
|
|
|
819
566
|
.tag-4 { background: var(--tag-4-bg); color: var(--tag-4-fg); border-color: var(--tag-4-fg); }
|
|
820
567
|
.tag-5 { background: var(--tag-5-bg); color: var(--tag-5-fg); border-color: var(--tag-5-fg); }
|
|
821
568
|
.tag-6 { background: var(--tag-6-bg); color: var(--tag-6-fg); border-color: var(--tag-6-fg); }
|
|
822
|
-
|
|
823
569
|
.not-implemented { border-color: var(--method-delete); color: var(--method-delete); background: var(--method-delete-bg); }
|
|
824
570
|
|
|
825
|
-
/*
|
|
826
|
-
.
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
font-size: 10px;
|
|
832
|
-
margin-left: 8px;
|
|
833
|
-
}
|
|
834
|
-
.endpoint-card[data-expanded="true"] .expand-icon {
|
|
835
|
-
transform: rotate(180deg);
|
|
836
|
-
color: var(--text-main);
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
/* Card Body */
|
|
840
|
-
.card-body {
|
|
841
|
-
display: none;
|
|
842
|
-
padding: 0 16px 16px;
|
|
843
|
-
border-top: 1px solid var(--border-subtle);
|
|
844
|
-
margin-top: 4px;
|
|
845
|
-
cursor: default;
|
|
846
|
-
}
|
|
847
|
-
.endpoint-card[data-expanded="true"] .card-body {
|
|
848
|
-
display: block;
|
|
849
|
-
animation: slideDown 0.2s ease-out;
|
|
850
|
-
}
|
|
851
|
-
@keyframes slideDown {
|
|
852
|
-
from { opacity: 0; transform: translateY(-5px); }
|
|
853
|
-
to { opacity: 1; transform: translateY(0); }
|
|
571
|
+
/* Use Endpoint Button */
|
|
572
|
+
.btn-try-it {
|
|
573
|
+
background: rgba(168, 85, 247, 0.1); border: 1px solid rgba(168, 85, 247, 0.4);
|
|
574
|
+
color: var(--text-accent); font-size: 11px; font-weight: 600; padding: 4px 12px;
|
|
575
|
+
border-radius: 12px; cursor: pointer; transition: all 0.2s; margin-right: 12px;
|
|
576
|
+
display: flex; align-items: center; gap: 6px;
|
|
854
577
|
}
|
|
578
|
+
.btn-try-it:hover { background: rgba(168, 85, 247, 0.25); color: #fff; box-shadow: 0 0 10px var(--accent-glow); }
|
|
579
|
+
.btn-try-it .play-icon { font-size: 10px; }
|
|
855
580
|
|
|
581
|
+
/* Section Titles */
|
|
856
582
|
.section-block { margin-top: 18px; }
|
|
857
|
-
.section-title {
|
|
858
|
-
font-size: 11px;
|
|
859
|
-
text-transform: uppercase;
|
|
860
|
-
letter-spacing: 0.1em;
|
|
861
|
-
color: var(--text-muted);
|
|
862
|
-
margin-bottom: 8px;
|
|
863
|
-
border-bottom: 1px solid var(--border-subtle);
|
|
864
|
-
padding-bottom: 4px;
|
|
865
|
-
}
|
|
866
|
-
.summary-text { font-size: 14px; color: var(--text-main); line-height: 1.5; }
|
|
583
|
+
.section-title { font-size: 11px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-muted); margin-bottom: 8px; border-bottom: 1px solid var(--border-subtle); padding-bottom: 4px; }
|
|
867
584
|
|
|
868
|
-
|
|
869
|
-
font-size: 12px;
|
|
870
|
-
opacity: 0.7;
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
/* Tables */
|
|
585
|
+
/* Tables & Schema */
|
|
874
586
|
.schema-table { width: 100%; border-collapse: collapse; font-size: 12px; }
|
|
875
|
-
.schema-table th {
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
}
|
|
879
|
-
.schema-table td {
|
|
880
|
-
padding: 6px 8px; border-bottom: 1px solid var(--border-subtle); vertical-align: top; color: var(--text-muted);
|
|
881
|
-
}
|
|
882
|
-
.col-name { font-family: var(--font-mono); color: var(--text-accent) !important; width: 20%; }
|
|
587
|
+
.schema-table th { text-align: left; color: var(--text-muted); font-weight: 600; padding: 6px 8px; border-bottom: 1px solid var(--border-subtle); background: rgba(0,0,0,0.2); }
|
|
588
|
+
.schema-table td { padding: 6px 8px; border-bottom: 1px solid var(--border-subtle); vertical-align: top; color: var(--text-muted); }
|
|
589
|
+
.col-name { font-family: var(--font-mono); color: var(--text-accent) !important; width: 25%; }
|
|
883
590
|
.col-type { font-family: var(--font-mono); color: #93c5fd !important; width: 15%; }
|
|
884
591
|
.req-badge { font-size: 9px; text-transform: uppercase; padding: 2px 4px; border-radius: 2px; }
|
|
885
592
|
.req-true { color: #4ade80; background: rgba(74, 222, 128, 0.1); }
|
|
886
593
|
.req-false { color: #94a3b8; background: rgba(148, 163, 184, 0.1); }
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
594
|
+
.schema-toggle {
|
|
595
|
+
background: none; border: none; color: var(--text-muted); cursor: pointer; font-size: 10px;
|
|
596
|
+
padding: 0 4px; margin-right: 4px; width: 16px; text-align: center;
|
|
597
|
+
}
|
|
598
|
+
.schema-toggle:hover { color: var(--text-main); }
|
|
599
|
+
tr[data-hidden="true"] { display: none; }
|
|
600
|
+
|
|
601
|
+
/* --- Playground Panel --- */
|
|
602
|
+
.playground-overlay {
|
|
603
|
+
position: fixed; top: 0; right: 0; bottom: 0; width: 600px; max-width: 90vw;
|
|
604
|
+
background: var(--bg-panel); border-left: 1px solid var(--border-subtle);
|
|
605
|
+
box-shadow: var(--shadow-panel); z-index: 100;
|
|
606
|
+
transform: translateX(100%); transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
|
607
|
+
display: flex; flex-direction: column;
|
|
608
|
+
}
|
|
609
|
+
.playground-overlay.open { transform: translateX(0); }
|
|
610
|
+
|
|
611
|
+
.pg-header {
|
|
612
|
+
padding: 16px; border-bottom: 1px solid var(--border-subtle);
|
|
613
|
+
display: flex; justify-content: space-between; align-items: center;
|
|
614
|
+
background: var(--bg-glass);
|
|
893
615
|
}
|
|
894
|
-
|
|
895
|
-
.
|
|
616
|
+
.pg-title { font-size: 16px; font-weight: 700; color: var(--text-main); }
|
|
617
|
+
.pg-close { background: transparent; border: none; color: var(--text-muted); font-size: 20px; cursor: pointer; }
|
|
618
|
+
.pg-close:hover { color: var(--text-main); }
|
|
619
|
+
|
|
620
|
+
.pg-content { flex: 1; overflow-y: auto; padding: 20px; display: flex; flex-direction: column; gap: 24px; }
|
|
621
|
+
|
|
622
|
+
.pg-section { display: flex; flex-direction: column; gap: 8px; }
|
|
623
|
+
.pg-label { font-size: 11px; text-transform: uppercase; color: var(--text-muted); font-weight: 700; letter-spacing: 0.05em; }
|
|
624
|
+
.pg-input, .pg-textarea {
|
|
625
|
+
background: rgba(2, 6, 23, 0.4); border: 1px solid var(--border-subtle);
|
|
626
|
+
color: var(--text-main); padding: 8px; border-radius: 6px; font-family: var(--font-mono); font-size: 12px;
|
|
627
|
+
}
|
|
628
|
+
.pg-input:focus, .pg-textarea:focus { outline: none; border-color: var(--accent-primary); }
|
|
629
|
+
.pg-textarea { min-height: 150px; resize: vertical; }
|
|
630
|
+
|
|
631
|
+
/* Param Grid */
|
|
632
|
+
.param-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
|
|
633
|
+
.param-row { display: contents; }
|
|
634
|
+
|
|
635
|
+
.pg-footer {
|
|
636
|
+
padding: 16px; border-top: 1px solid var(--border-subtle); background: var(--bg-glass);
|
|
637
|
+
display: flex; justify-content: flex-end; gap: 12px;
|
|
638
|
+
}
|
|
639
|
+
.btn-send {
|
|
640
|
+
background: var(--accent-primary); color: white; border: none; padding: 8px 24px;
|
|
641
|
+
border-radius: 6px; font-weight: 600; cursor: pointer; transition: opacity 0.2s;
|
|
642
|
+
}
|
|
643
|
+
.btn-send:hover { opacity: 0.9; }
|
|
644
|
+
.btn-send:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
645
|
+
|
|
646
|
+
/* Response View */
|
|
647
|
+
.response-box { background: rgba(0,0,0,0.3); border-radius: 8px; border: 1px solid var(--border-subtle); overflow: hidden; }
|
|
648
|
+
.response-meta {
|
|
649
|
+
background: rgba(255,255,255,0.05); padding: 8px 12px;
|
|
650
|
+
display: flex; gap: 12px; font-size: 11px; font-family: var(--font-mono); border-bottom: 1px solid var(--border-subtle);
|
|
651
|
+
}
|
|
652
|
+
.meta-item { display: flex; align-items: center; gap: 4px; }
|
|
653
|
+
.status-ok { color: var(--accent-success); }
|
|
654
|
+
.status-err { color: var(--accent-error); }
|
|
655
|
+
.response-body { padding: 12px; overflow-x: auto; font-family: var(--font-mono); font-size: 12px; }
|
|
656
|
+
|
|
657
|
+
/* JSON Tree */
|
|
658
|
+
.json-tree { font-family: var(--font-mono); line-height: 1.6; }
|
|
659
|
+
.jt-obj, .jt-arr { margin-left: 14px; }
|
|
660
|
+
.jt-key { color: #93c5fd; margin-right: 4px; }
|
|
661
|
+
.jt-str { color: #86efac; }
|
|
662
|
+
.jt-num { color: #fca5a5; }
|
|
663
|
+
.jt-bool { color: #fcd34d; }
|
|
664
|
+
.jt-null { color: #94a3b8; }
|
|
665
|
+
.jt-toggle {
|
|
666
|
+
display: inline-block; width: 12px; text-align: center; cursor: pointer; color: var(--text-muted);
|
|
667
|
+
user-select: none; margin-left: -14px;
|
|
668
|
+
}
|
|
669
|
+
.jt-toggle:hover { color: var(--text-main); }
|
|
670
|
+
.jt-collapsed .jt-content { display: none; }
|
|
671
|
+
.jt-collapsed::after { content: " ... "; color: var(--text-muted); font-size: 10px; }
|
|
896
672
|
`;
|
|
897
673
|
var DOCS_JS = `
|
|
898
674
|
(function() {
|
|
899
675
|
let leaves = [];
|
|
676
|
+
let activeLeaf = null;
|
|
677
|
+
let activeRequest = {
|
|
678
|
+
pathParams: {},
|
|
679
|
+
queryParams: {},
|
|
680
|
+
body: ''
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
// --- Initialization ---
|
|
900
684
|
try {
|
|
901
685
|
leaves = JSON.parse(document.getElementById('leaf-data').textContent || '[]');
|
|
902
686
|
} catch(e) { console.error('Failed to parse docs', e); }
|
|
903
687
|
|
|
904
|
-
|
|
905
|
-
const filters = {
|
|
906
|
-
search: '',
|
|
907
|
-
methods: new Set(),
|
|
908
|
-
tags: new Set()
|
|
909
|
-
};
|
|
688
|
+
const filters = { search: '', methods: new Set(), tags: new Set() };
|
|
910
689
|
|
|
911
|
-
//
|
|
690
|
+
// Elements
|
|
912
691
|
const elRouteList = document.getElementById('routeList');
|
|
913
692
|
const elOverview = document.getElementById('overviewList');
|
|
914
693
|
const elSearch = document.getElementById('searchInput');
|
|
915
694
|
const elMethodFilters = document.getElementById('methodFilters');
|
|
916
695
|
const elTagFilters = document.getElementById('tagFilters');
|
|
917
696
|
|
|
918
|
-
//
|
|
919
|
-
|
|
697
|
+
// Playground Elements
|
|
698
|
+
let elPlayground, elPgTitle, elPgContent, elPgClose, elPgSend, elPgMethod, elPgUrlPreview, elPgBaseUrl;
|
|
920
699
|
|
|
921
|
-
// Initialization
|
|
922
700
|
function init() {
|
|
701
|
+
injectPlaygroundHTML();
|
|
923
702
|
populateFilters();
|
|
924
703
|
setupGlobalListeners();
|
|
704
|
+
setupPlaygroundListeners();
|
|
925
705
|
render();
|
|
926
706
|
}
|
|
927
707
|
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
708
|
+
function injectPlaygroundHTML() {
|
|
709
|
+
const div = document.createElement('div');
|
|
710
|
+
div.className = 'playground-overlay';
|
|
711
|
+
div.id = 'playgroundPanel';
|
|
712
|
+
div.innerHTML =
|
|
713
|
+
'<div class="pg-header">' +
|
|
714
|
+
'<div class="pg-title"><span id="pgMethod" class="method-badge">GET</span> <span id="pgTitle">Endpoint</span></div>' +
|
|
715
|
+
'<button class="pg-close" id="pgClose">×</button>' +
|
|
716
|
+
'</div>' +
|
|
717
|
+
'<div class="pg-content" id="pgContent">' +
|
|
718
|
+
// Dynamic content goes here
|
|
719
|
+
'</div>' +
|
|
720
|
+
'<div class="pg-footer">' +
|
|
721
|
+
'<button class="btn-send" id="pgSend">Send Request</button>' +
|
|
722
|
+
'</div>';
|
|
723
|
+
document.body.appendChild(div);
|
|
724
|
+
|
|
725
|
+
elPlayground = document.getElementById('playgroundPanel');
|
|
726
|
+
elPgTitle = document.getElementById('pgTitle');
|
|
727
|
+
elPgContent = document.getElementById('pgContent');
|
|
728
|
+
elPgClose = document.getElementById('pgClose');
|
|
729
|
+
elPgSend = document.getElementById('pgSend');
|
|
730
|
+
elPgMethod = document.getElementById('pgMethod');
|
|
937
731
|
}
|
|
938
732
|
|
|
939
|
-
//
|
|
940
|
-
function sortGroups(a, b) {
|
|
941
|
-
const nameA = a.toUpperCase();
|
|
942
|
-
const nameB = b.toUpperCase();
|
|
943
|
-
if (nameA === 'UNGROUPED') return 1;
|
|
944
|
-
if (nameB === 'UNGROUPED') return -1;
|
|
945
|
-
return nameA.localeCompare(nameB);
|
|
946
|
-
}
|
|
733
|
+
// --- Filtering & Rendering ---
|
|
947
734
|
|
|
948
735
|
function populateFilters() {
|
|
949
736
|
if (!elMethodFilters || !elTagFilters) return;
|
|
950
|
-
|
|
951
|
-
// Extract unique values
|
|
952
737
|
const allMethods = new Set(leaves.map(l => (l.method || 'GET').toUpperCase()));
|
|
953
738
|
const allTags = new Set(leaves.flatMap(l => (l.cfg && l.cfg.tags) ? l.cfg.tags : []));
|
|
954
739
|
|
|
955
|
-
// Setup Method Checkboxes
|
|
956
740
|
const sortedMethods = Array.from(allMethods).sort();
|
|
957
|
-
elMethodFilters.innerHTML = sortedMethods.map(m =>
|
|
958
|
-
'<label class="pill-checkbox">'
|
|
959
|
-
|
|
960
|
-
'<span>' + escapeHtml(m) + '</span>' +
|
|
961
|
-
'</label>'
|
|
962
|
-
)).join('');
|
|
741
|
+
elMethodFilters.innerHTML = sortedMethods.map(m =>
|
|
742
|
+
'<label class="pill-checkbox"><input type="checkbox" value="'+escapeAttr(m)+'" checked><span>'+escapeHtml(m)+'</span></label>'
|
|
743
|
+
).join('');
|
|
963
744
|
sortedMethods.forEach(m => filters.methods.add(m));
|
|
964
745
|
|
|
965
|
-
// Attach listeners to method checkboxes
|
|
966
|
-
elMethodFilters.querySelectorAll('input[type="checkbox"]').forEach(function(cb) {
|
|
967
|
-
cb.addEventListener('change', updateFilters);
|
|
968
|
-
});
|
|
969
|
-
|
|
970
|
-
// Setup Tag Checkboxes
|
|
971
746
|
if (allTags.size > 0) {
|
|
972
|
-
elTagFilters.innerHTML = Array.from(allTags).sort().map(t =>
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
'<label class="pill-checkbox colored-tag">' +
|
|
976
|
-
'<input type="checkbox" value="' + escapeAttr(t) + '">' +
|
|
977
|
-
'<span class="tag-filter-pill ' + colorClass + '">' + escapeHtml(t) + '</span>' +
|
|
978
|
-
'</label>'
|
|
979
|
-
);
|
|
980
|
-
}).join('');
|
|
981
|
-
|
|
982
|
-
// Attach listeners to tag checkboxes
|
|
983
|
-
elTagFilters.querySelectorAll('input[type="checkbox"]').forEach(function(cb) {
|
|
984
|
-
cb.addEventListener('change', updateFilters);
|
|
985
|
-
});
|
|
747
|
+
elTagFilters.innerHTML = Array.from(allTags).sort().map(t =>
|
|
748
|
+
'<label class="pill-checkbox colored-tag"><input type="checkbox" value="'+escapeAttr(t)+'"><span class="tag-filter-pill '+getTagColorClass(t)+'">'+escapeHtml(t)+'</span></label>'
|
|
749
|
+
).join('');
|
|
986
750
|
}
|
|
751
|
+
|
|
752
|
+
document.querySelectorAll('#methodFilters input, #tagFilters input').forEach(cb =>
|
|
753
|
+
cb.addEventListener('change', updateFilters)
|
|
754
|
+
);
|
|
987
755
|
}
|
|
988
756
|
|
|
989
757
|
function updateFilters() {
|
|
990
|
-
if (elSearch)
|
|
991
|
-
filters.search = elSearch.value.toLowerCase();
|
|
992
|
-
}
|
|
993
|
-
|
|
758
|
+
if (elSearch) filters.search = elSearch.value.toLowerCase();
|
|
994
759
|
filters.methods.clear();
|
|
995
|
-
|
|
996
|
-
elMethodFilters.querySelectorAll('input[type="checkbox"]:checked').forEach(function(cb) {
|
|
997
|
-
filters.methods.add(cb.value);
|
|
998
|
-
});
|
|
999
|
-
}
|
|
1000
|
-
|
|
760
|
+
elMethodFilters.querySelectorAll('input:checked').forEach(cb => filters.methods.add(cb.value));
|
|
1001
761
|
filters.tags.clear();
|
|
1002
|
-
|
|
1003
|
-
elTagFilters.querySelectorAll('input[type="checkbox"]:checked').forEach(function(cb) {
|
|
1004
|
-
filters.tags.add(cb.value);
|
|
1005
|
-
});
|
|
1006
|
-
}
|
|
1007
|
-
|
|
762
|
+
elTagFilters.querySelectorAll('input:checked').forEach(cb => filters.tags.add(cb.value));
|
|
1008
763
|
render();
|
|
1009
764
|
}
|
|
1010
765
|
|
|
1011
766
|
function setupGlobalListeners() {
|
|
1012
|
-
if (elSearch)
|
|
1013
|
-
|
|
1014
|
-
|
|
767
|
+
if (elSearch) elSearch.addEventListener('input', updateFilters);
|
|
768
|
+
|
|
769
|
+
// Event Delegation for Card interactions
|
|
770
|
+
document.body.addEventListener('click', function(e) {
|
|
771
|
+
const target = e.target;
|
|
772
|
+
|
|
773
|
+
// Try It Button
|
|
774
|
+
const btnTry = target.closest('.btn-try-it');
|
|
775
|
+
if (btnTry) {
|
|
776
|
+
e.preventDefault(); e.stopPropagation();
|
|
777
|
+
const index = parseInt(btnTry.getAttribute('data-index'), 10);
|
|
778
|
+
openPlayground(leaves[index]);
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
// Expand/Collapse Card
|
|
783
|
+
const cardHeader = target.closest('.card-header');
|
|
784
|
+
if (cardHeader) {
|
|
785
|
+
// Don't toggle if clicked on path (copy) or tags
|
|
786
|
+
if (target.closest('.path-container') || target.closest('.tags-container')) return;
|
|
787
|
+
const card = cardHeader.closest('.endpoint-card');
|
|
788
|
+
const expanded = card.getAttribute('data-expanded') === 'true';
|
|
789
|
+
card.setAttribute('data-expanded', String(!expanded));
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// Schema Toggles
|
|
794
|
+
const toggleBtn = target.closest('.schema-toggle');
|
|
795
|
+
if (toggleBtn) {
|
|
796
|
+
e.preventDefault(); e.stopPropagation();
|
|
797
|
+
toggleSchemaRow(toggleBtn);
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// Schema Expand All / Collapse All
|
|
802
|
+
if (target.matches('.schema-actions button')) {
|
|
803
|
+
e.preventDefault(); e.stopPropagation();
|
|
804
|
+
const table = target.closest('.section-block').querySelector('table');
|
|
805
|
+
const action = target.getAttribute('data-action');
|
|
806
|
+
toggleAllSchema(table, action === 'expand');
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// Copy Path
|
|
811
|
+
const pathContainer = target.closest('.path-container');
|
|
812
|
+
if (pathContainer) {
|
|
813
|
+
e.preventDefault(); e.stopPropagation();
|
|
814
|
+
copyText(pathContainer.getAttribute('data-path'));
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
// Group Chips
|
|
819
|
+
if (target.matches('.group-chip')) {
|
|
820
|
+
e.preventDefault();
|
|
821
|
+
scrollToGroup(target.getAttribute('data-group'));
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// Group Expand/Collapse
|
|
826
|
+
if (target.matches('.group-actions button')) {
|
|
827
|
+
toggleGroup(target.getAttribute('data-group'), target.getAttribute('data-action') === 'expand');
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// JSON Tree Toggles
|
|
832
|
+
if (target.classList.contains('jt-toggle')) {
|
|
833
|
+
e.stopPropagation();
|
|
834
|
+
target.parentElement.classList.toggle('jt-collapsed');
|
|
835
|
+
target.textContent = target.parentElement.classList.contains('jt-collapsed') ? '\u25B6' : '\u25BC';
|
|
836
|
+
}
|
|
837
|
+
});
|
|
1015
838
|
}
|
|
1016
839
|
|
|
1017
|
-
// Core Render Logic
|
|
1018
840
|
function render() {
|
|
1019
841
|
if (!elRouteList || !elOverview) return;
|
|
1020
842
|
|
|
1021
|
-
|
|
1022
|
-
const filtered = leaves.filter(function(leaf) {
|
|
843
|
+
const filtered = leaves.map((leaf, idx) => ({ ...leaf, originalIndex: idx })).filter(leaf => {
|
|
1023
844
|
const m = (leaf.method || 'GET').toUpperCase();
|
|
1024
|
-
const t = (leaf.cfg
|
|
845
|
+
const t = (leaf.cfg.tags || []);
|
|
1025
846
|
const path = (leaf.path || '').toLowerCase();
|
|
1026
|
-
const summary = (leaf.cfg
|
|
847
|
+
const summary = (leaf.cfg.summary || '').toLowerCase();
|
|
1027
848
|
|
|
1028
849
|
if (!filters.methods.has(m)) return false;
|
|
1029
|
-
if (filters.tags.size > 0 && !t.some(
|
|
850
|
+
if (filters.tags.size > 0 && !t.some(tag => filters.tags.has(tag))) return false;
|
|
1030
851
|
if (filters.search && !path.includes(filters.search) && !summary.includes(filters.search)) return false;
|
|
1031
|
-
|
|
1032
852
|
return true;
|
|
1033
853
|
});
|
|
1034
854
|
|
|
1035
855
|
if (filtered.length === 0) {
|
|
1036
|
-
elRouteList.innerHTML = '<div
|
|
856
|
+
elRouteList.innerHTML = '<div style="text-align:center; padding:40px; color:var(--text-muted)">No endpoints match filters.</div>';
|
|
1037
857
|
elOverview.innerHTML = '';
|
|
1038
858
|
return;
|
|
1039
859
|
}
|
|
1040
860
|
|
|
1041
|
-
// 2. Group Data
|
|
1042
861
|
const groups = {};
|
|
1043
|
-
filtered.forEach(
|
|
1044
|
-
const
|
|
1045
|
-
if (!groups[
|
|
1046
|
-
groups[
|
|
862
|
+
filtered.forEach(leaf => {
|
|
863
|
+
const g = leaf.cfg.docsGroup || 'UNGROUPED';
|
|
864
|
+
if (!groups[g]) groups[g] = [];
|
|
865
|
+
groups[g].push(leaf);
|
|
1047
866
|
});
|
|
1048
867
|
|
|
1049
|
-
const
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
'<span class="overview-label">JUMP TO:</span>' +
|
|
1054
|
-
sortedGroupNames.map(function(gName) {
|
|
1055
|
-
return (
|
|
1056
|
-
'<a href="#group-' + escapeAttr(gName) + '" ' +
|
|
1057
|
-
'class="group-chip" data-group="' + escapeAttr(gName) + '">' +
|
|
1058
|
-
escapeHtml(gName) +
|
|
1059
|
-
'</a>'
|
|
1060
|
-
);
|
|
1061
|
-
}).join('')
|
|
1062
|
-
);
|
|
1063
|
-
|
|
1064
|
-
// 4. Render Groups & Cards
|
|
1065
|
-
const html = sortedGroupNames.map(function(gName) {
|
|
1066
|
-
const routes = groups[gName];
|
|
1067
|
-
routes.sort(function(a, b) {
|
|
1068
|
-
const pA = a.path || '';
|
|
1069
|
-
const pB = b.path || '';
|
|
1070
|
-
if (pA < pB) return -1;
|
|
1071
|
-
if (pA > pB) return 1;
|
|
1072
|
-
return (a.method || '').localeCompare(b.method || '');
|
|
1073
|
-
});
|
|
868
|
+
const sortedGroups = Object.keys(groups).sort((a,b) => {
|
|
869
|
+
if (a === 'UNGROUPED') return 1; if (b === 'UNGROUPED') return -1;
|
|
870
|
+
return a.localeCompare(b);
|
|
871
|
+
});
|
|
1074
872
|
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
'</div>' +
|
|
1087
|
-
'</div>'
|
|
1088
|
-
);
|
|
873
|
+
// Render Overview
|
|
874
|
+
elOverview.innerHTML = '<span class="overview-label">JUMP TO:</span>' +
|
|
875
|
+
sortedGroups.map(g => '<a href="#group-'+escapeAttr(g)+'" class="group-chip" data-group="'+escapeAttr(g)+'">'+escapeHtml(g)+'</a>').join('');
|
|
876
|
+
|
|
877
|
+
// Render Cards
|
|
878
|
+
elRouteList.innerHTML = sortedGroups.map(g => {
|
|
879
|
+
const routes = groups[g].sort((a,b) => (a.path||'').localeCompare(b.path||''));
|
|
880
|
+
return '<div class="api-group" id="group-'+escapeAttr(g)+'">' +
|
|
881
|
+
'<div class="group-header"><h2 class="group-title">'+escapeHtml(g)+'</h2>' +
|
|
882
|
+
'<div class="group-actions"><button data-group="'+escapeAttr(g)+'" data-action="expand">Expand All</button><button data-group="'+escapeAttr(g)+'" data-action="collapse">Collapse All</button></div></div>' +
|
|
883
|
+
'<div class="cards-list">' + routes.map(renderCard).join('') + '</div></div>';
|
|
1089
884
|
}).join('');
|
|
1090
|
-
|
|
1091
|
-
elRouteList.innerHTML = html;
|
|
1092
|
-
|
|
1093
|
-
// Wire up interactions that depend on rendered DOM
|
|
1094
|
-
wireOverviewInteractions();
|
|
1095
|
-
wireCardInteractions();
|
|
1096
885
|
}
|
|
1097
886
|
|
|
1098
|
-
// Card Renderer
|
|
1099
887
|
function renderCard(leaf) {
|
|
1100
|
-
const method =
|
|
1101
|
-
const path = leaf.path || '/';
|
|
1102
|
-
const cfg = leaf.cfg || {};
|
|
1103
|
-
const summary = cfg.summary || '';
|
|
1104
|
-
const description = cfg.description || '';
|
|
888
|
+
const { method, path, cfg, originalIndex } = leaf;
|
|
1105
889
|
const tags = cfg.tags || [];
|
|
890
|
+
const methodUc = (method || 'GET').toUpperCase();
|
|
1106
891
|
|
|
1107
|
-
const tagBadges = tags.map(
|
|
1108
|
-
const cClass = getTagColorClass(t);
|
|
1109
|
-
return '<span class="status-badge ' + cClass + '">' + escapeHtml(t) + '</span>';
|
|
1110
|
-
}).join('');
|
|
892
|
+
const tagBadges = tags.map(t => '<span class="status-badge '+getTagColorClass(t)+'">'+escapeHtml(t)+'</span>').join('');
|
|
1111
893
|
|
|
1112
|
-
|
|
894
|
+
return '<article class="endpoint-card" data-expanded="false">' +
|
|
1113
895
|
'<div class="card-header">' +
|
|
1114
|
-
'<span class="method-badge m-'
|
|
1115
|
-
'<div class="path-container" data-path="'
|
|
1116
|
-
|
|
1117
|
-
'<span class="copy-icon">\u{1F4CB}</span>' +
|
|
1118
|
-
'</div>' +
|
|
1119
|
-
'<div class="tags-container">' +
|
|
1120
|
-
tagBadges +
|
|
1121
|
-
'</div>' +
|
|
896
|
+
'<span class="method-badge m-'+escapeAttr(methodUc)+'">'+escapeHtml(methodUc)+'</span>' +
|
|
897
|
+
'<div class="path-container" data-path="'+escapeAttr(path)+'"><span class="path-text">'+escapeHtml(path)+'</span><span class="copy-icon">\u{1F4CB}</span></div>' +
|
|
898
|
+
'<div class="tags-container">'+tagBadges+'</div>' +
|
|
1122
899
|
'<div class="header-spacer"></div>' +
|
|
900
|
+
'<button class="btn-try-it" data-index="'+originalIndex+'"><span class="play-icon">\u25B6</span> Use endpoint</button>' +
|
|
1123
901
|
'<div class="expand-icon">\u25BC</div>' +
|
|
902
|
+
'</div>' +
|
|
903
|
+
'<div class="card-body">' +
|
|
904
|
+
'<div class="section-block"><div class="summary-text"><strong>'+escapeHtml(cfg.summary||'')+'</strong><br><span class="description-text">'+escapeHtml(cfg.description||'')+'</span></div></div>' +
|
|
905
|
+
(cfg.paramsSchema || cfg.querySchema ? renderSection('Parameters', cfg.paramsSchema, cfg.querySchema) : '') +
|
|
906
|
+
(cfg.bodySchema ? renderSection('Request Body ' + (cfg.hasBody?'':'(Optional)'), cfg.bodySchema) : '') +
|
|
907
|
+
(cfg.outputSchema ? renderSection('Response Schema', cfg.outputSchema) : '') +
|
|
908
|
+
'</div></article>';
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
function renderSection(title, schema1, schema2) {
|
|
912
|
+
return '<div class="section-block">' +
|
|
913
|
+
'<div style="display:flex; justify-content:space-between; align-items:flex-end; border-bottom:1px solid var(--border-subtle); margin-bottom:8px;">' +
|
|
914
|
+
'<div class="section-title" style="border:none; margin:0;">'+title+'</div>' +
|
|
915
|
+
'<div class="schema-actions" style="margin-bottom:4px;"><button data-action="expand" style="background:none;border:none;color:var(--text-accent);cursor:pointer;font-size:10px;">Exp All</button> <button data-action="collapse" style="background:none;border:none;color:var(--text-muted);cursor:pointer;font-size:10px;">Col All</button></div>' +
|
|
916
|
+
'</div>' +
|
|
917
|
+
(schema1 ? renderSchemaTable(schema1) : '') +
|
|
918
|
+
(schema2 ? renderSchemaTable(schema2) : '') +
|
|
1124
919
|
'</div>';
|
|
920
|
+
}
|
|
1125
921
|
|
|
1126
|
-
|
|
1127
|
-
'<div class="section-block"><div class="summary-text">' +
|
|
1128
|
-
'<strong>' + escapeHtml(summary) + '</strong>' +
|
|
1129
|
-
(description
|
|
1130
|
-
? '<br><span class="description-text">' + escapeHtml(description) + '</span>'
|
|
1131
|
-
: ''
|
|
1132
|
-
) +
|
|
1133
|
-
'</div></div>';
|
|
922
|
+
// --- Schema Table Logic (Flattened with Depth) ---
|
|
1134
923
|
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
924
|
+
function renderSchemaTable(node) {
|
|
925
|
+
let rows = '';
|
|
926
|
+
// Start rendering. Root node usually doesn't need a row unless it's not an object.
|
|
927
|
+
if (node.kind === 'object' && node.properties) {
|
|
928
|
+
rows = renderObjectChildren(node, 0);
|
|
929
|
+
} else {
|
|
930
|
+
rows = renderNodeRow('(root)', node, 0);
|
|
1140
931
|
}
|
|
932
|
+
return '<table class="schema-table">' +
|
|
933
|
+
'<tr><th class="col-name">Field</th><th class="col-type">Type</th><th>Req</th><th>Description</th></tr>' +
|
|
934
|
+
rows + '</table>';
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
// Returns HTML string for rows
|
|
938
|
+
function renderNodeRow(name, node, depth, parentPath) {
|
|
939
|
+
const hasChildren = isComplexNode(node) && ((node.properties && Object.keys(node.properties).length > 0) || (node.element && isComplexNode(node.element)) || (node.union));
|
|
940
|
+
const currentPath = (parentPath ? parentPath + '.' : '') + name;
|
|
941
|
+
const indent = depth * 16;
|
|
942
|
+
|
|
943
|
+
// NEW: Collapse by default. Hide deeply nested rows (depth > 0) initially.
|
|
944
|
+
const isExpanded = false;
|
|
945
|
+
const isHidden = depth > 0;
|
|
946
|
+
|
|
947
|
+
const styleAttr = isHidden ? 'style="display:none"' : '';
|
|
948
|
+
const rowAttrs = 'data-depth="'+depth+'" data-path="'+escapeAttr(currentPath)+'" '+styleAttr + (hasChildren ? ' data-has-children="true" data-expanded="'+isExpanded+'"' : '');
|
|
949
|
+
|
|
950
|
+
const toggleHtml = hasChildren
|
|
951
|
+
? '<button class="schema-toggle">' + (isExpanded ? '\u25BC' : '\u25B6') + '</button>'
|
|
952
|
+
: '<span style="width:20px; display:inline-block;"></span>';
|
|
1141
953
|
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
954
|
+
const nameCell = '<div style="padding-left:'+indent+'px; display:flex; align-items:center;">' +
|
|
955
|
+
toggleHtml + '<span style="opacity:0.8">'+escapeHtml(name)+'</span></div>';
|
|
956
|
+
|
|
957
|
+
let html = '<tr '+rowAttrs+'>' +
|
|
958
|
+
'<td class="col-name">' + nameCell + '</td>' +
|
|
959
|
+
'<td class="col-type">' + escapeHtml(getTypeLabel(node)) + '</td>' +
|
|
960
|
+
'<td><span class="req-badge '+(node.optional?'req-false':'req-true')+'">'+(node.optional?'OPT':'REQ')+'</span></td>' +
|
|
961
|
+
'<td>' + renderNodeDescription(node) + '</td>' +
|
|
962
|
+
'</tr>';
|
|
963
|
+
|
|
964
|
+
if (hasChildren) {
|
|
965
|
+
if (node.kind === 'object' && node.properties) {
|
|
966
|
+
html += renderObjectChildren(node, depth + 1, currentPath);
|
|
967
|
+
}
|
|
968
|
+
else if (node.kind === 'array' && node.element) {
|
|
969
|
+
// ARRAY FIX: If array element is an object, render its properties directly (removing [item] layer)
|
|
970
|
+
if (node.element.kind === 'object' && node.element.properties) {
|
|
971
|
+
html += renderObjectChildren(node.element, depth + 1, currentPath);
|
|
972
|
+
} else {
|
|
973
|
+
html += renderNodeRow('[item]', node.element, depth + 1, currentPath);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
else if (node.kind === 'union' && node.union) {
|
|
977
|
+
node.union.forEach((u, i) => { html += renderNodeRow('OneOf ('+i+')', u, depth + 1, currentPath); });
|
|
978
|
+
}
|
|
1148
979
|
}
|
|
980
|
+
return html;
|
|
981
|
+
}
|
|
1149
982
|
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
983
|
+
function renderObjectChildren(node, depth, parentPath) {
|
|
984
|
+
if (!node.properties) return '';
|
|
985
|
+
return Object.keys(node.properties).map(key => renderNodeRow(key, node.properties[key], depth, parentPath)).join('');
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
// Schema Toggling Logic (JS)
|
|
989
|
+
function toggleSchemaRow(btn) {
|
|
990
|
+
const tr = btn.closest('tr');
|
|
991
|
+
const wasExpanded = tr.getAttribute('data-expanded') === 'true';
|
|
992
|
+
const isNowExpanded = !wasExpanded;
|
|
993
|
+
const currentDepth = parseInt(tr.getAttribute('data-depth'), 10);
|
|
994
|
+
|
|
995
|
+
// Update State UI
|
|
996
|
+
tr.setAttribute('data-expanded', String(isNowExpanded));
|
|
997
|
+
btn.textContent = isNowExpanded ? '\u25BC' : '\u25B6';
|
|
998
|
+
|
|
999
|
+
// Process Siblings
|
|
1000
|
+
let sibling = tr.nextElementSibling;
|
|
1001
|
+
while (sibling) {
|
|
1002
|
+
const sibDepth = parseInt(sibling.getAttribute('data-depth'), 10);
|
|
1003
|
+
// Stop if we hit a row with equal or less depth (next sibling or uncle)
|
|
1004
|
+
if (isNaN(sibDepth) || sibDepth <= currentDepth) break;
|
|
1005
|
+
|
|
1006
|
+
if (isNowExpanded) {
|
|
1007
|
+
// EXPANDING: We only show this child if its immediate parent is also expanded.
|
|
1008
|
+
checkVisibility(sibling);
|
|
1009
|
+
} else {
|
|
1010
|
+
// COLLAPSING: Hide all descendants regardless of their state
|
|
1011
|
+
sibling.style.display = 'none';
|
|
1012
|
+
}
|
|
1013
|
+
sibling = sibling.nextElementSibling;
|
|
1156
1014
|
}
|
|
1015
|
+
}
|
|
1157
1016
|
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1017
|
+
function checkVisibility(row) {
|
|
1018
|
+
// Walk up previous siblings to find parent and check its expansion state
|
|
1019
|
+
const myDepth = parseInt(row.getAttribute('data-depth'), 10);
|
|
1020
|
+
let prev = row.previousElementSibling;
|
|
1021
|
+
while (prev) {
|
|
1022
|
+
const pDepth = parseInt(prev.getAttribute('data-depth'), 10);
|
|
1023
|
+
if (pDepth < myDepth) {
|
|
1024
|
+
// Found parent
|
|
1025
|
+
// If parent is expanded AND visible, then I should be visible.
|
|
1026
|
+
if (prev.getAttribute('data-expanded') === 'true' && prev.style.display !== 'none') {
|
|
1027
|
+
row.style.display = '';
|
|
1028
|
+
} else {
|
|
1029
|
+
row.style.display = 'none';
|
|
1030
|
+
}
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
1033
|
+
prev = prev.previousElementSibling;
|
|
1034
|
+
}
|
|
1035
|
+
// No parent found (depth 0), always visible
|
|
1036
|
+
row.style.display = '';
|
|
1166
1037
|
}
|
|
1167
1038
|
|
|
1168
|
-
|
|
1039
|
+
function toggleAllSchema(table, expand) {
|
|
1040
|
+
if (!table) return;
|
|
1041
|
+
const rows = table.querySelectorAll('tr[data-has-children="true"]');
|
|
1042
|
+
rows.forEach(row => {
|
|
1043
|
+
row.setAttribute('data-expanded', String(expand));
|
|
1044
|
+
const btn = row.querySelector('.schema-toggle');
|
|
1045
|
+
if(btn) btn.textContent = expand ? '\u25BC' : '\u25B6';
|
|
1046
|
+
});
|
|
1047
|
+
// Update visibility for all rows
|
|
1048
|
+
const allRows = table.querySelectorAll('tr[data-depth]');
|
|
1049
|
+
allRows.forEach(row => {
|
|
1050
|
+
if(parseInt(row.getAttribute('data-depth')) === 0) return;
|
|
1051
|
+
checkVisibility(row);
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1169
1054
|
|
|
1170
|
-
|
|
1171
|
-
let rows = '';
|
|
1055
|
+
// --- Playground Logic ---
|
|
1172
1056
|
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
} else {
|
|
1177
|
-
rows = renderNodeRow('(root)', node, 0);
|
|
1057
|
+
function setupPlaygroundListeners() {
|
|
1058
|
+
if(elPgClose) elPgClose.addEventListener('click', closePlayground);
|
|
1059
|
+
if(elPgSend) elPgSend.addEventListener('click', executeRequest);
|
|
1178
1060
|
}
|
|
1179
1061
|
|
|
1180
|
-
|
|
1181
|
-
(
|
|
1182
|
-
|
|
1183
|
-
: '') +
|
|
1184
|
-
'<table class="schema-table">' +
|
|
1185
|
-
rows +
|
|
1186
|
-
'</table>'
|
|
1187
|
-
);
|
|
1188
|
-
}
|
|
1062
|
+
function closePlayground() {
|
|
1063
|
+
elPlayground.classList.remove('open');
|
|
1064
|
+
}
|
|
1189
1065
|
|
|
1190
|
-
function
|
|
1191
|
-
|
|
1066
|
+
function openPlayground(leaf) {
|
|
1067
|
+
activeLeaf = leaf;
|
|
1068
|
+
elPgTitle.textContent = leaf.path;
|
|
1069
|
+
elPgMethod.textContent = (leaf.method || 'GET').toUpperCase();
|
|
1070
|
+
elPgMethod.className = 'method-badge m-' + elPgMethod.textContent;
|
|
1192
1071
|
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
}
|
|
1072
|
+
// Reset request state
|
|
1073
|
+
activeRequest = { pathParams: {}, queryParams: {}, body: '' };
|
|
1074
|
+
|
|
1075
|
+
renderPlaygroundForm(leaf);
|
|
1076
|
+
elPlayground.classList.add('open');
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
function renderPlaygroundForm(leaf) {
|
|
1080
|
+
// 1. Base URL
|
|
1081
|
+
let html = '<div class="pg-section"><div class="pg-label">Base URL</div>' +
|
|
1082
|
+
'<input class="pg-input" id="pgBaseUrl" value="'+window.location.origin+'"></div>';
|
|
1083
|
+
|
|
1084
|
+
// 2. Path Params
|
|
1085
|
+
// Extract :param or {param} from path
|
|
1086
|
+
const pathParamRegex = /[:{]([a-zA-Z0-9_]+)}?/g;
|
|
1087
|
+
const matches = [...leaf.path.matchAll(pathParamRegex)];
|
|
1088
|
+
|
|
1089
|
+
if (matches.length > 0) {
|
|
1090
|
+
html += '<div class="pg-section"><div class="pg-label">Path Variables</div><div class="param-grid">';
|
|
1091
|
+
matches.forEach(m => {
|
|
1092
|
+
const key = m[1];
|
|
1093
|
+
html += '<div class="param-row">' +
|
|
1094
|
+
'<input class="pg-input" placeholder="'+key+'" disabled value="'+key+'" style="opacity:0.7">' +
|
|
1095
|
+
'<input class="pg-input pg-path-input" data-key="'+key+'" placeholder="value">' +
|
|
1096
|
+
'</div>';
|
|
1097
|
+
});
|
|
1098
|
+
html += '</div></div>';
|
|
1099
|
+
}
|
|
1199
1100
|
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1101
|
+
// 3. Query Params
|
|
1102
|
+
// We check leaf.cfg.querySchema or just provide a generic adder
|
|
1103
|
+
html += '<div class="pg-section"><div class="pg-label">Query Parameters</div>' +
|
|
1104
|
+
'<div class="param-grid" id="pgQueryGrid"></div>' +
|
|
1105
|
+
'<button type="button" id="pgAddQuery" style="font-size:11px;background:none;border:1px dashed var(--border-subtle);color:var(--text-muted);padding:4px;cursor:pointer;">+ Add Parameter</button>' +
|
|
1106
|
+
'</div>';
|
|
1203
1107
|
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
escapeHtml(name);
|
|
1108
|
+
// 4. Body
|
|
1109
|
+
if (['POST','PUT','PATCH'].includes((leaf.method||'').toUpperCase())) {
|
|
1110
|
+
html += '<div class="pg-section"><div class="pg-label">Request Body (JSON)</div>' +
|
|
1111
|
+
'<textarea class="pg-textarea" id="pgBodyInput" placeholder="{ ... }"></textarea>' +
|
|
1112
|
+
'</div>';
|
|
1113
|
+
}
|
|
1211
1114
|
|
|
1212
|
-
|
|
1213
|
-
|
|
1115
|
+
// 5. Response Container
|
|
1116
|
+
html += '<div class="pg-section" id="pgResponseContainer" style="display:none;">' +
|
|
1117
|
+
'<div class="pg-label">Response</div>' +
|
|
1118
|
+
'<div class="response-box">' +
|
|
1119
|
+
'<div class="response-meta" id="pgResponseMeta"></div>' +
|
|
1120
|
+
'<div class="response-body" id="pgResponseBody"></div>' +
|
|
1121
|
+
'</div></div>';
|
|
1214
1122
|
|
|
1215
|
-
|
|
1216
|
-
'<tr>' +
|
|
1217
|
-
'<td class="col-name">' + nameCell + '</td>' +
|
|
1218
|
-
'<td class="col-type">' + escapeHtml(typeLabel) + '</td>' +
|
|
1219
|
-
'<td><span class="req-badge ' + reqClass + '">' + reqText + '</span></td>' +
|
|
1220
|
-
'<td>' + descriptionHtml + '</td>' +
|
|
1221
|
-
'</tr>';
|
|
1123
|
+
elPgContent.innerHTML = html;
|
|
1222
1124
|
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
}
|
|
1125
|
+
// Setup listeners for the dynamic form
|
|
1126
|
+
const btnAddQuery = document.getElementById('pgAddQuery');
|
|
1127
|
+
if(btnAddQuery) btnAddQuery.addEventListener('click', () => addQueryRow());
|
|
1227
1128
|
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
rows += renderNodeRow('[items]', element, depth + 1);
|
|
1129
|
+
// Pre-fill query rows if schema exists
|
|
1130
|
+
if (leaf.cfg.querySchema && leaf.cfg.querySchema.properties) {
|
|
1131
|
+
Object.keys(leaf.cfg.querySchema.properties).forEach(k => addQueryRow(k));
|
|
1132
|
+
} else {
|
|
1133
|
+
addQueryRow();
|
|
1234
1134
|
}
|
|
1235
1135
|
}
|
|
1236
1136
|
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1137
|
+
function addQueryRow(key = '') {
|
|
1138
|
+
const container = document.getElementById('pgQueryGrid');
|
|
1139
|
+
const div = document.createElement('div');
|
|
1140
|
+
div.className = 'param-row';
|
|
1141
|
+
div.innerHTML = '<input class="pg-input pg-query-key" placeholder="Name" value="'+key+'"><input class="pg-input pg-query-val" placeholder="Value">';
|
|
1142
|
+
container.appendChild(div);
|
|
1242
1143
|
}
|
|
1243
1144
|
|
|
1244
|
-
|
|
1245
|
-
}
|
|
1145
|
+
// --- Execution ---
|
|
1246
1146
|
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
node.kind === 'array' ||
|
|
1254
|
-
node.kind === 'union' ||
|
|
1255
|
-
node.kind === 'record' ||
|
|
1256
|
-
node.kind === 'tuple'
|
|
1257
|
-
);
|
|
1258
|
-
}
|
|
1147
|
+
async function executeRequest() {
|
|
1148
|
+
const btn = document.getElementById('pgSend');
|
|
1149
|
+
const resContainer = document.getElementById('pgResponseContainer');
|
|
1150
|
+
const metaEl = document.getElementById('pgResponseMeta');
|
|
1151
|
+
const bodyEl = document.getElementById('pgResponseBody');
|
|
1152
|
+
const baseUrlInput = document.getElementById('pgBaseUrl');
|
|
1259
1153
|
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
function isSimpleNode(node) {
|
|
1264
|
-
return !isComplexNode(node);
|
|
1265
|
-
}
|
|
1154
|
+
btn.disabled = true;
|
|
1155
|
+
btn.textContent = 'Sending...';
|
|
1156
|
+
resContainer.style.display = 'none';
|
|
1266
1157
|
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
function getTypeLabel(node) {
|
|
1271
|
-
let base;
|
|
1272
|
-
|
|
1273
|
-
switch (node.kind) {
|
|
1274
|
-
case 'object':
|
|
1275
|
-
base = 'object';
|
|
1276
|
-
break;
|
|
1277
|
-
|
|
1278
|
-
case 'array':
|
|
1279
|
-
if (!node.element) {
|
|
1280
|
-
base = 'array';
|
|
1281
|
-
} else if (isSimpleNode(node.element)) {
|
|
1282
|
-
base = 'array<' + getTypeLabel(node.element) + '>';
|
|
1283
|
-
} else {
|
|
1284
|
-
base = 'array<object>';
|
|
1285
|
-
}
|
|
1286
|
-
break;
|
|
1158
|
+
try {
|
|
1159
|
+
// Build URL
|
|
1160
|
+
let url = (baseUrlInput.value || window.location.origin).replace(/\\/$/, '') + activeLeaf.path;
|
|
1287
1161
|
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
const
|
|
1291
|
-
|
|
1292
|
-
});
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1162
|
+
// Sub Path Params
|
|
1163
|
+
document.querySelectorAll('.pg-path-input').forEach(input => {
|
|
1164
|
+
const k = input.getAttribute('data-key');
|
|
1165
|
+
const v = input.value.trim();
|
|
1166
|
+
url = url.replace(':'+k, v).replace('{'+k+'}', v);
|
|
1167
|
+
});
|
|
1168
|
+
|
|
1169
|
+
// Build Query
|
|
1170
|
+
const queryParts = [];
|
|
1171
|
+
document.querySelectorAll('.pgQueryGrid .param-row, .param-grid .param-row').forEach(row => {
|
|
1172
|
+
const k = row.querySelector('.pg-query-key');
|
|
1173
|
+
const v = row.querySelector('.pg-query-val');
|
|
1174
|
+
if(k && v && k.value.trim()) {
|
|
1175
|
+
queryParts.push(encodeURIComponent(k.value.trim()) + '=' + encodeURIComponent(v.value.trim()));
|
|
1176
|
+
}
|
|
1177
|
+
});
|
|
1178
|
+
if(queryParts.length) url += '?' + queryParts.join('&');
|
|
1179
|
+
|
|
1180
|
+
// Build Body
|
|
1181
|
+
const method = (activeLeaf.method || 'GET').toUpperCase();
|
|
1182
|
+
const headers = {};
|
|
1183
|
+
let body = null;
|
|
1184
|
+
|
|
1185
|
+
const bodyInput = document.getElementById('pgBodyInput');
|
|
1186
|
+
if (bodyInput && bodyInput.value.trim()) {
|
|
1187
|
+
try {
|
|
1188
|
+
// Validate JSON
|
|
1189
|
+
const j = JSON.parse(bodyInput.value);
|
|
1190
|
+
body = JSON.stringify(j);
|
|
1191
|
+
headers['Content-Type'] = 'application/json';
|
|
1192
|
+
} catch(e) {
|
|
1193
|
+
alert('Invalid JSON in request body');
|
|
1194
|
+
btn.disabled = false; btn.textContent = 'Send Request';
|
|
1195
|
+
return;
|
|
1196
|
+
}
|
|
1296
1197
|
}
|
|
1297
|
-
break;
|
|
1298
1198
|
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1199
|
+
const start = performance.now();
|
|
1200
|
+
const res = await fetch(url, { method, headers, body });
|
|
1201
|
+
const time = (performance.now() - start).toFixed(0);
|
|
1302
1202
|
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1203
|
+
// Read response
|
|
1204
|
+
const status = res.status;
|
|
1205
|
+
const statusText = res.statusText;
|
|
1206
|
+
let resData;
|
|
1207
|
+
const contentType = res.headers.get('content-type') || '';
|
|
1306
1208
|
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1209
|
+
if (contentType.includes('application/json')) {
|
|
1210
|
+
resData = await res.json();
|
|
1211
|
+
} else {
|
|
1212
|
+
resData = await res.text();
|
|
1213
|
+
}
|
|
1310
1214
|
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1215
|
+
// Render Response
|
|
1216
|
+
resContainer.style.display = 'block';
|
|
1217
|
+
const statusClass = (status >= 200 && status < 300) ? 'status-ok' : 'status-err';
|
|
1218
|
+
metaEl.innerHTML = '<div class="meta-item '+statusClass+'">\u2B24 '+status+' '+statusText+'</div><div class="meta-item">\u23F1 '+time+'ms</div>';
|
|
1314
1219
|
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1220
|
+
bodyEl.innerHTML = '';
|
|
1221
|
+
if (typeof resData === 'object' && resData !== null) {
|
|
1222
|
+
bodyEl.appendChild(renderJsonTree(resData));
|
|
1223
|
+
} else {
|
|
1224
|
+
const pre = document.createElement('pre');
|
|
1225
|
+
pre.style.margin = 0;
|
|
1226
|
+
pre.textContent = resData;
|
|
1227
|
+
bodyEl.appendChild(pre);
|
|
1228
|
+
}
|
|
1319
1229
|
|
|
1320
|
-
|
|
1321
|
-
|
|
1230
|
+
} catch (err) {
|
|
1231
|
+
resContainer.style.display = 'block';
|
|
1232
|
+
metaEl.innerHTML = '<div class="meta-item status-err">Network Error</div>';
|
|
1233
|
+
bodyEl.textContent = err.message;
|
|
1234
|
+
} finally {
|
|
1235
|
+
btn.disabled = false;
|
|
1236
|
+
btn.textContent = 'Send Request';
|
|
1237
|
+
}
|
|
1322
1238
|
}
|
|
1323
|
-
return base;
|
|
1324
|
-
}
|
|
1325
1239
|
|
|
1326
|
-
|
|
1327
|
-
* Description cell content:
|
|
1328
|
-
* - main description text
|
|
1329
|
-
* - for enum: allowed values
|
|
1330
|
-
* - for literal: literal value
|
|
1331
|
-
*/
|
|
1332
|
-
function renderNodeDescription(node) {
|
|
1333
|
-
const parts = [];
|
|
1334
|
-
|
|
1335
|
-
if (node.description) {
|
|
1336
|
-
parts.push(escapeHtml(node.description));
|
|
1337
|
-
}
|
|
1240
|
+
// --- JSON Tree Renderer ---
|
|
1338
1241
|
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
parts.push('<div class="schema-meta">Allowed: ' + values + '</div>');
|
|
1346
|
-
}
|
|
1242
|
+
function renderJsonTree(data) {
|
|
1243
|
+
// Recursive DOM generator
|
|
1244
|
+
if (data === null) return createSpan('null', 'jt-null');
|
|
1245
|
+
if (typeof data === 'number') return createSpan(String(data), 'jt-num');
|
|
1246
|
+
if (typeof data === 'boolean') return createSpan(String(data), 'jt-bool');
|
|
1247
|
+
if (typeof data === 'string') return createSpan('"'+data+'"', 'jt-str');
|
|
1347
1248
|
|
|
1348
|
-
|
|
1349
|
-
const
|
|
1350
|
-
|
|
1351
|
-
'<div class="schema-meta">Literal: <code>' + valueStr + '</code></div>'
|
|
1352
|
-
);
|
|
1353
|
-
}
|
|
1249
|
+
const isArr = Array.isArray(data);
|
|
1250
|
+
const container = document.createElement('div');
|
|
1251
|
+
container.className = 'json-tree ' + (isArr ? 'jt-arr' : 'jt-obj');
|
|
1354
1252
|
|
|
1355
|
-
|
|
1356
|
-
|
|
1253
|
+
const toggle = document.createElement('span');
|
|
1254
|
+
toggle.className = 'jt-toggle';
|
|
1255
|
+
toggle.textContent = '\u25BC';
|
|
1357
1256
|
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
*/
|
|
1361
|
-
function escapeHtml(str) {
|
|
1362
|
-
return String(str)
|
|
1363
|
-
.replace(/&/g, '&')
|
|
1364
|
-
.replace(/</g, '<')
|
|
1365
|
-
.replace(/>/g, '>')
|
|
1366
|
-
.replace(/"/g, '"')
|
|
1367
|
-
.replace(/'/g, ''');
|
|
1368
|
-
}
|
|
1257
|
+
const content = document.createElement('div');
|
|
1258
|
+
content.className = 'jt-content';
|
|
1369
1259
|
|
|
1260
|
+
const keys = Object.keys(data);
|
|
1261
|
+
if (keys.length === 0) {
|
|
1262
|
+
container.textContent = isArr ? '[]' : '{}';
|
|
1263
|
+
return container;
|
|
1264
|
+
}
|
|
1370
1265
|
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
if (node.enumValues) return 'enum(' + node.enumValues.join('|') + ')';
|
|
1375
|
-
return node.kind || 'any';
|
|
1376
|
-
}
|
|
1266
|
+
container.appendChild(toggle);
|
|
1267
|
+
container.appendChild(document.createTextNode(isArr ? '[' : '{'));
|
|
1268
|
+
container.appendChild(content);
|
|
1377
1269
|
|
|
1378
|
-
|
|
1270
|
+
keys.forEach((k, i) => {
|
|
1271
|
+
const line = document.createElement('div');
|
|
1272
|
+
line.style.paddingLeft = '16px';
|
|
1379
1273
|
|
|
1380
|
-
|
|
1381
|
-
|
|
1274
|
+
if (!isArr) {
|
|
1275
|
+
const keySpan = createSpan('"'+k+'": ', 'jt-key');
|
|
1276
|
+
line.appendChild(keySpan);
|
|
1277
|
+
}
|
|
1382
1278
|
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
e.preventDefault();
|
|
1387
|
-
const gName = chip.getAttribute('data-group');
|
|
1388
|
-
if (gName) scrollToGroup(gName);
|
|
1389
|
-
});
|
|
1279
|
+
line.appendChild(renderJsonTree(data[k]));
|
|
1280
|
+
if (i < keys.length - 1) line.appendChild(document.createTextNode(','));
|
|
1281
|
+
content.appendChild(line);
|
|
1390
1282
|
});
|
|
1391
1283
|
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
btn.addEventListener('click', function() {
|
|
1395
|
-
const gName = btn.getAttribute('data-group');
|
|
1396
|
-
const action = btn.getAttribute('data-action');
|
|
1397
|
-
if (!gName || !action) return;
|
|
1398
|
-
toggleGroup(gName, action === 'expand');
|
|
1399
|
-
});
|
|
1400
|
-
});
|
|
1284
|
+
container.appendChild(document.createTextNode(isArr ? ']' : '}'));
|
|
1285
|
+
return container;
|
|
1401
1286
|
}
|
|
1402
1287
|
|
|
1403
|
-
function
|
|
1404
|
-
|
|
1288
|
+
function createSpan(text, cls) {
|
|
1289
|
+
const s = document.createElement('span');
|
|
1290
|
+
s.className = cls;
|
|
1291
|
+
s.textContent = text;
|
|
1292
|
+
return s;
|
|
1293
|
+
}
|
|
1405
1294
|
|
|
1406
|
-
|
|
1407
|
-
elRouteList.querySelectorAll('.endpoint-card').forEach(function(card) {
|
|
1408
|
-
card.addEventListener('click', function(e) {
|
|
1409
|
-
var target = e.target;
|
|
1410
|
-
if (target && target.closest && target.closest('.path-container')) {
|
|
1411
|
-
// handled separately for copy
|
|
1412
|
-
return;
|
|
1413
|
-
}
|
|
1414
|
-
toggleCard(card);
|
|
1415
|
-
});
|
|
1416
|
-
});
|
|
1295
|
+
// --- Utilities ---
|
|
1417
1296
|
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1297
|
+
function getTagColorClass(tagName) {
|
|
1298
|
+
if (tagName === 'not-implemented') return 'not-implemented';
|
|
1299
|
+
let hash = 0;
|
|
1300
|
+
for (let i = 0; i < tagName.length; i++) hash = tagName.charCodeAt(i) + ((hash << 5) - hash);
|
|
1301
|
+
return 'tag-' + Math.abs(hash % 7);
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
function getTypeLabel(node) {
|
|
1305
|
+
if (!node) return 'any';
|
|
1306
|
+
if (node.kind === 'array') {
|
|
1307
|
+
if(!node.element) return 'array';
|
|
1308
|
+
return 'array<' + getTypeLabel(node.element) + '>';
|
|
1309
|
+
}
|
|
1310
|
+
if (node.kind === 'union' && node.union) return node.union.map(getTypeLabel).join(' | ');
|
|
1311
|
+
if (node.kind === 'literal') return 'literal';
|
|
1312
|
+
if (node.kind === 'enum') return 'enum';
|
|
1313
|
+
return node.kind || 'any';
|
|
1426
1314
|
}
|
|
1427
1315
|
|
|
1428
|
-
function
|
|
1429
|
-
|
|
1430
|
-
|
|
1316
|
+
function renderNodeDescription(node) {
|
|
1317
|
+
let desc = [];
|
|
1318
|
+
if (node.description) desc.push(escapeHtml(node.description));
|
|
1319
|
+
if (node.kind === 'enum' && node.enumValues) desc.push('Allowed: ' + node.enumValues.map(v=>'<code>'+v+'</code>').join(' | '));
|
|
1320
|
+
if (node.kind === 'literal' && node.literal !== undefined) desc.push('Value: <code>'+JSON.stringify(node.literal)+'</code>');
|
|
1321
|
+
return desc.join('<br>');
|
|
1431
1322
|
}
|
|
1432
1323
|
|
|
1433
|
-
function
|
|
1434
|
-
|
|
1435
|
-
if (!group) return;
|
|
1436
|
-
group.querySelectorAll('.endpoint-card').forEach(function(c) {
|
|
1437
|
-
c.setAttribute('data-expanded', String(!!expand));
|
|
1438
|
-
});
|
|
1324
|
+
function isComplexNode(node) {
|
|
1325
|
+
return ['object','array','union','record','tuple'].includes(node.kind);
|
|
1439
1326
|
}
|
|
1440
1327
|
|
|
1441
|
-
function
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
// ignore
|
|
1445
|
-
});
|
|
1328
|
+
function toggleGroup(gName, expand) {
|
|
1329
|
+
const g = document.getElementById('group-'+gName);
|
|
1330
|
+
if(g) g.querySelectorAll('.endpoint-card').forEach(c => c.setAttribute('data-expanded', String(!!expand)));
|
|
1446
1331
|
}
|
|
1447
1332
|
|
|
1448
1333
|
function scrollToGroup(gName) {
|
|
1449
|
-
const el = document.getElementById('group-'
|
|
1450
|
-
if (el) window.scrollTo({ top: el.offsetTop -
|
|
1334
|
+
const el = document.getElementById('group-'+gName);
|
|
1335
|
+
if (el) window.scrollTo({ top: el.offsetTop - 100, behavior: 'smooth' });
|
|
1451
1336
|
}
|
|
1452
1337
|
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
if (!str && str !== 0) return '';
|
|
1456
|
-
return String(str)
|
|
1457
|
-
.replace(/&/g, '&')
|
|
1458
|
-
.replace(/</g, '<')
|
|
1459
|
-
.replace(/>/g, '>')
|
|
1460
|
-
.replace(/"/g, '"')
|
|
1461
|
-
.replace(/'/g, ''');
|
|
1338
|
+
function copyText(text) {
|
|
1339
|
+
if (navigator.clipboard && text) navigator.clipboard.writeText(text);
|
|
1462
1340
|
}
|
|
1463
1341
|
|
|
1464
|
-
function
|
|
1465
|
-
|
|
1342
|
+
function escapeHtml(str) {
|
|
1343
|
+
if (str === null || str === undefined) return '';
|
|
1344
|
+
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
1466
1345
|
}
|
|
1346
|
+
function escapeAttr(str) { return escapeHtml(str); }
|
|
1467
1347
|
|
|
1468
1348
|
init();
|
|
1469
1349
|
})();
|
|
@@ -1477,42 +1357,36 @@ function renderLeafDocsHTML(leaves, options = {}) {
|
|
|
1477
1357
|
<head>
|
|
1478
1358
|
<meta charset="UTF-8">
|
|
1479
1359
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1480
|
-
<title>API
|
|
1481
|
-
|
|
1360
|
+
<title>API Reference</title>
|
|
1482
1361
|
<style${nonceAttr}>${CSS_STYLES}</style>
|
|
1483
1362
|
${options.inlineCss ? `<style${nonceAttr}>${options.inlineCss}</style>` : ""}
|
|
1484
1363
|
</head>
|
|
1485
1364
|
<body>
|
|
1486
1365
|
<div class="page">
|
|
1487
1366
|
<header class="header">
|
|
1488
|
-
<div class="header-title">
|
|
1489
|
-
<h1>API Reference</h1>
|
|
1490
|
-
</div>
|
|
1367
|
+
<div class="header-title"><h1>API Reference</h1></div>
|
|
1491
1368
|
</header>
|
|
1492
1369
|
|
|
1493
1370
|
<div class="controls-container">
|
|
1494
1371
|
<div class="filters-row">
|
|
1495
1372
|
<div class="left-column">
|
|
1496
|
-
<div class="filter-group
|
|
1373
|
+
<div class="filter-group">
|
|
1497
1374
|
<div class="filter-label">Search</div>
|
|
1498
1375
|
<div class="search-box">
|
|
1499
1376
|
<span class="search-icon">\u{1F50D}</span>
|
|
1500
1377
|
<input type="text" id="searchInput" class="search-input" placeholder="Filter endpoints...">
|
|
1501
1378
|
</div>
|
|
1502
1379
|
</div>
|
|
1503
|
-
|
|
1504
|
-
<div class="filter-group method-filters-container">
|
|
1380
|
+
<div class="filter-group">
|
|
1505
1381
|
<div class="filter-label">Methods</div>
|
|
1506
1382
|
<div class="checkbox-group" id="methodFilters"></div>
|
|
1507
1383
|
</div>
|
|
1508
1384
|
</div>
|
|
1509
|
-
|
|
1510
|
-
<div class="filter-group tag-filters-container" id="tagFilterContainer">
|
|
1385
|
+
<div class="filter-group tag-filters-container">
|
|
1511
1386
|
<div class="filter-label">Tags</div>
|
|
1512
1387
|
<div class="checkbox-group" id="tagFilters"></div>
|
|
1513
1388
|
</div>
|
|
1514
1389
|
</div>
|
|
1515
|
-
|
|
1516
1390
|
<div class="overview-row" id="overviewList"></div>
|
|
1517
1391
|
</div>
|
|
1518
1392
|
|