@matdata/yasgui 5.6.0 → 5.8.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.
- package/README.md +157 -3
- package/build/ts/src/ConfigExportImport.js +1 -0
- package/build/ts/src/ConfigExportImport.js.map +1 -1
- package/build/ts/src/OAuth2Utils.d.ts +18 -0
- package/build/ts/src/OAuth2Utils.js +214 -0
- package/build/ts/src/OAuth2Utils.js.map +1 -0
- package/build/ts/src/Tab.d.ts +9 -0
- package/build/ts/src/Tab.js +138 -3
- package/build/ts/src/Tab.js.map +1 -1
- package/build/ts/src/TabSettingsModal.d.ts +1 -0
- package/build/ts/src/TabSettingsModal.js +197 -17
- package/build/ts/src/TabSettingsModal.js.map +1 -1
- package/build/ts/src/index.d.ts +12 -0
- package/build/ts/src/index.js +3 -0
- package/build/ts/src/index.js.map +1 -1
- package/build/ts/src/version.d.ts +1 -1
- package/build/ts/src/version.js +1 -1
- package/build/yasgui.min.css +1 -1
- package/build/yasgui.min.css.map +3 -3
- package/build/yasgui.min.js +185 -157
- package/build/yasgui.min.js.map +4 -4
- package/package.json +3 -2
- package/src/ConfigExportImport.ts +1 -0
- package/src/OAuth2Utils.ts +315 -0
- package/src/Tab.ts +185 -2
- package/src/TabElements.scss +22 -2
- package/src/TabSettingsModal.scss +70 -3
- package/src/TabSettingsModal.ts +234 -13
- package/src/index.scss +17 -0
- package/src/index.ts +15 -0
- package/src/tab.scss +82 -22
- package/src/themes.scss +2 -3
- package/src/version.ts +1 -1
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
background: var(--yasgui-bg-primary, white);
|
|
64
64
|
border-radius: 8px;
|
|
65
65
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
|
66
|
-
max-width:
|
|
66
|
+
max-width: 1000px;
|
|
67
67
|
width: 90%;
|
|
68
68
|
max-height: 90vh;
|
|
69
69
|
display: flex;
|
|
@@ -160,6 +160,13 @@
|
|
|
160
160
|
|
|
161
161
|
.settingsSection {
|
|
162
162
|
margin-bottom: 20px;
|
|
163
|
+
|
|
164
|
+
h3 {
|
|
165
|
+
margin: 0 0 12px 0;
|
|
166
|
+
font-size: 16px;
|
|
167
|
+
font-weight: 600;
|
|
168
|
+
color: var(--yasgui-text-primary, #000);
|
|
169
|
+
}
|
|
163
170
|
}
|
|
164
171
|
|
|
165
172
|
.settingsLabel {
|
|
@@ -297,7 +304,8 @@
|
|
|
297
304
|
}
|
|
298
305
|
|
|
299
306
|
.primaryButton,
|
|
300
|
-
.secondaryButton
|
|
307
|
+
.secondaryButton,
|
|
308
|
+
.dangerButton {
|
|
301
309
|
padding: 8px 20px;
|
|
302
310
|
border: none;
|
|
303
311
|
border-radius: 4px;
|
|
@@ -320,7 +328,21 @@
|
|
|
320
328
|
color: var(--yasgui-text-primary, #333);
|
|
321
329
|
|
|
322
330
|
&:hover {
|
|
323
|
-
background: var(--yasgui-
|
|
331
|
+
background: var(--yasgui-bg-quaternary, #d0d0d0);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.dangerButton {
|
|
336
|
+
background: var(--yasgui-danger-bg, #dc3545);
|
|
337
|
+
color: white;
|
|
338
|
+
margin-top: 10px;
|
|
339
|
+
|
|
340
|
+
&:hover {
|
|
341
|
+
background: var(--yasgui-danger-hover, #c82333);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
&:active {
|
|
345
|
+
background: var(--yasgui-danger-active, #bd2130);
|
|
324
346
|
}
|
|
325
347
|
}
|
|
326
348
|
|
|
@@ -849,6 +871,24 @@
|
|
|
849
871
|
}
|
|
850
872
|
}
|
|
851
873
|
|
|
874
|
+
.authHelpLink {
|
|
875
|
+
padding: 10px 12px;
|
|
876
|
+
background: var(--yasgui-bg-secondary, #f5f5f5);
|
|
877
|
+
border: 1px solid var(--yasgui-border-color, #e0e0e0);
|
|
878
|
+
border-radius: 4px;
|
|
879
|
+
font-size: 13px;
|
|
880
|
+
margin-bottom: 12px;
|
|
881
|
+
|
|
882
|
+
a {
|
|
883
|
+
color: var(--yasgui-accent-color, #337ab7);
|
|
884
|
+
text-decoration: none;
|
|
885
|
+
|
|
886
|
+
&:hover {
|
|
887
|
+
text-decoration: underline;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
852
892
|
.authSecurityNotice {
|
|
853
893
|
padding: 12px;
|
|
854
894
|
background: var(--yasgui-warning-bg, #fff3cd);
|
|
@@ -925,6 +965,33 @@
|
|
|
925
965
|
}
|
|
926
966
|
}
|
|
927
967
|
|
|
968
|
+
// OAuth 2.0 specific styling
|
|
969
|
+
.authInputHelp {
|
|
970
|
+
font-size: 12px;
|
|
971
|
+
color: var(--yasgui-text-secondary, #666);
|
|
972
|
+
margin-top: 4px;
|
|
973
|
+
line-height: 1.4;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
.oauth2TokenStatus {
|
|
977
|
+
padding: 8px 12px;
|
|
978
|
+
border-radius: 4px;
|
|
979
|
+
font-size: 14px;
|
|
980
|
+
font-weight: 500;
|
|
981
|
+
|
|
982
|
+
&.authenticated {
|
|
983
|
+
background: var(--yasgui-success-bg, #d4edda);
|
|
984
|
+
color: var(--yasgui-success-text, #155724);
|
|
985
|
+
border: 1px solid var(--yasgui-success-border, #c3e6cb);
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
&.expired {
|
|
989
|
+
background: var(--yasgui-warning-bg, #fff3cd);
|
|
990
|
+
color: var(--yasgui-warning-text, #856404);
|
|
991
|
+
border: 1px solid var(--yasgui-warning-border, #ffeaa7);
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
|
|
928
995
|
// Add endpoint form styling
|
|
929
996
|
.addEndpointForm {
|
|
930
997
|
margin-top: 20px;
|
package/src/TabSettingsModal.ts
CHANGED
|
@@ -3,6 +3,8 @@ import "./TabSettingsModal.scss";
|
|
|
3
3
|
import Tab from "./Tab";
|
|
4
4
|
import * as ConfigExportImport from "./ConfigExportImport";
|
|
5
5
|
import { VERSION } from "./version";
|
|
6
|
+
import * as OAuth2Utils from "./OAuth2Utils";
|
|
7
|
+
import PersistentConfig from "./PersistentConfig";
|
|
6
8
|
|
|
7
9
|
// Theme toggle icons
|
|
8
10
|
const MOON_ICON = `<svg viewBox="0 0 24 24" fill="currentColor">
|
|
@@ -92,7 +94,8 @@ export default class TabSettingsModal {
|
|
|
92
94
|
// Modal overlay
|
|
93
95
|
this.modalOverlay = document.createElement("div");
|
|
94
96
|
addClass(this.modalOverlay, "tabSettingsModalOverlay");
|
|
95
|
-
this.modalOverlay.onclick = () => this.close();
|
|
97
|
+
// Removed: this.modalOverlay.onclick = () => this.close();
|
|
98
|
+
// Users must explicitly click 'Save' or 'Cancel' to close the modal
|
|
96
99
|
|
|
97
100
|
// Modal content
|
|
98
101
|
this.modalContent = document.createElement("div");
|
|
@@ -717,6 +720,11 @@ export default class TabSettingsModal {
|
|
|
717
720
|
apiKeyOption.textContent = "API Key (Custom Header)";
|
|
718
721
|
typeSelect.appendChild(apiKeyOption);
|
|
719
722
|
|
|
723
|
+
const oauth2Option = document.createElement("option");
|
|
724
|
+
oauth2Option.value = "oauth2";
|
|
725
|
+
oauth2Option.textContent = "OAuth 2.0";
|
|
726
|
+
typeSelect.appendChild(oauth2Option);
|
|
727
|
+
|
|
720
728
|
// Set the current auth type
|
|
721
729
|
if (existingAuth) {
|
|
722
730
|
typeSelect.value = existingAuth.type;
|
|
@@ -813,12 +821,104 @@ export default class TabSettingsModal {
|
|
|
813
821
|
|
|
814
822
|
body.appendChild(apiKeyAuthFields);
|
|
815
823
|
|
|
824
|
+
// OAuth 2.0 Fields
|
|
825
|
+
const oauth2AuthFields = document.createElement("div");
|
|
826
|
+
oauth2AuthFields.id = "oauth2AuthFields";
|
|
827
|
+
addClass(oauth2AuthFields, "authFieldsContainer");
|
|
828
|
+
oauth2AuthFields.style.display = "none";
|
|
829
|
+
|
|
830
|
+
const clientIdSection = document.createElement("div");
|
|
831
|
+
addClass(clientIdSection, "authModalSection");
|
|
832
|
+
const clientIdLabel = document.createElement("label");
|
|
833
|
+
clientIdLabel.textContent = "Client ID";
|
|
834
|
+
const clientIdInput = document.createElement("input");
|
|
835
|
+
clientIdInput.type = "text";
|
|
836
|
+
clientIdInput.placeholder = "Enter OAuth 2.0 client ID";
|
|
837
|
+
clientIdInput.value = existingAuth?.type === "oauth2" ? existingAuth.clientId : "";
|
|
838
|
+
clientIdSection.appendChild(clientIdLabel);
|
|
839
|
+
clientIdSection.appendChild(clientIdInput);
|
|
840
|
+
oauth2AuthFields.appendChild(clientIdSection);
|
|
841
|
+
|
|
842
|
+
const authEndpointSection = document.createElement("div");
|
|
843
|
+
addClass(authEndpointSection, "authModalSection");
|
|
844
|
+
const authEndpointLabel = document.createElement("label");
|
|
845
|
+
authEndpointLabel.textContent = "Authorization Endpoint";
|
|
846
|
+
const authEndpointInput = document.createElement("input");
|
|
847
|
+
authEndpointInput.type = "url";
|
|
848
|
+
authEndpointInput.placeholder = "https://provider.com/oauth/authorize";
|
|
849
|
+
authEndpointInput.value = existingAuth?.type === "oauth2" ? existingAuth.authorizationEndpoint : "";
|
|
850
|
+
authEndpointSection.appendChild(authEndpointLabel);
|
|
851
|
+
authEndpointSection.appendChild(authEndpointInput);
|
|
852
|
+
oauth2AuthFields.appendChild(authEndpointSection);
|
|
853
|
+
|
|
854
|
+
const tokenEndpointSection = document.createElement("div");
|
|
855
|
+
addClass(tokenEndpointSection, "authModalSection");
|
|
856
|
+
const tokenEndpointLabel = document.createElement("label");
|
|
857
|
+
tokenEndpointLabel.textContent = "Token Endpoint";
|
|
858
|
+
const tokenEndpointInput = document.createElement("input");
|
|
859
|
+
tokenEndpointInput.type = "url";
|
|
860
|
+
tokenEndpointInput.placeholder = "https://provider.com/oauth/token";
|
|
861
|
+
tokenEndpointInput.value = existingAuth?.type === "oauth2" ? existingAuth.tokenEndpoint : "";
|
|
862
|
+
tokenEndpointSection.appendChild(tokenEndpointLabel);
|
|
863
|
+
tokenEndpointSection.appendChild(tokenEndpointInput);
|
|
864
|
+
oauth2AuthFields.appendChild(tokenEndpointSection);
|
|
865
|
+
|
|
866
|
+
const redirectUriSection = document.createElement("div");
|
|
867
|
+
addClass(redirectUriSection, "authModalSection");
|
|
868
|
+
const redirectUriLabel = document.createElement("label");
|
|
869
|
+
redirectUriLabel.textContent = "Redirect URI (Optional)";
|
|
870
|
+
const redirectUriInput = document.createElement("input");
|
|
871
|
+
redirectUriInput.type = "url";
|
|
872
|
+
redirectUriInput.placeholder = window.location.origin + window.location.pathname;
|
|
873
|
+
redirectUriInput.value = existingAuth?.type === "oauth2" ? existingAuth.redirectUri || "" : "";
|
|
874
|
+
const redirectUriHelp = document.createElement("div");
|
|
875
|
+
redirectUriHelp.textContent = "Leave empty to use current page URL. Must be registered with OAuth provider.";
|
|
876
|
+
addClass(redirectUriHelp, "authInputHelp");
|
|
877
|
+
redirectUriSection.appendChild(redirectUriLabel);
|
|
878
|
+
redirectUriSection.appendChild(redirectUriInput);
|
|
879
|
+
redirectUriSection.appendChild(redirectUriHelp);
|
|
880
|
+
oauth2AuthFields.appendChild(redirectUriSection);
|
|
881
|
+
|
|
882
|
+
const scopeSection = document.createElement("div");
|
|
883
|
+
addClass(scopeSection, "authModalSection");
|
|
884
|
+
const scopeLabel = document.createElement("label");
|
|
885
|
+
scopeLabel.textContent = "Scope (Optional)";
|
|
886
|
+
const scopeInput = document.createElement("input");
|
|
887
|
+
scopeInput.type = "text";
|
|
888
|
+
scopeInput.placeholder = "e.g., read write";
|
|
889
|
+
scopeInput.value = existingAuth?.type === "oauth2" ? existingAuth.scope || "" : "";
|
|
890
|
+
const scopeHelp = document.createElement("div");
|
|
891
|
+
scopeHelp.textContent = "Space-separated list of OAuth 2.0 scopes";
|
|
892
|
+
addClass(scopeHelp, "authInputHelp");
|
|
893
|
+
scopeSection.appendChild(scopeLabel);
|
|
894
|
+
scopeSection.appendChild(scopeInput);
|
|
895
|
+
scopeSection.appendChild(scopeHelp);
|
|
896
|
+
oauth2AuthFields.appendChild(scopeSection);
|
|
897
|
+
|
|
898
|
+
// Show token status if already authenticated
|
|
899
|
+
if (existingAuth?.type === "oauth2" && existingAuth.accessToken) {
|
|
900
|
+
const tokenStatusSection = document.createElement("div");
|
|
901
|
+
addClass(tokenStatusSection, "authModalSection");
|
|
902
|
+
const tokenStatusLabel = document.createElement("label");
|
|
903
|
+
tokenStatusLabel.textContent = "Authentication Status";
|
|
904
|
+
const tokenStatus = document.createElement("div");
|
|
905
|
+
addClass(tokenStatus, "oauth2TokenStatus");
|
|
906
|
+
tokenStatus.innerHTML = "✓ Authenticated";
|
|
907
|
+
addClass(tokenStatus, "authenticated");
|
|
908
|
+
tokenStatusSection.appendChild(tokenStatusLabel);
|
|
909
|
+
tokenStatusSection.appendChild(tokenStatus);
|
|
910
|
+
oauth2AuthFields.appendChild(tokenStatusSection);
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
body.appendChild(oauth2AuthFields);
|
|
914
|
+
|
|
816
915
|
// Function to toggle fields based on auth type
|
|
817
916
|
const toggleAuthFields = () => {
|
|
818
917
|
const authType = typeSelect.value;
|
|
819
918
|
basicAuthFields.style.display = authType === "basic" ? "block" : "none";
|
|
820
919
|
bearerAuthFields.style.display = authType === "bearer" ? "block" : "none";
|
|
821
920
|
apiKeyAuthFields.style.display = authType === "apiKey" ? "block" : "none";
|
|
921
|
+
oauth2AuthFields.style.display = authType === "oauth2" ? "block" : "none";
|
|
822
922
|
};
|
|
823
923
|
|
|
824
924
|
// Set initial visibility
|
|
@@ -827,6 +927,14 @@ export default class TabSettingsModal {
|
|
|
827
927
|
// Update visibility when auth type changes
|
|
828
928
|
typeSelect.onchange = toggleAuthFields;
|
|
829
929
|
|
|
930
|
+
// Help link
|
|
931
|
+
const helpLink = document.createElement("div");
|
|
932
|
+
addClass(helpLink, "authHelpLink");
|
|
933
|
+
helpLink.innerHTML = `
|
|
934
|
+
📚 <a href="https://triply.cc/docs/yasgui#authentication" target="_blank" rel="noopener noreferrer">View authentication documentation</a>
|
|
935
|
+
`;
|
|
936
|
+
body.appendChild(helpLink);
|
|
937
|
+
|
|
830
938
|
// Security notice
|
|
831
939
|
const securityNotice = document.createElement("div");
|
|
832
940
|
addClass(securityNotice, "authSecurityNotice");
|
|
@@ -850,13 +958,18 @@ export default class TabSettingsModal {
|
|
|
850
958
|
removeButton.textContent = "Remove Authentication";
|
|
851
959
|
removeButton.type = "button";
|
|
852
960
|
addClass(removeButton, "authRemoveButton");
|
|
961
|
+
// Helper function to close modal and refresh endpoints list
|
|
962
|
+
const closeModalAndRefresh = () => {
|
|
963
|
+
authModalOverlay.remove();
|
|
964
|
+
const endpointsList = this.modalContent.querySelector(".endpointsTable");
|
|
965
|
+
if (endpointsList) this.renderEndpointsList(endpointsList as HTMLElement);
|
|
966
|
+
};
|
|
967
|
+
|
|
853
968
|
removeButton.onclick = () => {
|
|
854
969
|
this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(endpoint, {
|
|
855
970
|
authentication: undefined,
|
|
856
971
|
});
|
|
857
|
-
|
|
858
|
-
const endpointsList = this.modalContent.querySelector(".endpointsTable");
|
|
859
|
-
if (endpointsList) this.renderEndpointsList(endpointsList as HTMLElement);
|
|
972
|
+
closeModalAndRefresh();
|
|
860
973
|
};
|
|
861
974
|
if (!existingAuth) {
|
|
862
975
|
removeButton.disabled = true;
|
|
@@ -887,9 +1000,7 @@ export default class TabSettingsModal {
|
|
|
887
1000
|
password,
|
|
888
1001
|
},
|
|
889
1002
|
});
|
|
890
|
-
|
|
891
|
-
const endpointsList = this.modalContent.querySelector(".endpointsTable");
|
|
892
|
-
if (endpointsList) this.renderEndpointsList(endpointsList as HTMLElement);
|
|
1003
|
+
closeModalAndRefresh();
|
|
893
1004
|
} else {
|
|
894
1005
|
alert("Please enter both username and password.");
|
|
895
1006
|
}
|
|
@@ -903,9 +1014,7 @@ export default class TabSettingsModal {
|
|
|
903
1014
|
token,
|
|
904
1015
|
},
|
|
905
1016
|
});
|
|
906
|
-
|
|
907
|
-
const endpointsList = this.modalContent.querySelector(".endpointsTable");
|
|
908
|
-
if (endpointsList) this.renderEndpointsList(endpointsList as HTMLElement);
|
|
1017
|
+
closeModalAndRefresh();
|
|
909
1018
|
} else {
|
|
910
1019
|
alert("Please enter a bearer token.");
|
|
911
1020
|
}
|
|
@@ -921,12 +1030,81 @@ export default class TabSettingsModal {
|
|
|
921
1030
|
apiKey,
|
|
922
1031
|
},
|
|
923
1032
|
});
|
|
924
|
-
|
|
925
|
-
const endpointsList = this.modalContent.querySelector(".endpointsTable");
|
|
926
|
-
if (endpointsList) this.renderEndpointsList(endpointsList as HTMLElement);
|
|
1033
|
+
closeModalAndRefresh();
|
|
927
1034
|
} else {
|
|
928
1035
|
alert("Please enter both header name and API key.");
|
|
929
1036
|
}
|
|
1037
|
+
} else if (authType === "oauth2") {
|
|
1038
|
+
const clientId = clientIdInput.value.trim();
|
|
1039
|
+
const authorizationEndpoint = authEndpointInput.value.trim();
|
|
1040
|
+
const tokenEndpoint = tokenEndpointInput.value.trim();
|
|
1041
|
+
const redirectUri = redirectUriInput.value.trim() || window.location.origin + window.location.pathname;
|
|
1042
|
+
const scope = scopeInput.value.trim();
|
|
1043
|
+
|
|
1044
|
+
if (!clientId || !authorizationEndpoint || !tokenEndpoint) {
|
|
1045
|
+
alert("Please enter Client ID, Authorization Endpoint, and Token Endpoint.");
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
// Check if already authenticated (has access token)
|
|
1050
|
+
if (existingAuth?.type === "oauth2" && existingAuth.accessToken) {
|
|
1051
|
+
// Already has token, just save the configuration updates
|
|
1052
|
+
this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(endpoint, {
|
|
1053
|
+
authentication: {
|
|
1054
|
+
type: "oauth2",
|
|
1055
|
+
clientId,
|
|
1056
|
+
authorizationEndpoint,
|
|
1057
|
+
tokenEndpoint,
|
|
1058
|
+
redirectUri,
|
|
1059
|
+
scope,
|
|
1060
|
+
accessToken: existingAuth.accessToken,
|
|
1061
|
+
idToken: existingAuth.idToken,
|
|
1062
|
+
refreshToken: existingAuth.refreshToken,
|
|
1063
|
+
tokenExpiry: existingAuth.tokenExpiry,
|
|
1064
|
+
},
|
|
1065
|
+
});
|
|
1066
|
+
closeModalAndRefresh();
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
// Start OAuth 2.0 flow
|
|
1071
|
+
saveButton.disabled = true;
|
|
1072
|
+
saveButton.textContent = "Authenticating...";
|
|
1073
|
+
|
|
1074
|
+
OAuth2Utils.startOAuth2Flow({
|
|
1075
|
+
clientId,
|
|
1076
|
+
authorizationEndpoint,
|
|
1077
|
+
tokenEndpoint,
|
|
1078
|
+
redirectUri,
|
|
1079
|
+
scope,
|
|
1080
|
+
})
|
|
1081
|
+
.then((tokenResponse) => {
|
|
1082
|
+
const tokenExpiry = OAuth2Utils.calculateTokenExpiry(tokenResponse.expires_in);
|
|
1083
|
+
|
|
1084
|
+
this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(endpoint, {
|
|
1085
|
+
authentication: {
|
|
1086
|
+
type: "oauth2",
|
|
1087
|
+
clientId,
|
|
1088
|
+
authorizationEndpoint,
|
|
1089
|
+
tokenEndpoint,
|
|
1090
|
+
redirectUri,
|
|
1091
|
+
scope,
|
|
1092
|
+
accessToken: tokenResponse.access_token,
|
|
1093
|
+
idToken: tokenResponse.id_token,
|
|
1094
|
+
refreshToken: tokenResponse.refresh_token,
|
|
1095
|
+
tokenExpiry,
|
|
1096
|
+
},
|
|
1097
|
+
});
|
|
1098
|
+
|
|
1099
|
+
closeModalAndRefresh();
|
|
1100
|
+
alert("OAuth 2.0 authentication successful!");
|
|
1101
|
+
})
|
|
1102
|
+
.catch((error) => {
|
|
1103
|
+
console.error("OAuth 2.0 authentication failed:", error);
|
|
1104
|
+
alert("OAuth 2.0 authentication failed: " + error.message);
|
|
1105
|
+
saveButton.disabled = false;
|
|
1106
|
+
saveButton.textContent = "Save & Authenticate";
|
|
1107
|
+
});
|
|
930
1108
|
}
|
|
931
1109
|
};
|
|
932
1110
|
|
|
@@ -1599,6 +1777,28 @@ export default class TabSettingsModal {
|
|
|
1599
1777
|
aboutSection.appendChild(footerInfo);
|
|
1600
1778
|
|
|
1601
1779
|
container.appendChild(aboutSection);
|
|
1780
|
+
|
|
1781
|
+
// Storage Management Section
|
|
1782
|
+
const storageSection = document.createElement("div");
|
|
1783
|
+
addClass(storageSection, "settingsSection");
|
|
1784
|
+
|
|
1785
|
+
const storageTitle = document.createElement("h3");
|
|
1786
|
+
storageTitle.textContent = "Storage Management";
|
|
1787
|
+
storageSection.appendChild(storageTitle);
|
|
1788
|
+
|
|
1789
|
+
const storageDescription = document.createElement("p");
|
|
1790
|
+
storageDescription.textContent =
|
|
1791
|
+
"Clear all locally stored data including tabs, queries, endpoint configurations, and preferences. This action cannot be undone.";
|
|
1792
|
+
addClass(storageDescription, "settingsHelp");
|
|
1793
|
+
storageSection.appendChild(storageDescription);
|
|
1794
|
+
|
|
1795
|
+
const clearStorageButton = document.createElement("button");
|
|
1796
|
+
clearStorageButton.textContent = "Clear Persistent Storage";
|
|
1797
|
+
addClass(clearStorageButton, "dangerButton");
|
|
1798
|
+
clearStorageButton.onclick = () => this.clearPersistentStorage();
|
|
1799
|
+
storageSection.appendChild(clearStorageButton);
|
|
1800
|
+
|
|
1801
|
+
container.appendChild(storageSection);
|
|
1602
1802
|
}
|
|
1603
1803
|
|
|
1604
1804
|
private createAboutLink(label: string, url: string, description: string): HTMLElement {
|
|
@@ -1622,6 +1822,27 @@ export default class TabSettingsModal {
|
|
|
1622
1822
|
return linkContainer;
|
|
1623
1823
|
}
|
|
1624
1824
|
|
|
1825
|
+
private clearPersistentStorage() {
|
|
1826
|
+
const confirmed = confirm(
|
|
1827
|
+
"Are you sure you want to clear all persistent storage?\n\n" +
|
|
1828
|
+
"This will delete:\n" +
|
|
1829
|
+
"- All saved tabs and queries\n" +
|
|
1830
|
+
"- Endpoint configurations and history\n" +
|
|
1831
|
+
"- Authentication settings\n" +
|
|
1832
|
+
"- Editor preferences\n" +
|
|
1833
|
+
"- Prefixes\n\n" +
|
|
1834
|
+
"This action cannot be undone and the page will reload.",
|
|
1835
|
+
);
|
|
1836
|
+
|
|
1837
|
+
if (confirmed) {
|
|
1838
|
+
// Clear persistent storage
|
|
1839
|
+
PersistentConfig.clear();
|
|
1840
|
+
|
|
1841
|
+
// Reload the page to reinitialize with empty storage
|
|
1842
|
+
window.location.reload();
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1625
1846
|
public destroy() {
|
|
1626
1847
|
if (this.modalOverlay && this.modalOverlay.parentNode) {
|
|
1627
1848
|
this.modalOverlay.parentNode.removeChild(this.modalOverlay);
|
package/src/index.scss
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
+
// Main YASGUI container - fills 100% of parent element
|
|
2
|
+
// Parent element (typically body) should have height: 100%; width: 100%
|
|
1
3
|
.yasgui {
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
height: 100%;
|
|
7
|
+
width: 100%;
|
|
8
|
+
min-height: 800px; // Minimum to ensure usable UI (control bar + editor + yasr min)
|
|
9
|
+
|
|
2
10
|
a {
|
|
3
11
|
color: #337ab7;
|
|
4
12
|
text-decoration: none;
|
|
@@ -28,6 +36,15 @@
|
|
|
28
36
|
}
|
|
29
37
|
}
|
|
30
38
|
|
|
39
|
+
// Container for tab panels - takes remaining height after tabsList
|
|
40
|
+
> div:not(.tabsList) {
|
|
41
|
+
flex: 1;
|
|
42
|
+
display: flex;
|
|
43
|
+
flex-direction: column;
|
|
44
|
+
min-height: 0; // Required for flex children to shrink properly
|
|
45
|
+
overflow: hidden;
|
|
46
|
+
}
|
|
47
|
+
|
|
31
48
|
//css taken from https://www.muicss.com/docs/v1/css-js/forms
|
|
32
49
|
$focusColor: #337ab7;
|
|
33
50
|
$font-size: 15px;
|
package/src/index.ts
CHANGED
|
@@ -12,6 +12,8 @@ import { default as Yasr, Config as YasrConfig } from "@matdata/yasr";
|
|
|
12
12
|
import { addClass, removeClass } from "@matdata/yasgui-utils";
|
|
13
13
|
import GeoPlugin from "yasgui-geo-tg";
|
|
14
14
|
import GraphPlugin from "@matdata/yasgui-graph-plugin";
|
|
15
|
+
import TablePlugin from "@matdata/yasgui-table-plugin";
|
|
16
|
+
import "@matdata/yasgui-table-plugin/dist/yasgui-table-plugin.min.css";
|
|
15
17
|
import { ThemeManager, Theme } from "./ThemeManager";
|
|
16
18
|
import "./index.scss";
|
|
17
19
|
import "./themes.scss";
|
|
@@ -19,6 +21,7 @@ import "../../yasr/src/scss/global.scss";
|
|
|
19
21
|
import "codemirror/theme/material-palenight.css";
|
|
20
22
|
|
|
21
23
|
// Register plugins to Yasr
|
|
24
|
+
Yasr.registerPlugin("Table", TablePlugin);
|
|
22
25
|
Yasr.registerPlugin("Geo", GeoPlugin);
|
|
23
26
|
Yasr.registerPlugin("Graph", GraphPlugin);
|
|
24
27
|
if (window) {
|
|
@@ -54,6 +57,18 @@ export interface EndpointConfig {
|
|
|
54
57
|
type: "apiKey";
|
|
55
58
|
headerName: string;
|
|
56
59
|
apiKey: string;
|
|
60
|
+
}
|
|
61
|
+
| {
|
|
62
|
+
type: "oauth2";
|
|
63
|
+
clientId: string;
|
|
64
|
+
authorizationEndpoint: string;
|
|
65
|
+
tokenEndpoint: string;
|
|
66
|
+
redirectUri?: string;
|
|
67
|
+
scope?: string;
|
|
68
|
+
accessToken?: string;
|
|
69
|
+
idToken?: string; // ID token for OIDC/Azure AD authentication
|
|
70
|
+
refreshToken?: string;
|
|
71
|
+
tokenExpiry?: number; // Unix timestamp in milliseconds
|
|
57
72
|
};
|
|
58
73
|
}
|
|
59
74
|
export interface Config<EndpointObject extends CatalogueItem = CatalogueItem> {
|
package/src/tab.scss
CHANGED
|
@@ -2,8 +2,16 @@
|
|
|
2
2
|
.tabPanel {
|
|
3
3
|
display: none;
|
|
4
4
|
position: relative;
|
|
5
|
+
flex: 1;
|
|
6
|
+
min-height: 0;
|
|
7
|
+
|
|
5
8
|
&.active {
|
|
6
|
-
display:
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.yasrWrapperEl {
|
|
14
|
+
height: 100%;
|
|
7
15
|
}
|
|
8
16
|
|
|
9
17
|
// Hide editor wrapper when yasr is in fullscreen
|
|
@@ -34,44 +42,95 @@
|
|
|
34
42
|
display: flex;
|
|
35
43
|
flex-direction: row;
|
|
36
44
|
gap: 10px;
|
|
37
|
-
|
|
38
|
-
min-height: var(--yasgui-min-height);
|
|
39
|
-
max-height: calc(100vh - var(--yasgui-header-height));
|
|
45
|
+
height: 100%;
|
|
40
46
|
}
|
|
41
47
|
|
|
42
48
|
.editorwrapper {
|
|
43
|
-
flex: 1
|
|
49
|
+
flex: 1 1 50%;
|
|
44
50
|
display: flex;
|
|
45
51
|
flex-direction: column;
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
.yasqe {
|
|
50
|
-
display: flex;
|
|
51
|
-
flex-direction: column;
|
|
52
|
-
flex: 1;
|
|
53
|
-
min-height: 0;
|
|
52
|
+
min-width: 400px;
|
|
53
|
+
max-width: 50%;
|
|
54
|
+
height: 100%;
|
|
54
55
|
overflow: hidden;
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
// Unnamed div container needs full height
|
|
58
|
+
> div {
|
|
59
|
+
height: 100%;
|
|
60
|
+
flex: 1;
|
|
61
|
+
display: flex;
|
|
62
|
+
flex-direction: column;
|
|
63
|
+
|
|
64
|
+
// Container for tab panels - takes remaining height after tabsList
|
|
65
|
+
> div:not(.controlbar) {
|
|
66
|
+
flex: 1;
|
|
67
|
+
display: flex;
|
|
68
|
+
flex-direction: column;
|
|
69
|
+
min-height: 0; // Required for flex children to shrink properly
|
|
70
|
+
overflow: hidden;
|
|
71
|
+
|
|
72
|
+
// Make YASQE fill the vertical space in horizontal mode
|
|
73
|
+
.yasqe {
|
|
74
|
+
flex: 1;
|
|
75
|
+
display: flex;
|
|
76
|
+
flex-direction: column;
|
|
77
|
+
height: 100%;
|
|
78
|
+
overflow: hidden;
|
|
79
|
+
|
|
80
|
+
.CodeMirror {
|
|
81
|
+
flex: 1;
|
|
82
|
+
height: 100%;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
58
86
|
}
|
|
59
87
|
}
|
|
60
88
|
|
|
61
89
|
.yasrWrapperEl {
|
|
62
|
-
flex: 1
|
|
90
|
+
flex: 1 1 50%;
|
|
63
91
|
min-width: 0;
|
|
64
|
-
|
|
92
|
+
min-height: 400px;
|
|
65
93
|
}
|
|
66
94
|
|
|
67
95
|
.yasr {
|
|
68
96
|
margin-top: 0;
|
|
69
97
|
height: 100%;
|
|
70
98
|
}
|
|
99
|
+
|
|
100
|
+
// Hide horizontal resize wrapper in horizontal mode (vertical resizer is used instead)
|
|
101
|
+
.horizontalResizeWrapper {
|
|
102
|
+
display: none;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Vertical resizer for horizontal layout
|
|
107
|
+
.verticalResizeWrapper {
|
|
108
|
+
display: none; // Hidden by default (shown only in horizontal mode)
|
|
109
|
+
width: 10px;
|
|
110
|
+
flex-shrink: 0;
|
|
111
|
+
align-items: center;
|
|
112
|
+
justify-content: center;
|
|
113
|
+
cursor: col-resize;
|
|
114
|
+
position: relative;
|
|
115
|
+
z-index: 10;
|
|
116
|
+
|
|
117
|
+
&:hover .verticalResizeChip {
|
|
118
|
+
visibility: visible;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.verticalResizeChip {
|
|
123
|
+
width: 4px;
|
|
124
|
+
height: 20%;
|
|
125
|
+
background-color: #d1d1d1;
|
|
126
|
+
visibility: hidden;
|
|
127
|
+
border-radius: 2px;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Show vertical resizer only in horizontal mode
|
|
131
|
+
&.orientation-horizontal .verticalResizeWrapper {
|
|
132
|
+
display: flex;
|
|
71
133
|
}
|
|
72
|
-
}
|
|
73
|
-
.yasr {
|
|
74
|
-
margin-top: 5px;
|
|
75
134
|
}
|
|
76
135
|
|
|
77
136
|
.tabContextButton {
|
|
@@ -90,9 +149,9 @@
|
|
|
90
149
|
.svgImg {
|
|
91
150
|
width: 15px;
|
|
92
151
|
height: 15px;
|
|
93
|
-
font-family: initial;
|
|
152
|
+
font-family: initial;
|
|
94
153
|
}
|
|
95
|
-
|
|
154
|
+
|
|
96
155
|
svg {
|
|
97
156
|
width: 20px;
|
|
98
157
|
height: 20px;
|
|
@@ -107,6 +166,7 @@
|
|
|
107
166
|
.controlbar {
|
|
108
167
|
display: flex;
|
|
109
168
|
align-content: center;
|
|
169
|
+
flex-shrink: 0;
|
|
110
170
|
max-height: 35px;
|
|
111
171
|
}
|
|
112
172
|
}
|
package/src/themes.scss
CHANGED
|
@@ -38,9 +38,8 @@
|
|
|
38
38
|
--yasgui-nav-bg: #eee;
|
|
39
39
|
--yasgui-match-highlight-bg: #dbdeed;
|
|
40
40
|
|
|
41
|
-
//
|
|
42
|
-
--yasgui-
|
|
43
|
-
--yasgui-min-height: 400px; // Minimum height for horizontal layout panels
|
|
41
|
+
// Minimum heights
|
|
42
|
+
--yasgui-yasr-min-height: 400px; // Minimum height for yasr
|
|
44
43
|
|
|
45
44
|
--yasgui-endpoint-button-bg: #337ab7;
|
|
46
45
|
--yasgui-endpoint-button-border: #337ab7;
|
package/src/version.ts
CHANGED