@matdata/yasgui 5.4.0 → 5.5.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@matdata/yasgui",
3
3
  "description": "Yet Another SPARQL GUI",
4
- "version": "5.4.0",
4
+ "version": "5.5.0",
5
5
  "main": "build/yasgui.min.js",
6
6
  "types": "build/ts/src/index.d.ts",
7
7
  "license": "MIT",
@@ -304,6 +304,7 @@ export function parseFromTurtle(turtle: string): Partial<PersistedJson> {
304
304
  headers: {},
305
305
  withCredentials: false,
306
306
  adjustQueryBeforeRequest: false,
307
+ basicAuth: undefined,
307
308
  },
308
309
  yasr: {
309
310
  settings: {},
@@ -1,5 +1,5 @@
1
1
  import { Storage as YStorage } from "@matdata/yasgui-utils";
2
- import Yasgui, { EndpointButton } from "./";
2
+ import Yasgui, { EndpointButton, EndpointConfig } from "./";
3
3
  import * as Tab from "./Tab";
4
4
  export var storageNamespace = "triply";
5
5
  export interface PersistedJson {
@@ -10,7 +10,8 @@ export interface PersistedJson {
10
10
  lastClosedTab: { index: number; tab: Tab.PersistedJson } | undefined;
11
11
  prefixes?: string;
12
12
  autoCaptureEnabled?: boolean;
13
- customEndpointButtons?: EndpointButton[];
13
+ customEndpointButtons?: EndpointButton[]; // Legacy, kept for backwards compatibility
14
+ endpointConfigs?: EndpointConfig[]; // New endpoint-based storage with auth
14
15
  theme?: "light" | "dark";
15
16
  orientation?: "vertical" | "horizontal";
16
17
  }
@@ -24,6 +25,7 @@ function getDefaults(): PersistedJson {
24
25
  prefixes: "",
25
26
  autoCaptureEnabled: true,
26
27
  customEndpointButtons: [],
28
+ endpointConfigs: [],
27
29
  };
28
30
  }
29
31
 
@@ -163,6 +165,46 @@ export default class PersistentConfig {
163
165
  this.persistedJson.customEndpointButtons = buttons;
164
166
  this.toStorage();
165
167
  }
168
+
169
+ // New endpoint configuration methods
170
+ public getEndpointConfigs(): EndpointConfig[] {
171
+ return this.persistedJson.endpointConfigs || [];
172
+ }
173
+
174
+ public setEndpointConfigs(configs: EndpointConfig[]) {
175
+ this.persistedJson.endpointConfigs = configs;
176
+ this.toStorage();
177
+ }
178
+
179
+ public addOrUpdateEndpoint(endpoint: string, updates: Partial<Omit<EndpointConfig, "endpoint">>) {
180
+ const configs = this.getEndpointConfigs();
181
+ const existingIndex = configs.findIndex((c) => c.endpoint === endpoint);
182
+
183
+ if (existingIndex >= 0) {
184
+ // Update existing endpoint
185
+ const merged = { ...configs[existingIndex], ...updates };
186
+ if ("authentication" in updates && updates.authentication === undefined) {
187
+ delete merged.authentication;
188
+ }
189
+ configs[existingIndex] = merged;
190
+ } else {
191
+ // Add new endpoint
192
+ configs.push({ endpoint, ...updates });
193
+ }
194
+
195
+ this.setEndpointConfigs(configs);
196
+ }
197
+
198
+ public getEndpointConfig(endpoint: string): EndpointConfig | undefined {
199
+ const configs = this.getEndpointConfigs();
200
+ return configs.find((c) => c.endpoint === endpoint);
201
+ }
202
+
203
+ public deleteEndpointConfig(endpoint: string) {
204
+ const configs = this.getEndpointConfigs();
205
+ const filtered = configs.filter((c) => c.endpoint !== endpoint);
206
+ this.setEndpointConfigs(filtered);
207
+ }
166
208
  public static clear() {
167
209
  const storage = new YStorage(storageNamespace);
168
210
  storage.removeNamespace();
package/src/Tab.ts CHANGED
@@ -241,8 +241,8 @@ export class Tab extends EventEmitter {
241
241
  return this.persistentJson.requestConfig;
242
242
  }
243
243
  private initControlbar() {
244
- this.initEndpointSelectField();
245
244
  this.initOrientationToggle();
245
+ this.initEndpointSelectField();
246
246
  this.initEndpointButtons();
247
247
  if (this.yasgui.config.endpointInfo && this.controlBarEl) {
248
248
  this.controlBarEl.appendChild(this.yasgui.config.endpointInfo());
@@ -359,10 +359,19 @@ export class Tab extends EventEmitter {
359
359
  // Clear existing buttons
360
360
  this.endpointButtonsContainer.innerHTML = "";
361
361
 
362
- // Merge config buttons with custom user buttons
362
+ // Get config buttons (for backwards compatibility)
363
363
  const configButtons = this.yasgui.config.endpointButtons || [];
364
+
365
+ // Get endpoint configs where showAsButton is true
366
+ const endpointConfigs = this.yasgui.persistentConfig.getEndpointConfigs();
367
+ const endpointButtons = endpointConfigs
368
+ .filter((config) => config.showAsButton && config.label)
369
+ .map((config) => ({ endpoint: config.endpoint, label: config.label! }));
370
+
371
+ // Also include legacy custom buttons for backwards compatibility
364
372
  const customButtons = this.yasgui.persistentConfig.getCustomEndpointButtons();
365
- const allButtons = [...configButtons, ...customButtons];
373
+
374
+ const allButtons = [...configButtons, ...endpointButtons, ...customButtons];
366
375
 
367
376
  if (allButtons.length === 0) {
368
377
  // Hide container if no buttons
@@ -413,6 +422,11 @@ export class Tab extends EventEmitter {
413
422
  this.persistentJson.requestConfig.endpoint = endpoint;
414
423
  this.emit("change", this, this.persistentJson);
415
424
  this.emit("endpointChange", this, endpoint);
425
+
426
+ // Auto-track this endpoint in endpoint configs (if not already present)
427
+ if (endpoint && !this.yasgui.persistentConfig.getEndpointConfig(endpoint)) {
428
+ this.yasgui.persistentConfig.addOrUpdateEndpoint(endpoint, {});
429
+ }
416
430
  }
417
431
  if (this.endpointSelect instanceof EndpointSelect) {
418
432
  this.endpointSelect.setEndpoint(endpoint, endpointHistory);
@@ -464,6 +478,28 @@ export class Tab extends EventEmitter {
464
478
  this.emit("change", this, this.persistentJson);
465
479
  }
466
480
 
481
+ /**
482
+ * Get authentication configuration for the current endpoint
483
+ * This retrieves auth from the endpoint-based storage
484
+ */
485
+ private getAuthForCurrentEndpoint() {
486
+ const endpoint = this.getEndpoint();
487
+ if (!endpoint) return undefined;
488
+
489
+ const endpointConfig = this.yasgui.persistentConfig.getEndpointConfig(endpoint);
490
+ if (!endpointConfig || !endpointConfig.authentication) return undefined;
491
+
492
+ // Convert endpoint auth to requestConfig format
493
+ if (endpointConfig.authentication.type === "basic") {
494
+ return {
495
+ username: endpointConfig.authentication.username,
496
+ password: endpointConfig.authentication.password,
497
+ };
498
+ }
499
+
500
+ return undefined;
501
+ }
502
+
467
503
  /**
468
504
  * The Yasgui configuration object may contain a custom request config
469
505
  * This request config object can contain getter functions, or plain json
@@ -524,6 +560,14 @@ export class Tab extends EventEmitter {
524
560
  //The adjustQueryBeforeRequest is meant to be a function though, so let's copy that as is
525
561
  adjustQueryBeforeRequest: this.yasgui.config.requestConfig.adjustQueryBeforeRequest,
526
562
  };
563
+
564
+ // Inject authentication from endpoint-based storage
565
+ // Only inject endpoint-based auth if basicAuth is not already set
566
+ const endpointAuth = this.getAuthForCurrentEndpoint();
567
+ if (endpointAuth && typeof processedReqConfig.basicAuth === "undefined") {
568
+ processedReqConfig.basicAuth = endpointAuth;
569
+ }
570
+
527
571
  if (this.yasgui.config.corsProxy && !Yasgui.corsEnabled[this.getEndpoint()]) {
528
572
  return {
529
573
  ...processedReqConfig,
@@ -104,36 +104,49 @@
104
104
 
105
105
  .modalBody {
106
106
  flex: 1;
107
- overflow-y: auto;
108
- padding: 20px;
107
+ display: flex;
108
+ overflow: hidden;
109
109
  }
110
110
 
111
- .modalTabs {
111
+ .modalSidebar {
112
+ width: 200px;
113
+ min-width: 200px;
114
+ border-right: 1px solid var(--yasgui-border-color, #e0e0e0);
115
+ background: var(--yasgui-bg-secondary, #f8f9fa);
112
116
  display: flex;
113
- gap: 10px;
114
- margin-bottom: 20px;
115
- border-bottom: 2px solid var(--yasgui-border-color, #e0e0e0);
117
+ flex-direction: column;
118
+ overflow-y: auto;
119
+ }
120
+
121
+ .modalContentArea {
122
+ flex: 1;
123
+ overflow-y: auto;
124
+ padding: 20px;
116
125
  }
117
126
 
118
- .modalTabButton {
127
+ .modalNavButton {
119
128
  background: none;
120
129
  border: none;
121
- padding: 10px 20px;
130
+ padding: 12px 20px;
122
131
  font-size: 14px;
123
- font-weight: 600;
132
+ font-weight: 500;
124
133
  cursor: pointer;
125
134
  color: var(--yasgui-text-secondary, #666);
126
- border-bottom: 3px solid transparent;
127
- margin-bottom: -2px;
135
+ text-align: left;
136
+ border-left: 3px solid transparent;
128
137
  transition: all 0.2s;
138
+ white-space: nowrap;
129
139
 
130
140
  &:hover {
141
+ background: var(--yasgui-bg-primary, white);
131
142
  color: var(--yasgui-accent-color, #337ab7);
132
143
  }
133
144
 
134
145
  &.active {
135
146
  color: var(--yasgui-accent-color, #337ab7);
136
- border-bottom-color: var(--yasgui-accent-color, #337ab7);
147
+ background: var(--yasgui-bg-primary, white);
148
+ border-left-color: var(--yasgui-accent-color, #337ab7);
149
+ font-weight: 600;
137
150
  }
138
151
  }
139
152
 
@@ -185,8 +198,7 @@
185
198
  padding: 10px;
186
199
  border: 1px solid var(--yasgui-input-border, #ccc);
187
200
  border-radius: 4px;
188
- font-family:
189
- "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
201
+ font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
190
202
  font-size: 13px;
191
203
  resize: vertical;
192
204
  box-sizing: border-box;
@@ -222,6 +234,60 @@
222
234
  }
223
235
  }
224
236
 
237
+ .settingsInput {
238
+ width: 100%;
239
+ padding: 8px 12px;
240
+ border: 1px solid var(--yasgui-input-border, #ccc);
241
+ border-radius: 4px;
242
+ font-size: 14px;
243
+ background-color: var(--yasgui-bg-secondary, white);
244
+ color: var(--yasgui-text-primary, #000);
245
+ box-sizing: border-box;
246
+
247
+ &:focus {
248
+ outline: none;
249
+ border-color: var(--yasgui-input-focus, #337ab7);
250
+ box-shadow: 0 0 0 2px rgba(51, 122, 183, 0.1);
251
+ }
252
+
253
+ &:disabled {
254
+ background-color: var(--yasgui-bg-tertiary, #f5f5f5);
255
+ color: var(--yasgui-text-muted, #999);
256
+ cursor: not-allowed;
257
+ }
258
+
259
+ &::placeholder {
260
+ color: var(--yasgui-text-muted, #999);
261
+ }
262
+ }
263
+
264
+ .settingsSection.disabled {
265
+ opacity: 0.6;
266
+ pointer-events: none;
267
+ }
268
+
269
+ .securityNotice {
270
+ background: var(--yasgui-warning-bg, #fff3cd);
271
+ color: var(--yasgui-warning-text, #856404);
272
+ padding: 12px;
273
+ border-radius: 4px;
274
+ border: 1px solid var(--yasgui-warning-border, #ffeaa7);
275
+
276
+ strong {
277
+ display: block;
278
+ margin-bottom: 8px;
279
+ }
280
+
281
+ ul {
282
+ margin: 8px 0 0 20px;
283
+ padding: 0;
284
+ }
285
+
286
+ li {
287
+ margin: 4px 0;
288
+ }
289
+ }
290
+
225
291
  .modalFooter {
226
292
  padding: 15px 20px;
227
293
  border-top: 1px solid var(--yasgui-border-color, #e0e0e0);
@@ -496,8 +562,7 @@
496
562
  display: inline-block;
497
563
  padding: 4px 8px;
498
564
  font-size: 12px;
499
- font-family:
500
- SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
565
+ font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
501
566
  line-height: 1.4;
502
567
  color: var(--yasgui-text-primary, #333);
503
568
  background-color: var(--yasgui-bg-secondary, #f7f7f7);
@@ -618,3 +683,307 @@
618
683
  }
619
684
  }
620
685
  }
686
+
687
+ // Endpoints table styling
688
+ .endpointsTable {
689
+ margin-top: 20px;
690
+ }
691
+
692
+ .endpointsTableElement {
693
+ width: 100%;
694
+ border-collapse: collapse;
695
+ font-size: 14px;
696
+
697
+ th,
698
+ td {
699
+ padding: 10px;
700
+ text-align: left;
701
+ border-bottom: 1px solid var(--yasgui-border-color, #e0e0e0);
702
+ }
703
+
704
+ th {
705
+ font-weight: 600;
706
+ color: var(--yasgui-text-primary, #000);
707
+ background: var(--yasgui-bg-secondary, #f9f9f9);
708
+ }
709
+
710
+ td {
711
+ color: var(--yasgui-text-primary, #333);
712
+ }
713
+
714
+ .endpointCell {
715
+ max-width: 300px;
716
+ overflow: hidden;
717
+ text-overflow: ellipsis;
718
+ white-space: nowrap;
719
+ }
720
+
721
+ .centerCell {
722
+ text-align: center;
723
+ }
724
+
725
+ .endpointLabelInput {
726
+ width: 100%;
727
+ padding: 6px 8px;
728
+ border: 1px solid var(--yasgui-input-border, #ddd);
729
+ border-radius: 4px;
730
+ background: var(--yasgui-bg-primary, white);
731
+ color: var(--yasgui-text-primary, #333);
732
+ font-size: 14px;
733
+
734
+ &:focus {
735
+ outline: none;
736
+ border-color: var(--yasgui-accent-color, #337ab7);
737
+ }
738
+ }
739
+
740
+ .configureAuthButton {
741
+ padding: 6px 12px;
742
+ border: 1px solid var(--yasgui-border-color, #ddd);
743
+ border-radius: 4px;
744
+ background: var(--yasgui-bg-primary, white);
745
+ color: var(--yasgui-text-primary, #333);
746
+ font-size: 13px;
747
+ cursor: pointer;
748
+ transition: all 0.2s;
749
+
750
+ &:hover {
751
+ background: var(--yasgui-bg-secondary, #f5f5f5);
752
+ }
753
+
754
+ &.authenticated {
755
+ background: var(--yasgui-success-bg, #d4edda);
756
+ color: var(--yasgui-success-text, #155724);
757
+ border-color: var(--yasgui-success-border, #c3e6cb);
758
+ }
759
+ }
760
+
761
+ .deleteEndpointButton {
762
+ padding: 6px 12px;
763
+ border: 1px solid var(--yasgui-error-border, #f5c6cb);
764
+ border-radius: 4px;
765
+ background: var(--yasgui-error-bg, #f8d7da);
766
+ color: var(--yasgui-error-text, #721c24);
767
+ font-size: 13px;
768
+ cursor: pointer;
769
+ transition: all 0.2s;
770
+
771
+ &:hover {
772
+ background: var(--yasgui-error-hover, #f1b0b7);
773
+ }
774
+ }
775
+ }
776
+
777
+ // Authentication modal styling
778
+ .authModalOverlay {
779
+ position: fixed;
780
+ top: 0;
781
+ left: 0;
782
+ right: 0;
783
+ bottom: 0;
784
+ background: rgba(0, 0, 0, 0.6);
785
+ z-index: 10001;
786
+ display: flex;
787
+ align-items: center;
788
+ justify-content: center;
789
+ }
790
+
791
+ .authModal {
792
+ background: var(--yasgui-bg-primary, white);
793
+ border-radius: 8px;
794
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
795
+ max-width: 500px;
796
+ width: 90%;
797
+ max-height: 90vh;
798
+ overflow: auto;
799
+ }
800
+
801
+ .authModalHeader {
802
+ padding: 20px;
803
+ border-bottom: 1px solid var(--yasgui-border-color, #e0e0e0);
804
+
805
+ h3 {
806
+ margin: 0 0 8px 0;
807
+ font-size: 18px;
808
+ font-weight: 600;
809
+ color: var(--yasgui-text-primary, #000);
810
+ }
811
+ }
812
+
813
+ .authModalSubtitle {
814
+ font-size: 13px;
815
+ color: var(--yasgui-text-secondary, #666);
816
+ word-break: break-all;
817
+ }
818
+
819
+ .authModalBody {
820
+ padding: 20px;
821
+ }
822
+
823
+ .authModalSection {
824
+ margin-bottom: 20px;
825
+
826
+ label {
827
+ display: block;
828
+ font-size: 14px;
829
+ font-weight: 500;
830
+ color: var(--yasgui-text-primary, #333);
831
+ margin-bottom: 8px;
832
+ }
833
+
834
+ input,
835
+ select {
836
+ width: 90%;
837
+ padding: 8px 10px;
838
+ border: 1px solid var(--yasgui-input-border, #ddd);
839
+ border-radius: 4px;
840
+ background: var(--yasgui-bg-primary, white);
841
+ color: var(--yasgui-text-primary, #333);
842
+ font-size: 14px;
843
+
844
+ &:focus {
845
+ outline: none;
846
+ border-color: var(--yasgui-accent-color, #337ab7);
847
+ box-shadow: 0 0 0 3px var(--yasgui-accent-light, rgba(51, 122, 183, 0.1));
848
+ }
849
+ }
850
+ }
851
+
852
+ .authSecurityNotice {
853
+ padding: 12px;
854
+ background: var(--yasgui-warning-bg, #fff3cd);
855
+ border: 1px solid var(--yasgui-warning-border, #ffeaa7);
856
+ border-radius: 4px;
857
+ font-size: 13px;
858
+ color: var(--yasgui-warning-text, #856404);
859
+
860
+ strong {
861
+ display: block;
862
+ margin-bottom: 8px;
863
+ }
864
+
865
+ ul {
866
+ margin: 0;
867
+ padding-left: 20px;
868
+
869
+ li {
870
+ margin: 4px 0;
871
+ }
872
+ }
873
+ }
874
+
875
+ .authModalFooter {
876
+ display: flex;
877
+ gap: 10px;
878
+ padding: 20px;
879
+ border-top: 1px solid var(--yasgui-border-color, #e0e0e0);
880
+ justify-content: flex-end;
881
+
882
+ button {
883
+ padding: 8px 16px;
884
+ border-radius: 4px;
885
+ font-size: 14px;
886
+ font-weight: 500;
887
+ cursor: pointer;
888
+ transition: all 0.2s;
889
+ border: none;
890
+
891
+ &.authRemoveButton {
892
+ background: transparent;
893
+ color: var(--yasgui-error-text, #dc3545);
894
+ border: 1px solid var(--yasgui-error-border, #dc3545);
895
+ margin-right: auto;
896
+
897
+ &:hover:not(:disabled) {
898
+ background: var(--yasgui-error-bg, #f8d7da);
899
+ }
900
+
901
+ &:disabled {
902
+ opacity: 0.4;
903
+ cursor: not-allowed;
904
+ }
905
+ }
906
+
907
+ &.authCancelButton {
908
+ background: var(--yasgui-bg-secondary, #f5f5f5);
909
+ color: var(--yasgui-text-primary, #333);
910
+ border: 1px solid var(--yasgui-border-color, #ddd);
911
+
912
+ &:hover {
913
+ background: var(--yasgui-bg-tertiary, #e0e0e0);
914
+ }
915
+ }
916
+
917
+ &.authSaveButton {
918
+ background: var(--yasgui-accent-color, #337ab7);
919
+ color: white;
920
+
921
+ &:hover {
922
+ background: var(--yasgui-accent-dark, #2563a1);
923
+ }
924
+ }
925
+ }
926
+ }
927
+
928
+ // Add endpoint form styling
929
+ .addEndpointForm {
930
+ margin-top: 20px;
931
+ padding: 20px;
932
+ background: var(--yasgui-bg-secondary, #f9f9f9);
933
+ border-radius: 4px;
934
+ border: 1px solid var(--yasgui-border-color, #e0e0e0);
935
+ }
936
+
937
+ .addFormTitle {
938
+ font-size: 14px;
939
+ font-weight: 600;
940
+ color: var(--yasgui-text-primary, #333);
941
+ margin-bottom: 12px;
942
+ }
943
+
944
+ .addFormInputs {
945
+ display: flex;
946
+ gap: 10px;
947
+ align-items: center;
948
+ }
949
+
950
+ .addEndpointInput {
951
+ flex: 1;
952
+ padding: 8px 12px;
953
+ border: 1px solid var(--yasgui-input-border, #ddd);
954
+ border-radius: 4px;
955
+ background: var(--yasgui-bg-primary, white);
956
+ color: var(--yasgui-text-primary, #333);
957
+ font-size: 14px;
958
+
959
+ &:focus {
960
+ outline: none;
961
+ border-color: var(--yasgui-accent-color, #337ab7);
962
+ box-shadow: 0 0 0 3px var(--yasgui-accent-light, rgba(51, 122, 183, 0.1));
963
+ }
964
+
965
+ &::placeholder {
966
+ color: var(--yasgui-text-secondary, #999);
967
+ }
968
+ }
969
+
970
+ .addEndpointButton {
971
+ padding: 8px 16px;
972
+ border: none;
973
+ border-radius: 4px;
974
+ background: var(--yasgui-accent-color, #337ab7);
975
+ color: white;
976
+ font-size: 14px;
977
+ font-weight: 500;
978
+ cursor: pointer;
979
+ transition: all 0.2s;
980
+ white-space: nowrap;
981
+
982
+ &:hover {
983
+ background: var(--yasgui-accent-dark, #2563a1);
984
+ }
985
+
986
+ &:active {
987
+ transform: translateY(1px);
988
+ }
989
+ }