@matdata/yasgui 5.5.0 → 5.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/build/ts/src/ConfigExportImport.js +3 -0
  2. package/build/ts/src/ConfigExportImport.js.map +1 -1
  3. package/build/ts/src/OAuth2Utils.d.ts +18 -0
  4. package/build/ts/src/OAuth2Utils.js +214 -0
  5. package/build/ts/src/OAuth2Utils.js.map +1 -0
  6. package/build/ts/src/PersistentConfig.d.ts +3 -0
  7. package/build/ts/src/PersistentConfig.js +7 -0
  8. package/build/ts/src/PersistentConfig.js.map +1 -1
  9. package/build/ts/src/Tab.d.ts +1 -1
  10. package/build/ts/src/Tab.js +116 -85
  11. package/build/ts/src/Tab.js.map +1 -1
  12. package/build/ts/src/TabSettingsModal.d.ts +1 -0
  13. package/build/ts/src/TabSettingsModal.js +330 -27
  14. package/build/ts/src/TabSettingsModal.js.map +1 -1
  15. package/build/ts/src/defaults.js +1 -1
  16. package/build/ts/src/defaults.js.map +1 -1
  17. package/build/ts/src/index.d.ts +21 -6
  18. package/build/ts/src/index.js +7 -1
  19. package/build/ts/src/index.js.map +1 -1
  20. package/build/ts/src/version.d.ts +1 -1
  21. package/build/ts/src/version.js +1 -1
  22. package/build/yasgui.min.css +1 -1
  23. package/build/yasgui.min.css.map +3 -3
  24. package/build/yasgui.min.js +185 -157
  25. package/build/yasgui.min.js.map +4 -4
  26. package/package.json +3 -2
  27. package/src/ConfigExportImport.ts +3 -0
  28. package/src/OAuth2Utils.ts +315 -0
  29. package/src/PersistentConfig.ts +10 -0
  30. package/src/Tab.ts +191 -111
  31. package/src/TabSettingsModal.scss +70 -3
  32. package/src/TabSettingsModal.ts +400 -30
  33. package/src/defaults.ts +1 -1
  34. package/src/endpointSelect.scss +12 -0
  35. package/src/index.ts +42 -10
  36. package/src/tab.scss +1 -0
  37. package/src/themes.scss +1 -0
  38. package/src/version.ts +1 -1
@@ -11,6 +11,8 @@ import { addClass, removeClass } from "@matdata/yasgui-utils";
11
11
  import "./TabSettingsModal.scss";
12
12
  import * as ConfigExportImport from "./ConfigExportImport";
13
13
  import { VERSION } from "./version";
14
+ import * as OAuth2Utils from "./OAuth2Utils";
15
+ import PersistentConfig from "./PersistentConfig";
14
16
  const MOON_ICON = `<svg viewBox="0 0 24 24" fill="currentColor">
15
17
  <path d="M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9 9-4.03 9-9c0-.46-.04-.92-.1-1.36-.98 1.37-2.58 2.26-4.4 2.26-2.98 0-5.4-2.42-5.4-5.4 0-1.81.89-3.42 2.26-4.4-.44-.06-.9-.1-1.36-.1z"/>
16
18
  </svg>`;
@@ -36,6 +38,7 @@ const AcceptHeaderGraphMap = [
36
38
  { key: "CSV", value: "text/csv,*/*;q=0.9" },
37
39
  { key: "TSV", value: "text/tab-separated-values,*/*;q=0.9" },
38
40
  ];
