@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.
@@ -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: 800px;
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-border-color, #d0d0d0);
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;
@@ -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
- authModalOverlay.remove();
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
- authModalOverlay.remove();
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
- authModalOverlay.remove();
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
- authModalOverlay.remove();
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: block;
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
- // Allow flexible height - can use 100vh or be constrained by parent
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
- // Make YASQE fill the vertical space in horizontal mode
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
- .CodeMirror {
57
- height: calc(100vh - var(--yasgui-header-height)) !important;
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
- overflow: hidden;
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; //font families can slightly misalign svgs with the .svgimg div
152
+ font-family: initial;
94
153
  }
95
- // IE11 Needs this specified otherwise it will not resize the svg
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
- // Layout dimensions (used for horizontal orientation)
42
- --yasgui-header-height: 150px; // Height to subtract from viewport in horizontal layout
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
@@ -1,3 +1,3 @@
1
1
  // Version information for YASGUI
2
2
  // This file is auto-generated during build - do not edit manually
3
- export const VERSION = "5.6.0";
3
+ export const VERSION = "5.8.0";