@snack-kit/lib 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15546,95 +15546,530 @@ var SnackKit = (function (exports, crypto2, url, http, https, http2, util2, zlib
15546
15546
  var KEY_GATEWAY = `${STORAGE_PREFIX}gateway`;
15547
15547
  var KEY_TYPE = `${STORAGE_PREFIX}type`;
15548
15548
  var KEY_SERVER = `${STORAGE_PREFIX}server`;
15549
+ function isChinese() {
15550
+ return /^zh/i.test(navigator.language ?? "");
15551
+ }
15552
+ var T = {
15553
+ gateway: () => isChinese() ? "\u7F51\u5173" : "Gateway",
15554
+ tag: () => isChinese() ? "\u6807\u7B7E" : "Tag",
15555
+ server: () => isChinese() ? "\u670D\u52A1" : "Server",
15556
+ ctx: () => isChinese() ? "\u4E0A\u4E0B\u6587" : "Ctx",
15557
+ ctxLabel: () => isChinese() ? "\u4E0A\u4E0B\u6587 \u2014 \u70B9\u51FB\u884C\u590D\u5236 key" : "Ctx \u2014 click row to copy key",
15558
+ search: () => isChinese() ? "\u641C\u7D22..." : "Search...",
15559
+ noMatch: () => isChinese() ? "\u65E0\u5339\u914D\u9879" : "No results",
15560
+ allTags: () => isChinese() ? "\u5168\u90E8\u6807\u7B7E" : "All tags",
15561
+ selectGateway: () => isChinese() ? "\u9009\u62E9\u7F51\u5173" : "Select gateway",
15562
+ selectServer: () => isChinese() ? "\u9009\u62E9\u670D\u52A1" : "Select server",
15563
+ filterCtx: () => isChinese() ? "\u8FC7\u6EE4\u8DEF\u7531..." : "Filter routes...",
15564
+ selectFirst: () => isChinese() ? "\u2190 \u8BF7\u5148\u9009\u62E9\u670D\u52A1" : "\u2190 Select a server first",
15565
+ loading: () => isChinese() ? "\u52A0\u8F7D\u670D\u52A1\u5217\u8868..." : "Loading...",
15566
+ loadFailed: (m) => isChinese() ? `\u52A0\u8F7D\u5931\u8D25: ${m}` : `Failed: ${m}`,
15567
+ loaded: (n) => isChinese() ? `\u5DF2\u52A0\u8F7D ${n} \u4E2A\u670D\u52A1` : `${n} servers loaded`,
15568
+ switching: () => isChinese() ? "\u5207\u6362\u670D\u52A1\u4E2D..." : "Switching...",
15569
+ switchFailed: (m) => isChinese() ? `\u5207\u6362\u5931\u8D25: ${m}` : `Failed: ${m}`,
15570
+ routes: (name, n) => isChinese() ? `${name} \xB7 ${n} \u6761\u8DEF\u7531` : `${name} \xB7 ${n} routes`,
15571
+ copied: (k) => isChinese() ? `\u5DF2\u590D\u5236: ${k}` : `Copied: ${k}`,
15572
+ copyAll: () => isChinese() ? "\u590D\u5236\u5168\u90E8 JSON" : "Copy all JSON",
15573
+ copyAllDone: () => isChinese() ? "\u5DF2\u590D\u5236\u4E3A JSON" : "Copied as JSON"
15574
+ };
15549
15575
  var PANEL_STYLE = `
15550
15576
  #__snackkit_debugger__ {
15551
15577
  position: fixed;
15552
- bottom: 16px;
15553
- right: 16px;
15578
+ bottom: 20px;
15579
+ right: 20px;
15554
15580
  z-index: 99999;
15555
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
15581
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', sans-serif;
15556
15582
  font-size: 13px;
15557
- color: #e2e8f0;
15583
+ color: #b8ccec;
15584
+ user-select: none;
15558
15585
  }
15586
+
15587
+ /* \u2500\u2500 \u6D6E\u52A8\u6309\u94AE \u2500\u2500 */
15559
15588
  #__snackkit_debugger__ .dbg-toggle {
15560
- width: 40px;
15561
- height: 40px;
15589
+ width: 42px;
15590
+ height: 42px;
15562
15591
  border-radius: 50%;
15563
- background: #3b82f6;
15592
+ background: linear-gradient(135deg, #4f8ef7, #6d6ff5);
15564
15593
  border: none;
15565
15594
  cursor: pointer;
15566
15595
  display: flex;
15567
15596
  align-items: center;
15568
15597
  justify-content: center;
15569
- box-shadow: 0 2px 8px rgba(0,0,0,0.3);
15598
+ box-shadow: 0 4px 14px rgba(79,142,247,0.45);
15570
15599
  margin-left: auto;
15571
- color: #fff;
15572
- font-size: 18px;
15600
+ font-size: 20px;
15601
+ transition: transform 0.15s, box-shadow 0.15s;
15573
15602
  }
15603
+ #__snackkit_debugger__ .dbg-toggle:hover {
15604
+ transform: scale(1.08);
15605
+ box-shadow: 0 6px 20px rgba(79,142,247,0.55);
15606
+ }
15607
+ #__snackkit_debugger__ .dbg-toggle:active { transform: scale(0.94); }
15608
+
15609
+ /* \u2500\u2500 \u9762\u677F\u4E3B\u4F53 \u2500\u2500 */
15574
15610
  #__snackkit_debugger__ .dbg-panel {
15575
- background: #1e293b;
15576
- border: 1px solid #334155;
15577
- border-radius: 8px;
15578
- padding: 12px;
15579
- width: 320px;
15580
- margin-bottom: 8px;
15581
- box-shadow: 0 4px 16px rgba(0,0,0,0.4);
15582
- display: none;
15611
+ background: #1e2638;
15612
+ border: 1px solid #2d3a55;
15613
+ border-radius: 12px;
15614
+ width: 440px;
15615
+ margin-bottom: 10px;
15616
+ box-shadow: 0 8px 32px rgba(0,0,0,0.45), 0 0 0 1px rgba(255,255,255,0.05);
15617
+ overflow: visible;
15618
+ opacity: 0;
15619
+ transform: translateY(8px) scale(0.98);
15620
+ pointer-events: none;
15621
+ transition: opacity 0.18s ease, transform 0.18s ease;
15622
+ }
15623
+ #__snackkit_debugger__ .dbg-panel.open {
15624
+ opacity: 1;
15625
+ transform: translateY(0) scale(1);
15626
+ pointer-events: auto;
15583
15627
  }
15584
- #__snackkit_debugger__ .dbg-panel.open { display: block; }
15585
- #__snackkit_debugger__ .dbg-row {
15586
- margin-bottom: 8px;
15628
+
15629
+ /* \u2500\u2500 \u9762\u677F Header \u2500\u2500 */
15630
+ #__snackkit_debugger__ .dbg-header {
15587
15631
  display: flex;
15588
- flex-direction: column;
15589
- gap: 4px;
15632
+ align-items: center;
15633
+ justify-content: space-between;
15634
+ padding: 10px 14px;
15635
+ background: #171e2e;
15636
+ border-bottom: 1px solid #2d3a55;
15637
+ border-radius: 12px 12px 0 0;
15638
+ }
15639
+ #__snackkit_debugger__ .dbg-header-left {
15640
+ display: flex;
15641
+ align-items: center;
15642
+ gap: 7px;
15643
+ }
15644
+ #__snackkit_debugger__ .dbg-header-dot {
15645
+ width: 7px;
15646
+ height: 7px;
15647
+ border-radius: 50%;
15648
+ background: #34d399;
15649
+ box-shadow: 0 0 6px rgba(52,211,153,0.6);
15590
15650
  }
15591
- #__snackkit_debugger__ label {
15651
+ #__snackkit_debugger__ .dbg-header-dot.loading {
15652
+ background: #fbbf24;
15653
+ box-shadow: 0 0 6px rgba(251,191,36,0.6);
15654
+ animation: dbg-pulse 1s ease-in-out infinite;
15655
+ }
15656
+ #__snackkit_debugger__ .dbg-header-dot.err {
15657
+ background: #f87171;
15658
+ box-shadow: 0 0 6px rgba(248,113,113,0.6);
15659
+ }
15660
+ #__snackkit_debugger__ .dbg-header-title {
15661
+ font-size: 12px;
15662
+ font-weight: 600;
15663
+ color: #dce8fa;
15664
+ letter-spacing: 0.03em;
15665
+ }
15666
+ #__snackkit_debugger__ .dbg-header-meta {
15592
15667
  font-size: 11px;
15593
- color: #94a3b8;
15668
+ color: #4d6080;
15669
+ }
15670
+
15671
+ /* \u2500\u2500 \u9762\u677F\u5185\u5BB9\u533A \u2500\u2500 */
15672
+ #__snackkit_debugger__ .dbg-body {
15673
+ padding: 12px 14px;
15674
+ display: flex;
15675
+ flex-direction: column;
15676
+ gap: 10px;
15677
+ height: 420px;
15678
+ overflow: visible;
15679
+ }
15680
+
15681
+ /* \u2500\u2500 \u5B57\u6BB5\u884C \u2500\u2500 */
15682
+ #__snackkit_debugger__ .dbg-field {
15683
+ display: flex;
15684
+ flex-direction: column;
15685
+ gap: 5px;
15686
+ }
15687
+ #__snackkit_debugger__ .dbg-field-header {
15688
+ display: flex;
15689
+ align-items: center;
15690
+ justify-content: space-between;
15691
+ }
15692
+ #__snackkit_debugger__ .dbg-label {
15693
+ font-size: 10px;
15694
+ font-weight: 600;
15695
+ color: #5e72a0;
15594
15696
  text-transform: uppercase;
15595
- letter-spacing: 0.05em;
15697
+ letter-spacing: 0.07em;
15596
15698
  }
15597
- #__snackkit_debugger__ select {
15598
- background: #0f172a;
15599
- border: 1px solid #334155;
15699
+
15700
+ /* \u2500\u2500 \u4E00\u952E\u590D\u5236\u5168\u90E8 JSON \u6309\u94AE \u2500\u2500 */
15701
+ #__snackkit_debugger__ .dbg-copy-all {
15702
+ display: flex;
15703
+ align-items: center;
15704
+ gap: 4px;
15705
+ background: transparent;
15706
+ border: 1px solid #2d3a55;
15600
15707
  border-radius: 4px;
15601
- color: #e2e8f0;
15602
- padding: 4px 8px;
15603
- font-size: 13px;
15604
- width: 100%;
15708
+ padding: 2px 7px;
15709
+ color: #5e72a0;
15710
+ font-size: 10px;
15711
+ font-family: inherit;
15605
15712
  cursor: pointer;
15713
+ transition: border-color 0.15s, color 0.15s, background 0.15s;
15606
15714
  }
15607
- #__snackkit_debugger__ .dbg-ctx-list {
15715
+ #__snackkit_debugger__ .dbg-copy-all:hover {
15716
+ border-color: #4f8ef7;
15717
+ color: #7aacfa;
15718
+ background: rgba(79,142,247,0.08);
15719
+ }
15720
+ #__snackkit_debugger__ .dbg-copy-all.done {
15721
+ border-color: #34d399;
15722
+ color: #34d399;
15723
+ }
15724
+
15725
+ /* \u2500\u2500 \u81EA\u5B9A\u4E49\u4E0B\u62C9 \u2500\u2500 */
15726
+ #__snackkit_debugger__ .dbg-select {
15727
+ position: relative;
15728
+ }
15729
+ #__snackkit_debugger__ .dbg-select-trigger {
15730
+ display: flex;
15731
+ align-items: center;
15732
+ justify-content: space-between;
15733
+ gap: 6px;
15734
+ background: #252f45;
15735
+ border: 1px solid #2d3a55;
15736
+ border-radius: 6px;
15737
+ padding: 6px 10px;
15738
+ cursor: pointer;
15739
+ transition: border-color 0.15s, background 0.15s;
15740
+ min-height: 32px;
15741
+ }
15742
+ #__snackkit_debugger__ .dbg-select-trigger:hover { border-color: #3d5070; background: #2d3a55; }
15743
+ #__snackkit_debugger__ .dbg-select.open .dbg-select-trigger {
15744
+ border-color: #4f8ef7;
15745
+ background: #2d3a55;
15746
+ }
15747
+ #__snackkit_debugger__ .dbg-select-val {
15748
+ flex: 1;
15749
+ overflow: hidden;
15750
+ text-overflow: ellipsis;
15751
+ white-space: nowrap;
15752
+ font-size: 12px;
15753
+ color: #b8ccec;
15754
+ }
15755
+ #__snackkit_debugger__ .dbg-select-val.placeholder { color: #3d5070; }
15756
+ #__snackkit_debugger__ .dbg-select-arrow {
15757
+ color: #3d5070;
15758
+ font-size: 10px;
15759
+ flex-shrink: 0;
15760
+ transition: transform 0.15s, color 0.15s;
15761
+ }
15762
+ #__snackkit_debugger__ .dbg-select.open .dbg-select-arrow {
15763
+ transform: rotate(180deg);
15764
+ color: #4f8ef7;
15765
+ }
15766
+ #__snackkit_debugger__ .dbg-select-dropdown {
15767
+ position: absolute;
15768
+ bottom: calc(100% + 4px);
15769
+ left: 0;
15770
+ right: 0;
15771
+ background: #1e2638;
15772
+ border: 1px solid #2d3a55;
15773
+ border-radius: 8px;
15774
+ box-shadow: 0 -8px 24px rgba(0,0,0,0.35);
15775
+ overflow: hidden;
15776
+ opacity: 0;
15777
+ transform: translateY(4px);
15778
+ pointer-events: none;
15779
+ transition: opacity 0.14s, transform 0.14s;
15780
+ z-index: 100000;
15781
+ }
15782
+ #__snackkit_debugger__ .dbg-select.open .dbg-select-dropdown {
15783
+ opacity: 1;
15784
+ transform: translateY(0);
15785
+ pointer-events: auto;
15786
+ }
15787
+ #__snackkit_debugger__ .dbg-select-search {
15788
+ display: flex;
15789
+ align-items: center;
15790
+ gap: 6px;
15791
+ padding: 7px 10px;
15792
+ border-bottom: 1px solid #2d3a55;
15793
+ }
15794
+ #__snackkit_debugger__ .dbg-select-search-icon { color: #3d5070; font-size: 11px; flex-shrink: 0; }
15795
+ #__snackkit_debugger__ .dbg-select-search input {
15796
+ flex: 1;
15797
+ background: transparent;
15798
+ border: none;
15799
+ outline: none;
15800
+ color: #b8ccec;
15801
+ font-size: 12px;
15802
+ font-family: inherit;
15803
+ }
15804
+ #__snackkit_debugger__ .dbg-select-search input::placeholder { color: #3d5070; }
15805
+ #__snackkit_debugger__ .dbg-select-list {
15608
15806
  max-height: 160px;
15609
15807
  overflow-y: auto;
15610
- border: 1px solid #334155;
15611
- border-radius: 4px;
15612
- background: #0f172a;
15613
15808
  }
15614
- #__snackkit_debugger__ .dbg-ctx-item {
15615
- padding: 4px 8px;
15616
- border-bottom: 1px solid #1e293b;
15809
+ #__snackkit_debugger__ .dbg-select-list::-webkit-scrollbar { width: 3px; }
15810
+ #__snackkit_debugger__ .dbg-select-list::-webkit-scrollbar-track { background: transparent; }
15811
+ #__snackkit_debugger__ .dbg-select-list::-webkit-scrollbar-thumb { background: #2d3a55; border-radius: 2px; }
15812
+ #__snackkit_debugger__ .dbg-select-option {
15813
+ padding: 7px 10px;
15617
15814
  cursor: pointer;
15815
+ font-size: 12px;
15816
+ color: #8aa4c8;
15817
+ transition: background 0.1s, color 0.1s;
15818
+ overflow: hidden;
15819
+ text-overflow: ellipsis;
15820
+ white-space: nowrap;
15821
+ }
15822
+ #__snackkit_debugger__ .dbg-select-option:hover { background: #2d3a55; color: #dce8fa; }
15823
+ #__snackkit_debugger__ .dbg-select-option.active { color: #7aacfa; background: #1e3060; }
15824
+ #__snackkit_debugger__ .dbg-select-option.hidden { display: none; }
15825
+ #__snackkit_debugger__ .dbg-select-empty {
15826
+ padding: 12px 10px;
15827
+ font-size: 12px;
15828
+ color: #3d5070;
15829
+ text-align: center;
15830
+ }
15831
+
15832
+ /* \u2500\u2500 \u8DEF\u7531\u5217\u8868 \u2500\u2500 */
15833
+ #__snackkit_debugger__ .dbg-ctx-field {
15834
+ flex: 1;
15835
+ min-height: 0;
15836
+ display: flex;
15837
+ flex-direction: column;
15838
+ gap: 5px;
15839
+ }
15840
+ #__snackkit_debugger__ .dbg-ctx-wrap {
15841
+ flex: 1;
15842
+ min-height: 0;
15843
+ border: 1px solid #2d3a55;
15844
+ border-radius: 6px;
15845
+ overflow: hidden;
15846
+ background: #252f45;
15847
+ display: flex;
15848
+ flex-direction: column;
15849
+ }
15850
+ #__snackkit_debugger__ .dbg-ctx-search {
15618
15851
  display: flex;
15619
- justify-content: space-between;
15620
15852
  align-items: center;
15853
+ gap: 6px;
15854
+ padding: 6px 10px;
15855
+ border-bottom: 1px solid #2d3a55;
15856
+ background: #1e2638;
15857
+ }
15858
+ #__snackkit_debugger__ .dbg-ctx-search input {
15859
+ flex: 1;
15860
+ background: transparent;
15861
+ border: none;
15862
+ outline: none;
15863
+ color: #b8ccec;
15864
+ font-size: 12px;
15865
+ font-family: inherit;
15866
+ }
15867
+ #__snackkit_debugger__ .dbg-ctx-search input::placeholder { color: #3d5070; }
15868
+ #__snackkit_debugger__ .dbg-ctx-list {
15869
+ flex: 1;
15870
+ min-height: 0;
15871
+ overflow-y: auto;
15872
+ }
15873
+ #__snackkit_debugger__ .dbg-ctx-list::-webkit-scrollbar { width: 3px; }
15874
+ #__snackkit_debugger__ .dbg-ctx-list::-webkit-scrollbar-track { background: transparent; }
15875
+ #__snackkit_debugger__ .dbg-ctx-list::-webkit-scrollbar-thumb { background: #2d3a55; border-radius: 2px; }
15876
+ #__snackkit_debugger__ .dbg-ctx-item {
15877
+ padding: 5px 10px;
15878
+ cursor: pointer;
15879
+ display: grid;
15880
+ grid-template-columns: minmax(80px, 148px) 1fr auto;
15881
+ gap: 8px;
15882
+ align-items: center;
15883
+ border-bottom: 1px solid #1e2638;
15884
+ transition: background 0.1s;
15621
15885
  }
15622
15886
  #__snackkit_debugger__ .dbg-ctx-item:last-child { border-bottom: none; }
15623
- #__snackkit_debugger__ .dbg-ctx-item:hover { background: #1e293b; }
15624
- #__snackkit_debugger__ .dbg-ctx-key { color: #60a5fa; }
15625
- #__snackkit_debugger__ .dbg-ctx-val { color: #94a3b8; font-size: 11px; max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
15626
- #__snackkit_debugger__ .dbg-status {
15887
+ #__snackkit_debugger__ .dbg-ctx-item:hover { background: #2d3a55; }
15888
+ #__snackkit_debugger__ .dbg-ctx-item.hidden { display: none; }
15889
+ #__snackkit_debugger__ .dbg-ctx-key {
15890
+ font-size: 12px;
15891
+ color: #7aacfa;
15892
+ overflow: hidden;
15893
+ text-overflow: ellipsis;
15894
+ white-space: nowrap;
15895
+ }
15896
+ #__snackkit_debugger__ .dbg-ctx-val {
15897
+ font-size: 11px;
15898
+ color: #4d6080;
15899
+ overflow: hidden;
15900
+ text-overflow: ellipsis;
15901
+ white-space: nowrap;
15902
+ transition: color 0.1s;
15903
+ }
15904
+ #__snackkit_debugger__ .dbg-ctx-item:hover .dbg-ctx-val { color: #7a90b0; }
15905
+ #__snackkit_debugger__ .dbg-ctx-copy {
15906
+ font-size: 11px;
15907
+ color: #3d5070;
15908
+ opacity: 0;
15909
+ flex-shrink: 0;
15910
+ transition: opacity 0.1s, color 0.1s;
15911
+ }
15912
+ #__snackkit_debugger__ .dbg-ctx-item:hover .dbg-ctx-copy { opacity: 1; color: #5e72a0; }
15913
+ #__snackkit_debugger__ .dbg-ctx-item.copied .dbg-ctx-copy { color: #34d399; opacity: 1; }
15914
+ #__snackkit_debugger__ .dbg-ctx-empty {
15915
+ padding: 16px 10px;
15916
+ font-size: 12px;
15917
+ color: #3d5070;
15918
+ text-align: center;
15919
+ }
15920
+
15921
+ /* \u2500\u2500 \u5E95\u90E8\u72B6\u6001\u680F \u2500\u2500 */
15922
+ #__snackkit_debugger__ .dbg-footer {
15923
+ display: flex;
15924
+ align-items: center;
15925
+ gap: 6px;
15926
+ padding: 7px 14px;
15927
+ background: #171e2e;
15928
+ border-top: 1px solid #2d3a55;
15929
+ border-radius: 0 0 12px 12px;
15627
15930
  font-size: 11px;
15628
- color: #64748b;
15629
- margin-top: 4px;
15630
- min-height: 16px;
15931
+ color: #4d6080;
15932
+ min-height: 30px;
15933
+ }
15934
+ #__snackkit_debugger__ .dbg-footer.ok { color: #34d399; }
15935
+ #__snackkit_debugger__ .dbg-footer.err { color: #f87171; }
15936
+ #__snackkit_debugger__ .dbg-footer-dot {
15937
+ width: 4px;
15938
+ height: 4px;
15939
+ border-radius: 50%;
15940
+ background: currentColor;
15941
+ flex-shrink: 0;
15942
+ }
15943
+
15944
+ @keyframes dbg-pulse {
15945
+ 0%, 100% { opacity: 1; }
15946
+ 50% { opacity: 0.35; }
15631
15947
  }
15632
- #__snackkit_debugger__ .dbg-status.ok { color: #22c55e; }
15633
- #__snackkit_debugger__ .dbg-status.err { color: #ef4444; }
15634
15948
  `;
15949
+ var SearchSelect = class {
15950
+ constructor(onChange) {
15951
+ this.options = [];
15952
+ this.selected = "";
15953
+ this.onChange = onChange;
15954
+ this.el = document.createElement("div");
15955
+ this.el.className = "dbg-select";
15956
+ this.trigger = document.createElement("div");
15957
+ this.trigger.className = "dbg-select-trigger";
15958
+ this.valEl = document.createElement("span");
15959
+ this.valEl.className = "dbg-select-val placeholder";
15960
+ const arrow = document.createElement("span");
15961
+ arrow.className = "dbg-select-arrow";
15962
+ arrow.textContent = "\u25BE";
15963
+ this.trigger.appendChild(this.valEl);
15964
+ this.trigger.appendChild(arrow);
15965
+ this.dropdown = document.createElement("div");
15966
+ this.dropdown.className = "dbg-select-dropdown";
15967
+ const searchWrap = document.createElement("div");
15968
+ searchWrap.className = "dbg-select-search";
15969
+ const searchIcon = document.createElement("span");
15970
+ searchIcon.className = "dbg-select-search-icon";
15971
+ searchIcon.textContent = "\u2315";
15972
+ this.searchInput = document.createElement("input");
15973
+ this.searchInput.type = "text";
15974
+ this.searchInput.placeholder = T.search();
15975
+ searchWrap.appendChild(searchIcon);
15976
+ searchWrap.appendChild(this.searchInput);
15977
+ this.listEl = document.createElement("div");
15978
+ this.listEl.className = "dbg-select-list";
15979
+ this.dropdown.appendChild(searchWrap);
15980
+ this.dropdown.appendChild(this.listEl);
15981
+ this.el.appendChild(this.trigger);
15982
+ this.el.appendChild(this.dropdown);
15983
+ this.bindEvents();
15984
+ }
15985
+ bindEvents() {
15986
+ this.trigger.addEventListener("click", (e) => {
15987
+ e.stopPropagation();
15988
+ const isOpen = this.el.classList.contains("open");
15989
+ document.querySelectorAll("#__snackkit_debugger__ .dbg-select.open").forEach((el) => {
15990
+ if (el !== this.el) el.classList.remove("open");
15991
+ });
15992
+ this.el.classList.toggle("open", !isOpen);
15993
+ if (!isOpen) {
15994
+ this.searchInput.value = "";
15995
+ this.filterOptions("");
15996
+ setTimeout(() => this.searchInput.focus(), 50);
15997
+ }
15998
+ });
15999
+ this.searchInput.addEventListener("input", () => {
16000
+ this.filterOptions(this.searchInput.value);
16001
+ });
16002
+ this.searchInput.addEventListener("click", (e) => e.stopPropagation());
16003
+ document.addEventListener("click", () => {
16004
+ this.el.classList.remove("open");
16005
+ });
16006
+ }
16007
+ filterOptions(query) {
16008
+ const q = query.toLowerCase();
16009
+ let visibleCount = 0;
16010
+ this.listEl.querySelectorAll(".dbg-select-option").forEach((opt) => {
16011
+ const match = !q || opt.textContent.toLowerCase().includes(q);
16012
+ opt.classList.toggle("hidden", !match);
16013
+ if (match) visibleCount++;
16014
+ });
16015
+ let emptyEl = this.listEl.querySelector(".dbg-select-empty");
16016
+ if (visibleCount === 0) {
16017
+ if (!emptyEl) {
16018
+ emptyEl = document.createElement("div");
16019
+ emptyEl.className = "dbg-select-empty";
16020
+ emptyEl.textContent = T.noMatch();
16021
+ this.listEl.appendChild(emptyEl);
16022
+ }
16023
+ } else {
16024
+ emptyEl?.remove();
16025
+ }
16026
+ }
16027
+ /** 更新选项列表 */
16028
+ setOptions(options, placeholder) {
16029
+ this.options = options;
16030
+ this.listEl.innerHTML = "";
16031
+ this.valEl.textContent = placeholder;
16032
+ this.valEl.className = "dbg-select-val placeholder";
16033
+ this.selected = "";
16034
+ options.forEach(({ value, label }) => {
16035
+ const opt = document.createElement("div");
16036
+ opt.className = "dbg-select-option";
16037
+ opt.dataset["value"] = value;
16038
+ opt.textContent = label;
16039
+ opt.setAttribute("title", label);
16040
+ opt.addEventListener("click", (e) => {
16041
+ e.stopPropagation();
16042
+ this.select(value, label);
16043
+ this.el.classList.remove("open");
16044
+ });
16045
+ this.listEl.appendChild(opt);
16046
+ });
16047
+ }
16048
+ /** 选中某个值(不触发 onChange) */
16049
+ setValue(value) {
16050
+ const opt = this.options.find((o) => o.value === value);
16051
+ if (opt) this.select(opt.value, opt.label, false);
16052
+ }
16053
+ select(value, label, emit = true) {
16054
+ this.selected = value;
16055
+ this.valEl.textContent = label;
16056
+ this.valEl.className = "dbg-select-val";
16057
+ this.listEl.querySelectorAll(".dbg-select-option").forEach((el) => {
16058
+ el.classList.toggle("active", el.dataset["value"] === value);
16059
+ });
16060
+ if (emit) this.onChange(value);
16061
+ }
16062
+ getValue() {
16063
+ return this.selected;
16064
+ }
16065
+ getElement() {
16066
+ return this.el;
16067
+ }
16068
+ };
15635
16069
  var Debugger = class _Debugger {
15636
16070
  constructor(options) {
15637
16071
  this.servers = [];
16072
+ this.currentCtxMap = {};
15638
16073
  this.options = options;
15639
16074
  this.gateways = Array.isArray(options.gateways) ? options.gateways : [options.gateways];
15640
16075
  }
@@ -15697,146 +16132,167 @@ var SnackKit = (function (exports, crypto2, url, http, https, http2, util2, zlib
15697
16132
  const panel = document.createElement("div");
15698
16133
  panel.className = "dbg-panel";
15699
16134
  this.panelEl = panel;
15700
- const gwRow = this.createRow("\u7F51\u5173 (Gateway)");
15701
- const gwSelect = document.createElement("select");
15702
- this.gateways.forEach((gw) => {
15703
- const opt = document.createElement("option");
15704
- opt.value = gw;
15705
- opt.textContent = gw;
15706
- gwSelect.appendChild(opt);
16135
+ const header = document.createElement("div");
16136
+ header.className = "dbg-header";
16137
+ const headerLeft = document.createElement("div");
16138
+ headerLeft.className = "dbg-header-left";
16139
+ this.headerDot = document.createElement("span");
16140
+ this.headerDot.className = "dbg-header-dot loading";
16141
+ const headerTitle = document.createElement("span");
16142
+ headerTitle.className = "dbg-header-title";
16143
+ headerTitle.textContent = "Snack Kit Debugger";
16144
+ headerLeft.appendChild(this.headerDot);
16145
+ headerLeft.appendChild(headerTitle);
16146
+ header.appendChild(headerLeft);
16147
+ panel.appendChild(header);
16148
+ const body = document.createElement("div");
16149
+ body.className = "dbg-body";
16150
+ this.gwVersion = document.createElement("span");
16151
+ this.gwVersion.className = "dbg-header-meta";
16152
+ this.gwSelect = new SearchSelect((val) => this.onGatewayChange(val));
16153
+ this.gwSelect.setOptions(this.gateways.map((g) => ({ value: g, label: g })), T.selectGateway());
16154
+ body.appendChild(this.createFieldWithAction(T.gateway(), this.gwVersion, this.gwSelect.getElement()));
16155
+ this.typeSelect = new SearchSelect((val) => this.onTypeChange(val));
16156
+ body.appendChild(this.createField(T.tag(), this.typeSelect.getElement()));
16157
+ this.serverSelect = new SearchSelect((val) => this.onServerChange(val));
16158
+ body.appendChild(this.createField(T.server(), this.serverSelect.getElement()));
16159
+ const ctxWrap = document.createElement("div");
16160
+ ctxWrap.className = "dbg-ctx-wrap";
16161
+ const ctxSearch = document.createElement("div");
16162
+ ctxSearch.className = "dbg-ctx-search";
16163
+ const ctxSearchIcon = document.createElement("span");
16164
+ ctxSearchIcon.className = "dbg-select-search-icon";
16165
+ ctxSearchIcon.textContent = "\u2315";
16166
+ this.ctxSearchInput = document.createElement("input");
16167
+ this.ctxSearchInput.type = "text";
16168
+ this.ctxSearchInput.placeholder = T.filterCtx();
16169
+ this.ctxSearchInput.addEventListener("input", () => this.filterCtx(this.ctxSearchInput.value));
16170
+ ctxSearch.appendChild(ctxSearchIcon);
16171
+ ctxSearch.appendChild(this.ctxSearchInput);
16172
+ this.ctxList = document.createElement("div");
16173
+ this.ctxList.className = "dbg-ctx-list";
16174
+ ctxWrap.appendChild(ctxSearch);
16175
+ ctxWrap.appendChild(this.ctxList);
16176
+ this.copyAllBtn = document.createElement("button");
16177
+ this.copyAllBtn.className = "dbg-copy-all";
16178
+ this.copyAllBtn.textContent = T.copyAll();
16179
+ this.copyAllBtn.setAttribute("title", isChinese() ? "\u5C06\u5168\u90E8\u4E0A\u4E0B\u6587\u590D\u5236\u4E3A JSON\uFF0C\u53EF\u4F5C\u4E3A Context.load({...}) \u7684\u53C2\u6570" : "Copy all ctx entries as JSON for Context.load({...})");
16180
+ this.copyAllBtn.addEventListener("click", (e) => {
16181
+ e.stopPropagation();
16182
+ this.copyAllCtx();
15707
16183
  });
15708
- gwSelect.addEventListener("change", () => this.onGatewayChange());
15709
- gwRow.appendChild(gwSelect);
15710
- this.gwSelect = gwSelect;
15711
- panel.appendChild(gwRow);
15712
- const typeRow = this.createRow("\u670D\u52A1\u7C7B\u578B (Type)");
15713
- const typeSelect = document.createElement("select");
15714
- typeSelect.addEventListener("change", () => this.onTypeChange());
15715
- typeRow.appendChild(typeSelect);
15716
- this.typeSelect = typeSelect;
15717
- panel.appendChild(typeRow);
15718
- const serverRow = this.createRow("\u670D\u52A1 (Server)");
15719
- const serverSelect = document.createElement("select");
15720
- serverSelect.addEventListener("change", () => this.onServerChange());
15721
- serverRow.appendChild(serverSelect);
15722
- this.serverSelect = serverSelect;
15723
- panel.appendChild(serverRow);
15724
- const ctxRow = this.createRow("\u8DEF\u7531 (Ctx) - \u70B9\u51FB\u590D\u5236");
15725
- const ctxList = document.createElement("div");
15726
- ctxList.className = "dbg-ctx-list";
15727
- ctxRow.appendChild(ctxList);
15728
- this.ctxList = ctxList;
15729
- panel.appendChild(ctxRow);
15730
- const status = document.createElement("div");
15731
- status.className = "dbg-status";
15732
- this.statusEl = status;
15733
- panel.appendChild(status);
16184
+ const ctxField = this.createFieldWithAction(T.ctxLabel(), this.copyAllBtn, ctxWrap);
16185
+ ctxField.classList.add("dbg-ctx-field");
16186
+ body.appendChild(ctxField);
16187
+ panel.appendChild(body);
16188
+ const footer = document.createElement("div");
16189
+ footer.className = "dbg-footer";
16190
+ this.footerEl = footer;
16191
+ panel.appendChild(footer);
15734
16192
  const toggle = document.createElement("button");
15735
16193
  toggle.className = "dbg-toggle";
15736
16194
  toggle.textContent = "\u{1F41B}";
15737
16195
  toggle.setAttribute("title", "Snack Debugger");
15738
- toggle.addEventListener("click", () => panel.classList.toggle("open"));
16196
+ toggle.addEventListener("click", (e) => {
16197
+ e.stopPropagation();
16198
+ panel.classList.toggle("open");
16199
+ });
15739
16200
  root.appendChild(panel);
15740
16201
  root.appendChild(toggle);
15741
16202
  document.body.appendChild(root);
15742
16203
  }
15743
- createRow(label) {
15744
- const row = document.createElement("div");
15745
- row.className = "dbg-row";
15746
- const lbl = document.createElement("label");
16204
+ /** 创建普通字段行(label + content) */
16205
+ createField(label, content) {
16206
+ const field = document.createElement("div");
16207
+ field.className = "dbg-field";
16208
+ const lbl = document.createElement("div");
16209
+ lbl.className = "dbg-label";
16210
+ lbl.textContent = label;
16211
+ field.appendChild(lbl);
16212
+ field.appendChild(content);
16213
+ return field;
16214
+ }
16215
+ /** 创建带操作按钮的字段行(label + action button + content) */
16216
+ createFieldWithAction(label, action, content) {
16217
+ const field = document.createElement("div");
16218
+ field.className = "dbg-field";
16219
+ const fieldHeader = document.createElement("div");
16220
+ fieldHeader.className = "dbg-field-header";
16221
+ const lbl = document.createElement("div");
16222
+ lbl.className = "dbg-label";
15747
16223
  lbl.textContent = label;
15748
- row.appendChild(lbl);
15749
- return row;
16224
+ fieldHeader.appendChild(lbl);
16225
+ fieldHeader.appendChild(action);
16226
+ field.appendChild(fieldHeader);
16227
+ field.appendChild(content);
16228
+ return field;
15750
16229
  }
15751
16230
  /** 恢复上次持久化的状态 */
15752
16231
  async restoreState() {
15753
16232
  const savedGw = localStorage.getItem(KEY_GATEWAY);
15754
- if (savedGw && this.gateways.includes(savedGw)) {
15755
- this.gwSelect.value = savedGw;
15756
- }
15757
- await this.loadServers(this.gwSelect.value);
16233
+ const gw = savedGw && this.gateways.includes(savedGw) ? savedGw : this.gateways[0];
16234
+ this.gwSelect.setValue(gw);
16235
+ await this.loadServers(gw);
15758
16236
  const savedType = localStorage.getItem(KEY_TYPE);
15759
16237
  if (savedType) {
15760
- this.typeSelect.value = savedType;
16238
+ this.typeSelect.setValue(savedType);
15761
16239
  this.renderServerOptions(savedType);
15762
16240
  }
15763
16241
  const savedServer = localStorage.getItem(KEY_SERVER);
15764
- if (savedServer && this.serverSelect.querySelector(`option[value="${savedServer}"]`)) {
15765
- this.serverSelect.value = savedServer;
16242
+ if (savedServer && this.servers.find((s) => s.key === savedServer)) {
16243
+ this.serverSelect.setValue(savedServer);
15766
16244
  await this.applyServer(savedServer);
15767
16245
  }
15768
16246
  }
15769
- /** 网关切换处理 */
15770
- async onGatewayChange() {
15771
- const gw = this.gwSelect.value;
16247
+ async onGatewayChange(gw) {
15772
16248
  localStorage.setItem(KEY_GATEWAY, gw);
16249
+ this.gwVersion.textContent = "";
15773
16250
  await this.loadServers(gw);
15774
16251
  }
15775
- /** 服务类型切换处理 */
15776
- onTypeChange() {
15777
- const type = this.typeSelect.value;
16252
+ onTypeChange(type) {
15778
16253
  localStorage.setItem(KEY_TYPE, type);
15779
16254
  this.renderServerOptions(type);
15780
16255
  }
15781
- /** 服务切换处理 */
15782
- async onServerChange() {
15783
- const key = this.serverSelect.value;
16256
+ async onServerChange(key) {
15784
16257
  localStorage.setItem(KEY_SERVER, key);
15785
16258
  await this.applyServer(key);
15786
16259
  }
15787
- /**
15788
- * 从网关加载服务列表
15789
- * @param gwUrl 网关 URL
15790
- */
16260
+ /** 从网关加载服务列表 */
15791
16261
  async loadServers(gwUrl) {
15792
- this.setStatus("\u52A0\u8F7D\u4E2D...", "");
16262
+ this.setHeader("loading");
16263
+ this.setFooter(T.loading(), "");
15793
16264
  const result = await Get(`${gwUrl}/web-debug/host/list`, {
15794
16265
  cache: true,
15795
16266
  timeout: this.options.timeout
15796
16267
  });
15797
16268
  if (result.error) {
15798
- this.setStatus(`\u52A0\u8F7D\u5931\u8D25: ${result.error.message}`, "err");
16269
+ this.setHeader("err");
16270
+ this.setFooter(T.loadFailed(result.error.message), "err");
15799
16271
  return;
15800
16272
  }
15801
16273
  this.servers = result.data ?? [];
15802
16274
  this.renderTypeOptions();
15803
- this.setStatus(`\u5DF2\u52A0\u8F7D ${this.servers.length} \u4E2A\u670D\u52A1`, "ok");
16275
+ this.setHeader("ok");
16276
+ this.setFooter(T.loaded(this.servers.length), "ok");
15804
16277
  }
15805
- /** 渲染服务类型选项 */
16278
+ /** 渲染标签选项 */
15806
16279
  renderTypeOptions() {
15807
- const types = [...new Set(this.servers.map((s) => s.type))];
15808
- this.typeSelect.innerHTML = "";
15809
- const allOpt = document.createElement("option");
15810
- allOpt.value = "";
15811
- allOpt.textContent = "\u5168\u90E8";
15812
- this.typeSelect.appendChild(allOpt);
15813
- types.forEach((t) => {
15814
- const opt = document.createElement("option");
15815
- opt.value = t;
15816
- opt.textContent = t;
15817
- this.typeSelect.appendChild(opt);
15818
- });
15819
- this.renderServerOptions(this.typeSelect.value);
16280
+ const types = [...new Set(this.servers.map((s) => s.type).filter(Boolean))];
16281
+ const opts = [{ value: "", label: T.allTags() }, ...types.map((t) => ({ value: t, label: t }))];
16282
+ this.typeSelect.setOptions(opts, T.allTags());
16283
+ this.typeSelect.setValue("");
16284
+ this.renderServerOptions("");
15820
16285
  }
15821
- /** 根据类型渲染服务选项 */
16286
+ /** 根据标签渲染服务选项 */
15822
16287
  renderServerOptions(type) {
15823
16288
  const filtered = type ? this.servers.filter((s) => s.type === type) : this.servers;
15824
- this.serverSelect.innerHTML = "";
15825
- const emptyOpt = document.createElement("option");
15826
- emptyOpt.value = "";
15827
- emptyOpt.textContent = "\u8BF7\u9009\u62E9\u670D\u52A1";
15828
- this.serverSelect.appendChild(emptyOpt);
15829
- filtered.forEach((s) => {
15830
- const opt = document.createElement("option");
15831
- opt.value = s.key;
15832
- opt.textContent = `${s.name} (${s.key})`;
15833
- this.serverSelect.appendChild(opt);
15834
- });
16289
+ this.serverSelect.setOptions(
16290
+ filtered.map((s) => ({ value: s.key, label: `${s.name} (${s.key})` })),
16291
+ T.selectServer()
16292
+ );
16293
+ this.renderCtxList({});
15835
16294
  }
15836
- /**
15837
- * 切换目标服务,重新加载 Context 路由映射
15838
- * @param key 服务 key
15839
- */
16295
+ /** 切换目标服务,重新加载 Context 路由映射 */
15840
16296
  async applyServer(key) {
15841
16297
  if (!key) {
15842
16298
  this.renderCtxList({});
@@ -15844,56 +16300,105 @@ var SnackKit = (function (exports, crypto2, url, http, https, http2, util2, zlib
15844
16300
  }
15845
16301
  const server = this.servers.find((s) => s.key === key);
15846
16302
  if (!server) return;
15847
- this.setStatus("\u5207\u6362\u670D\u52A1\u4E2D...", "");
16303
+ this.setFooter(T.switching(), "");
15848
16304
  try {
15849
16305
  await Context.load(server.origin, this.options.timeout);
15850
- const ctxMap = {};
15851
16306
  const res = await fetch(`${server.origin}/ngw/context`);
15852
16307
  const raw = res.ok ? await res.json() : {};
16308
+ const ctxMap = {};
15853
16309
  for (const [k, v] of Object.entries(raw)) {
15854
16310
  if (k !== "$info" && typeof v === "string") ctxMap[k] = v;
15855
16311
  }
16312
+ const info = Context.info;
16313
+ if (info) this.gwVersion.textContent = `v${info.version}`;
15856
16314
  this.renderCtxList(ctxMap);
15857
- this.setStatus(`\u5DF2\u5207\u6362\u81F3 ${server.name}`, "ok");
16315
+ const count = Object.keys(ctxMap).length;
16316
+ this.setFooter(T.routes(server.name, count), "ok");
15858
16317
  } catch (err) {
15859
16318
  const msg = err instanceof Error ? err.message : String(err);
15860
- this.setStatus(`\u5207\u6362\u5931\u8D25: ${msg}`, "err");
16319
+ this.setFooter(T.switchFailed(msg), "err");
15861
16320
  }
15862
16321
  }
15863
16322
  /** 渲染路由列表 */
15864
16323
  renderCtxList(ctxMap) {
16324
+ this.currentCtxMap = ctxMap;
15865
16325
  this.ctxList.innerHTML = "";
16326
+ this.ctxSearchInput.value = "";
15866
16327
  const entries = Object.entries(ctxMap);
15867
16328
  if (entries.length === 0) {
15868
- this.ctxList.textContent = "\u6682\u65E0\u8DEF\u7531";
16329
+ const empty = document.createElement("div");
16330
+ empty.className = "dbg-ctx-empty";
16331
+ empty.textContent = T.selectFirst();
16332
+ this.ctxList.appendChild(empty);
15869
16333
  return;
15870
16334
  }
15871
16335
  entries.forEach(([k, v]) => {
15872
16336
  const item = document.createElement("div");
15873
16337
  item.className = "dbg-ctx-item";
16338
+ item.dataset["key"] = k;
15874
16339
  const keySpan = document.createElement("span");
15875
16340
  keySpan.className = "dbg-ctx-key";
15876
16341
  keySpan.textContent = k;
16342
+ keySpan.setAttribute("title", k);
15877
16343
  const valSpan = document.createElement("span");
15878
16344
  valSpan.className = "dbg-ctx-val";
15879
16345
  valSpan.textContent = v;
15880
- valSpan.setAttribute("title", v);
16346
+ valSpan.setAttribute("title", `${k} \u2192 ${v}`);
16347
+ const copyIcon = document.createElement("span");
16348
+ copyIcon.className = "dbg-ctx-copy";
16349
+ copyIcon.textContent = "\u2398";
15881
16350
  item.appendChild(keySpan);
15882
16351
  item.appendChild(valSpan);
15883
- item.addEventListener("click", () => this.copyText(k));
16352
+ item.appendChild(copyIcon);
16353
+ item.addEventListener("click", () => this.copyCtxItem(item, k));
15884
16354
  this.ctxList.appendChild(item);
15885
16355
  });
15886
16356
  }
15887
- /** 复制文本到剪贴板 */
15888
- copyText(text) {
16357
+ /** 过滤路由列表 */
16358
+ filterCtx(query) {
16359
+ const q = query.toLowerCase();
16360
+ this.ctxList.querySelectorAll(".dbg-ctx-item").forEach((item) => {
16361
+ const key = item.dataset["key"] ?? "";
16362
+ item.classList.toggle("hidden", !!q && !key.toLowerCase().includes(q));
16363
+ });
16364
+ }
16365
+ /** 复制单个 ctx key */
16366
+ copyCtxItem(item, text) {
15889
16367
  navigator.clipboard.writeText(text).then(() => {
15890
- this.setStatus(`\u5DF2\u590D\u5236: ${text}`, "ok");
15891
- setTimeout(() => this.setStatus("", ""), 2e3);
16368
+ item.classList.add("copied");
16369
+ const icon = item.querySelector(".dbg-ctx-copy");
16370
+ if (icon) icon.textContent = "\u2713";
16371
+ setTimeout(() => {
16372
+ item.classList.remove("copied");
16373
+ const ic = item.querySelector(".dbg-ctx-copy");
16374
+ if (ic) ic.textContent = "\u2398";
16375
+ }, 1500);
16376
+ this.setFooter(T.copied(text), "ok");
16377
+ setTimeout(() => this.setFooter("", ""), 2e3);
16378
+ });
16379
+ }
16380
+ /** 一键复制全部上下文为 JSON */
16381
+ copyAllCtx() {
16382
+ const entries = Object.keys(this.currentCtxMap);
16383
+ if (entries.length === 0) return;
16384
+ const json = JSON.stringify(this.currentCtxMap, null, 2);
16385
+ navigator.clipboard.writeText(json).then(() => {
16386
+ this.copyAllBtn.textContent = "\u2713 " + T.copyAllDone();
16387
+ this.copyAllBtn.classList.add("done");
16388
+ setTimeout(() => {
16389
+ this.copyAllBtn.textContent = T.copyAll();
16390
+ this.copyAllBtn.classList.remove("done");
16391
+ }, 2e3);
16392
+ this.setFooter(T.copyAllDone(), "ok");
16393
+ setTimeout(() => this.setFooter("", ""), 2500);
15892
16394
  });
15893
16395
  }
15894
- setStatus(msg, cls) {
15895
- this.statusEl.textContent = msg;
15896
- this.statusEl.className = `dbg-status${cls ? ` ${cls}` : ""}`;
16396
+ setHeader(state) {
16397
+ this.headerDot.className = `dbg-header-dot${state !== "ok" ? ` ${state}` : ""}`;
16398
+ }
16399
+ setFooter(msg, cls) {
16400
+ this.footerEl.innerHTML = msg ? `<span class="dbg-footer-dot"></span>${msg}` : "";
16401
+ this.footerEl.className = `dbg-footer${cls ? ` ${cls}` : ""}`;
15897
16402
  }
15898
16403
  };
15899
16404
  /*! Bundled license information: