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