41
+ const DEFAULT_API_KEY_HEADER = "X-API-Key";
39
42
  export default class TabSettingsModal {
40
43
  constructor(tab, controlBarEl) {
41
44
  this.tab = tab;
@@ -73,7 +76,6 @@ export default class TabSettingsModal {
73
76
  createModal() {
74
77
  this.modalOverlay = document.createElement("div");
75
78
  addClass(this.modalOverlay, "tabSettingsModalOverlay");
76
- this.modalOverlay.onclick = () => this.close();
77
79
  this.modalContent = document.createElement("div");
78
80
  addClass(this.modalContent, "tabSettingsModal");
79
81
  this.modalContent.onclick = (e) => e.stopPropagation();
@@ -326,7 +328,9 @@ export default class TabSettingsModal {
326
328
  const snippetsBarCheckbox = document.createElement("input");
327
329
  snippetsBarCheckbox.type = "checkbox";
328
330
  snippetsBarCheckbox.id = "showSnippetsBar";
329
- snippetsBarCheckbox.checked = yasqe.getSnippetsBarVisible();
331
+ const persistedValue = this.tab.yasgui.persistentConfig.getShowSnippetsBar();
332
+ snippetsBarCheckbox.checked =
333
+ persistedValue !== undefined ? persistedValue : this.tab.yasgui.config.showSnippetsBar !== false;
330
334
  const snippetsBarLabel = document.createElement("label");
331
335
  snippetsBarLabel.htmlFor = "showSnippetsBar";
332
336
  snippetsBarLabel.textContent = "Show code snippets bar";
@@ -538,9 +542,27 @@ export default class TabSettingsModal {
538
542
  basicOption.value = "basic";
539
543
  basicOption.textContent = "HTTP Basic Authentication";
540
544
  typeSelect.appendChild(basicOption);
545
+ const bearerOption = document.createElement("option");
546
+ bearerOption.value = "bearer";
547
+ bearerOption.textContent = "Bearer Token";
548
+ typeSelect.appendChild(bearerOption);
549
+ const apiKeyOption = document.createElement("option");
550
+ apiKeyOption.value = "apiKey";
551
+ apiKeyOption.textContent = "API Key (Custom Header)";
552
+ typeSelect.appendChild(apiKeyOption);
553
+ const oauth2Option = document.createElement("option");
554
+ oauth2Option.value = "oauth2";
555
+ oauth2Option.textContent = "OAuth 2.0";
556
+ typeSelect.appendChild(oauth2Option);
557
+ if (existingAuth) {
558
+ typeSelect.value = existingAuth.type;
559
+ }
541
560
  typeSection.appendChild(typeLabel);
542
561
  typeSection.appendChild(typeSelect);
543
562
  body.appendChild(typeSection);
563
+ const basicAuthFields = document.createElement("div");
564
+ basicAuthFields.id = "basicAuthFields";
565
+ addClass(basicAuthFields, "authFieldsContainer");
544
566
  const usernameSection = document.createElement("div");
545
567
  addClass(usernameSection, "authModalSection");
546
568
  const usernameLabel = document.createElement("label");
@@ -548,11 +570,11 @@ export default class TabSettingsModal {
548
570
  const usernameInput = document.createElement("input");
549
571
  usernameInput.type = "text";
550
572
  usernameInput.placeholder = "Enter username";
551
- usernameInput.value = (existingAuth === null || existingAuth === void 0 ? void 0 : existingAuth.username) || "";
573
+ usernameInput.value = (existingAuth === null || existingAuth === void 0 ? void 0 : existingAuth.type) === "basic" ? existingAuth.username : "";
552
574
  usernameInput.autocomplete = "username";
553
575
  usernameSection.appendChild(usernameLabel);
554
576
  usernameSection.appendChild(usernameInput);
555
- body.appendChild(usernameSection);
577
+ basicAuthFields.appendChild(usernameSection);
556
578
  const passwordSection = document.createElement("div");
557
579
  addClass(passwordSection, "authModalSection");
558
580
  const passwordLabel = document.createElement("label");
@@ -560,11 +582,153 @@ export default class TabSettingsModal {
560
582
  const passwordInput = document.createElement("input");
561
583
  passwordInput.type = "password";
562
584
  passwordInput.placeholder = "Enter password";
563
- passwordInput.value = (existingAuth === null || existingAuth === void 0 ? void 0 : existingAuth.password) || "";
585
+ passwordInput.value = (existingAuth === null || existingAuth === void 0 ? void 0 : existingAuth.type) === "basic" ? existingAuth.password : "";
564
586
  passwordInput.autocomplete = "current-password";
565
587
  passwordSection.appendChild(passwordLabel);
566
588
  passwordSection.appendChild(passwordInput);
567
- body.appendChild(passwordSection);
589
+ basicAuthFields.appendChild(passwordSection);
590
+ body.appendChild(basicAuthFields);
591
+ const bearerAuthFields = document.createElement("div");
592
+ bearerAuthFields.id = "bearerAuthFields";
593
+ addClass(bearerAuthFields, "authFieldsContainer");
594
+ bearerAuthFields.style.display = "none";
595
+ const tokenSection = document.createElement("div");
596
+ addClass(tokenSection, "authModalSection");
597
+ const tokenLabel = document.createElement("label");
598
+ tokenLabel.textContent = "Bearer Token";
599
+ const tokenInput = document.createElement("input");
600
+ tokenInput.type = "password";
601
+ tokenInput.placeholder = "Enter bearer token";
602
+ tokenInput.autocomplete = "off";
603
+ tokenInput.value = (existingAuth === null || existingAuth === void 0 ? void 0 : existingAuth.type) === "bearer" ? existingAuth.token : "";
604
+ tokenSection.appendChild(tokenLabel);
605
+ tokenSection.appendChild(tokenInput);
606
+ bearerAuthFields.appendChild(tokenSection);
607
+ body.appendChild(bearerAuthFields);
608
+ const apiKeyAuthFields = document.createElement("div");
609
+ apiKeyAuthFields.id = "apiKeyAuthFields";
610
+ addClass(apiKeyAuthFields, "authFieldsContainer");
611
+ apiKeyAuthFields.style.display = "none";
612
+ const headerNameSection = document.createElement("div");
613
+ addClass(headerNameSection, "authModalSection");
614
+ const headerNameLabel = document.createElement("label");
615
+ headerNameLabel.textContent = "Header Name";
616
+ const headerNameInput = document.createElement("input");
617
+ headerNameInput.type = "text";
618
+ headerNameInput.placeholder = `e.g., ${DEFAULT_API_KEY_HEADER}`;
619
+ headerNameInput.value = (existingAuth === null || existingAuth === void 0 ? void 0 : existingAuth.type) === "apiKey" ? existingAuth.headerName : DEFAULT_API_KEY_HEADER;
620
+ headerNameSection.appendChild(headerNameLabel);
621
+ headerNameSection.appendChild(headerNameInput);
622
+ apiKeyAuthFields.appendChild(headerNameSection);
623
+ const apiKeySection = document.createElement("div");
624
+ addClass(apiKeySection, "authModalSection");
625
+ const apiKeyLabel = document.createElement("label");
626
+ apiKeyLabel.textContent = "API Key";
627
+ const apiKeyInput = document.createElement("input");
628
+ apiKeyInput.type = "password";
629
+ apiKeyInput.placeholder = "Enter API key";
630
+ apiKeyInput.autocomplete = "off";
631
+ apiKeyInput.value = (existingAuth === null || existingAuth === void 0 ? void 0 : existingAuth.type) === "apiKey" ? existingAuth.apiKey : "";
632
+ apiKeySection.appendChild(apiKeyLabel);
633
+ apiKeySection.appendChild(apiKeyInput);
634
+ apiKeyAuthFields.appendChild(apiKeySection);
635
+ body.appendChild(apiKeyAuthFields);
636
+ const oauth2AuthFields = document.createElement("div");
637
+ oauth2AuthFields.id = "oauth2AuthFields";
638
+ addClass(oauth2AuthFields, "authFieldsContainer");
639
+ oauth2AuthFields.style.display = "none";
640
+ const clientIdSection = document.createElement("div");
641
+ addClass(clientIdSection, "authModalSection");
642
+ const clientIdLabel = document.createElement("label");
643
+ clientIdLabel.textContent = "Client ID";
644
+ const clientIdInput = document.createElement("input");
645
+ clientIdInput.type = "text";
646
+ clientIdInput.placeholder = "Enter OAuth 2.0 client ID";
647
+ clientIdInput.value = (existingAuth === null || existingAuth === void 0 ? void 0 : existingAuth.type) === "oauth2" ? existingAuth.clientId : "";
648
+ clientIdSection.appendChild(clientIdLabel);
649
+ clientIdSection.appendChild(clientIdInput);
650
+ oauth2AuthFields.appendChild(clientIdSection);
651
+ const authEndpointSection = document.createElement("div");
652
+ addClass(authEndpointSection, "authModalSection");
653
+ const authEndpointLabel = document.createElement("label");
654
+ authEndpointLabel.textContent = "Authorization Endpoint";
655
+ const authEndpointInput = document.createElement("input");
656
+ authEndpointInput.type = "url";
657
+ authEndpointInput.placeholder = "https://provider.com/oauth/authorize";
658
+ authEndpointInput.value = (existingAuth === null || existingAuth === void 0 ? void 0 : existingAuth.type) === "oauth2" ? existingAuth.authorizationEndpoint : "";
659
+ authEndpointSection.appendChild(authEndpointLabel);
660
+ authEndpointSection.appendChild(authEndpointInput);
661
+ oauth2AuthFields.appendChild(authEndpointSection);
662
+ const tokenEndpointSection = document.createElement("div");
663
+ addClass(tokenEndpointSection, "authModalSection");
664
+ const tokenEndpointLabel = document.createElement("label");
665
+ tokenEndpointLabel.textContent = "Token Endpoint";
666
+ const tokenEndpointInput = document.createElement("input");
667
+ tokenEndpointInput.type = "url";
668
+ tokenEndpointInput.placeholder = "https://provider.com/oauth/token";
669
+ tokenEndpointInput.value = (existingAuth === null || existingAuth === void 0 ? void 0 : existingAuth.type) === "oauth2" ? existingAuth.tokenEndpoint : "";
670
+ tokenEndpointSection.appendChild(tokenEndpointLabel);
671
+ tokenEndpointSection.appendChild(tokenEndpointInput);
672
+ oauth2AuthFields.appendChild(tokenEndpointSection);
673
+ const redirectUriSection = document.createElement("div");
674
+ addClass(redirectUriSection, "authModalSection");
675
+ const redirectUriLabel = document.createElement("label");
676
+ redirectUriLabel.textContent = "Redirect URI (Optional)";
677
+ const redirectUriInput = document.createElement("input");
678
+ redirectUriInput.type = "url";
679
+ redirectUriInput.placeholder = window.location.origin + window.location.pathname;
680
+ redirectUriInput.value = (existingAuth === null || existingAuth === void 0 ? void 0 : existingAuth.type) === "oauth2" ? existingAuth.redirectUri || "" : "";
681
+ const redirectUriHelp = document.createElement("div");
682
+ redirectUriHelp.textContent = "Leave empty to use current page URL. Must be registered with OAuth provider.";
683
+ addClass(redirectUriHelp, "authInputHelp");
684
+ redirectUriSection.appendChild(redirectUriLabel);
685
+ redirectUriSection.appendChild(redirectUriInput);
686
+ redirectUriSection.appendChild(redirectUriHelp);
687
+ oauth2AuthFields.appendChild(redirectUriSection);
688
+ const scopeSection = document.createElement("div");
689
+ addClass(scopeSection, "authModalSection");
690
+ const scopeLabel = document.createElement("label");
691
+ scopeLabel.textContent = "Scope (Optional)";
692
+ const scopeInput = document.createElement("input");
693
+ scopeInput.type = "text";
694
+ scopeInput.placeholder = "e.g., read write";
695
+ scopeInput.value = (existingAuth === null || existingAuth === void 0 ? void 0 : existingAuth.type) === "oauth2" ? existingAuth.scope || "" : "";
696
+ const scopeHelp = document.createElement("div");
697
+ scopeHelp.textContent = "Space-separated list of OAuth 2.0 scopes";
698
+ addClass(scopeHelp, "authInputHelp");
699
+ scopeSection.appendChild(scopeLabel);
700
+ scopeSection.appendChild(scopeInput);
701
+ scopeSection.appendChild(scopeHelp);
702
+ oauth2AuthFields.appendChild(scopeSection);
703
+ if ((existingAuth === null || existingAuth === void 0 ? void 0 : existingAuth.type) === "oauth2" && existingAuth.accessToken) {
704
+ const tokenStatusSection = document.createElement("div");
705
+ addClass(tokenStatusSection, "authModalSection");
706
+ const tokenStatusLabel = document.createElement("label");
707
+ tokenStatusLabel.textContent = "Authentication Status";
708
+ const tokenStatus = document.createElement("div");
709
+ addClass(tokenStatus, "oauth2TokenStatus");
710
+ tokenStatus.innerHTML = "✓ Authenticated";
711
+ addClass(tokenStatus, "authenticated");
712
+ tokenStatusSection.appendChild(tokenStatusLabel);
713
+ tokenStatusSection.appendChild(tokenStatus);
714
+ oauth2AuthFields.appendChild(tokenStatusSection);
715
+ }
716
+ body.appendChild(oauth2AuthFields);
717
+ const toggleAuthFields = () => {
718
+ const authType = typeSelect.value;
719
+ basicAuthFields.style.display = authType === "basic" ? "block" : "none";
720
+ bearerAuthFields.style.display = authType === "bearer" ? "block" : "none";
721
+ apiKeyAuthFields.style.display = authType === "apiKey" ? "block" : "none";
722
+ oauth2AuthFields.style.display = authType === "oauth2" ? "block" : "none";
723
+ };
724
+ toggleAuthFields();
725
+ typeSelect.onchange = toggleAuthFields;
726
+ const helpLink = document.createElement("div");
727
+ addClass(helpLink, "authHelpLink");
728
+ helpLink.innerHTML = `
729
+ 📚 <a href="https://triply.cc/docs/yasgui#authentication" target="_blank" rel="noopener noreferrer">View authentication documentation</a>
730
+ `;
731
+ body.appendChild(helpLink);
568
732
  const securityNotice = document.createElement("div");
569
733
  addClass(securityNotice, "authSecurityNotice");
570
734
  securityNotice.innerHTML = `
@@ -583,15 +747,18 @@ export default class TabSettingsModal {
583
747
  removeButton.textContent = "Remove Authentication";
584
748
  removeButton.type = "button";
585
749
  addClass(removeButton, "authRemoveButton");
586
- removeButton.onclick = () => {
587
- this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(endpoint, {
588
- authentication: undefined,
589
- });
750
+ const closeModalAndRefresh = () => {
590
751
  authModalOverlay.remove();
591
752
  const endpointsList = this.modalContent.querySelector(".endpointsTable");
592
753
  if (endpointsList)
593
754
  this.renderEndpointsList(endpointsList);
594
755
  };
756
+ removeButton.onclick = () => {
757
+ this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(endpoint, {
758
+ authentication: undefined,
759
+ });
760
+ closeModalAndRefresh();
761
+ };
595
762
  if (!existingAuth) {
596
763
  removeButton.disabled = true;
597
764
  }
@@ -605,23 +772,118 @@ export default class TabSettingsModal {
605
772
  saveButton.type = "button";
606
773
  addClass(saveButton, "authSaveButton");
607
774
  saveButton.onclick = () => {
608
- const username = usernameInput.value.trim();
609
- const password = passwordInput.value;
610
- if (username && password) {
611
- this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(endpoint, {
612
- authentication: {
613
- type: "basic",
614
- username,
615
- password,
616
- },
617
- });
618
- authModalOverlay.remove();
619
- const endpointsList = this.modalContent.querySelector(".endpointsTable");
620
- if (endpointsList)
621
- this.renderEndpointsList(endpointsList);
775
+ const authType = typeSelect.value;
776
+ if (authType === "basic") {
777
+ const username = usernameInput.value.trim();
778
+ const password = passwordInput.value;
779
+ if (username && password) {
780
+ this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(endpoint, {
781
+ authentication: {
782
+ type: "basic",
783
+ username,
784
+ password,
785
+ },
786
+ });
787
+ closeModalAndRefresh();
788
+ }
789
+ else {
790
+ alert("Please enter both username and password.");
791
+ }
622
792
  }
623
- else {
624
- alert("Please enter both username and password.");
793
+ else if (authType === "bearer") {
794
+ const token = tokenInput.value.trim();
795
+ if (token) {
796
+ this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(endpoint, {
797
+ authentication: {
798
+ type: "bearer",
799
+ token,
800
+ },
801
+ });
802
+ closeModalAndRefresh();
803
+ }
804
+ else {
805
+ alert("Please enter a bearer token.");
806
+ }
807
+ }
808
+ else if (authType === "apiKey") {
809
+ const headerName = headerNameInput.value.trim();
810
+ const apiKey = apiKeyInput.value.trim();
811
+ if (headerName && apiKey) {
812
+ this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(endpoint, {
813
+ authentication: {
814
+ type: "apiKey",
815
+ headerName,
816
+ apiKey,
817
+ },
818
+ });
819
+ closeModalAndRefresh();
820
+ }
821
+ else {
822
+ alert("Please enter both header name and API key.");
823
+ }
824
+ }
825
+ else if (authType === "oauth2") {
826
+ const clientId = clientIdInput.value.trim();
827
+ const authorizationEndpoint = authEndpointInput.value.trim();
828
+ const tokenEndpoint = tokenEndpointInput.value.trim();
829
+ const redirectUri = redirectUriInput.value.trim() || window.location.origin + window.location.pathname;
830
+ const scope = scopeInput.value.trim();
831
+ if (!clientId || !authorizationEndpoint || !tokenEndpoint) {
832
+ alert("Please enter Client ID, Authorization Endpoint, and Token Endpoint.");
833
+ return;
834
+ }
835
+ if ((existingAuth === null || existingAuth === void 0 ? void 0 : existingAuth.type) === "oauth2" && existingAuth.accessToken) {
836
+ this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(endpoint, {
837
+ authentication: {
838
+ type: "oauth2",
839
+ clientId,
840
+ authorizationEndpoint,
841
+ tokenEndpoint,
842
+ redirectUri,
843
+ scope,
844
+ accessToken: existingAuth.accessToken,
845
+ idToken: existingAuth.idToken,
846
+ refreshToken: existingAuth.refreshToken,
847
+ tokenExpiry: existingAuth.tokenExpiry,
848
+ },
849
+ });
850
+ closeModalAndRefresh();
851
+ return;
852
+ }
853
+ saveButton.disabled = true;
854
+ saveButton.textContent = "Authenticating...";
855
+ OAuth2Utils.startOAuth2Flow({
856
+ clientId,
857
+ authorizationEndpoint,
858
+ tokenEndpoint,
859
+ redirectUri,
860
+ scope,
861
+ })
862
+ .then((tokenResponse) => {
863
+ const tokenExpiry = OAuth2Utils.calculateTokenExpiry(tokenResponse.expires_in);
864
+ this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(endpoint, {
865
+ authentication: {
866
+ type: "oauth2",
867
+ clientId,
868
+ authorizationEndpoint,
869
+ tokenEndpoint,
870
+ redirectUri,
871
+ scope,
872
+ accessToken: tokenResponse.access_token,
873
+ idToken: tokenResponse.id_token,
874
+ refreshToken: tokenResponse.refresh_token,
875
+ tokenExpiry,
876
+ },
877
+ });
878
+ closeModalAndRefresh();
879
+ alert("OAuth 2.0 authentication successful!");
880
+ })
881
+ .catch((error) => {
882
+ console.error("OAuth 2.0 authentication failed:", error);
883
+ alert("OAuth 2.0 authentication failed: " + error.message);
884
+ saveButton.disabled = false;
885
+ saveButton.textContent = "Save & Authenticate";
886
+ });
625
887
  }
626
888
  };
627
889
  footer.appendChild(removeButton);
@@ -727,7 +989,18 @@ export default class TabSettingsModal {
727
989
  }
728
990
  const snippetsBarCheckbox = document.getElementById("showSnippetsBar");
729
991
  if (snippetsBarCheckbox) {
730
- yasqe.setSnippetsBarVisible(snippetsBarCheckbox.checked);
992
+ this.tab.yasgui.config.showSnippetsBar = snippetsBarCheckbox.checked;
993
+ this.tab.yasgui.persistentConfig.setShowSnippetsBar(snippetsBarCheckbox.checked);
994
+ this.tab.yasgui.persistentConfig.getTabs().forEach((tabId) => {
995
+ const tab = this.tab.yasgui.getTab(tabId);
996
+ if (tab) {
997
+ const tabYasqe = tab.getYasqe();
998
+ if (tabYasqe) {
999
+ tabYasqe.config.showSnippetsBar = snippetsBarCheckbox.checked;
1000
+ tabYasqe.refreshSnippetsBar();
1001
+ }
1002
+ }
1003
+ });
731
1004
  }
732
1005
  yasqe.saveQuery();
733
1006
  }
@@ -1125,6 +1398,22 @@ export default class TabSettingsModal {
1125
1398
  footerInfo.appendChild(paragraph2);
1126
1399
  aboutSection.appendChild(footerInfo);
1127
1400
  container.appendChild(aboutSection);
1401
+ const storageSection = document.createElement("div");
1402
+ addClass(storageSection, "settingsSection");
1403
+ const storageTitle = document.createElement("h3");
1404
+ storageTitle.textContent = "Storage Management";
1405
+ storageSection.appendChild(storageTitle);
1406
+ const storageDescription = document.createElement("p");
1407
+ storageDescription.textContent =
1408
+ "Clear all locally stored data including tabs, queries, endpoint configurations, and preferences. This action cannot be undone.";
1409
+ addClass(storageDescription, "settingsHelp");
1410
+ storageSection.appendChild(storageDescription);
1411
+ const clearStorageButton = document.createElement("button");
1412
+ clearStorageButton.textContent = "Clear Persistent Storage";
1413
+ addClass(clearStorageButton, "dangerButton");
1414
+ clearStorageButton.onclick = () => this.clearPersistentStorage();
1415
+ storageSection.appendChild(clearStorageButton);
1416
+ container.appendChild(storageSection);
1128
1417
  }
1129
1418
  createAboutLink(label, url, description) {
1130
1419
  const linkContainer = document.createElement("div");
@@ -1142,6 +1431,20 @@ export default class TabSettingsModal {
1142
1431
  linkContainer.appendChild(desc);
1143
1432
  return linkContainer;
1144
1433
  }
1434
+ clearPersistentStorage() {
1435
+ const confirmed = confirm("Are you sure you want to clear all persistent storage?\n\n" +
1436
+ "This will delete:\n" +
1437
+ "- All saved tabs and queries\n" +
1438
+ "- Endpoint configurations and history\n" +
1439
+ "- Authentication settings\n" +
1440
+ "- Editor preferences\n" +
1441
+ "- Prefixes\n\n" +
1442
+ "This action cannot be undone and the page will reload.");
1443
+ if (confirmed) {
1444
+ PersistentConfig.clear();
1445
+ window.location.reload();
1446
+ }
1447
+ }
1145
1448
  destroy() {
1146
1449
  if (this.modalOverlay && this.modalOverlay.parentNode) {
1147
1450
  this.modalOverlay.parentNode.removeChild(this.modalOverlay);