@matdata/yasgui 5.4.0 → 5.6.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/build/ts/src/ConfigExportImport.js +3 -0
- package/build/ts/src/ConfigExportImport.js.map +1 -1
- package/build/ts/src/PersistentConfig.d.ts +10 -1
- package/build/ts/src/PersistentConfig.js +39 -0
- package/build/ts/src/PersistentConfig.js.map +1 -1
- package/build/ts/src/Tab.d.ts +1 -1
- package/build/ts/src/Tab.js +76 -78
- package/build/ts/src/Tab.js.map +1 -1
- package/build/ts/src/TabSettingsModal.d.ts +3 -2
- package/build/ts/src/TabSettingsModal.js +453 -121
- package/build/ts/src/TabSettingsModal.js.map +1 -1
- package/build/ts/src/defaults.js +1 -1
- package/build/ts/src/defaults.js.map +1 -1
- package/build/ts/src/index.d.ts +18 -5
- package/build/ts/src/index.js +4 -1
- 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 +128 -121
- package/build/yasgui.min.js.map +3 -3
- package/package.json +1 -1
- package/src/ConfigExportImport.ts +3 -0
- package/src/PersistentConfig.ts +54 -2
- package/src/Tab.ts +147 -106
- package/src/TabSettingsModal.scss +385 -16
- package/src/TabSettingsModal.ts +564 -150
- package/src/defaults.ts +1 -1
- package/src/endpointSelect.scss +12 -0
- package/src/index.ts +31 -3
- package/src/version.ts +1 -1
package/src/TabSettingsModal.ts
CHANGED
|
@@ -34,6 +34,9 @@ const AcceptHeaderGraphMap: { key: string; value: string }[] = [
|
|
|
34
34
|
{ key: "TSV", value: "text/tab-separated-values,*/*;q=0.9" },
|
|
35
35
|
];
|
|
36
36
|
|
|
37
|
+
// Default API Key header name
|
|
38
|
+
const DEFAULT_API_KEY_HEADER = "X-API-Key";
|
|
39
|
+
|
|
37
40
|
export default class TabSettingsModal {
|
|
38
41
|
private tab: Tab;
|
|
39
42
|
private modalOverlay!: HTMLElement;
|
|
@@ -111,56 +114,63 @@ export default class TabSettingsModal {
|
|
|
111
114
|
|
|
112
115
|
this.modalContent.appendChild(header);
|
|
113
116
|
|
|
114
|
-
// Body with
|
|
117
|
+
// Body with sidebar navigation
|
|
115
118
|
const body = document.createElement("div");
|
|
116
119
|
addClass(body, "modalBody");
|
|
117
120
|
|
|
118
|
-
|
|
119
|
-
|
|
121
|
+
// Sidebar navigation
|
|
122
|
+
const sidebar = document.createElement("div");
|
|
123
|
+
addClass(sidebar, "modalSidebar");
|
|
120
124
|
|
|
121
125
|
const requestTab = document.createElement("button");
|
|
122
126
|
requestTab.textContent = "Request";
|
|
123
|
-
addClass(requestTab, "
|
|
127
|
+
addClass(requestTab, "modalNavButton", "active");
|
|
124
128
|
requestTab.onclick = () => this.switchTab("request");
|
|
125
129
|
|
|
130
|
+
const endpointsTab = document.createElement("button");
|
|
131
|
+
endpointsTab.textContent = "SPARQL Endpoints";
|
|
132
|
+
addClass(endpointsTab, "modalNavButton");
|
|
133
|
+
endpointsTab.onclick = () => this.switchTab("endpoints");
|
|
134
|
+
|
|
126
135
|
const prefixTab = document.createElement("button");
|
|
127
136
|
prefixTab.textContent = "Prefixes";
|
|
128
|
-
addClass(prefixTab, "
|
|
137
|
+
addClass(prefixTab, "modalNavButton");
|
|
129
138
|
prefixTab.onclick = () => this.switchTab("prefix");
|
|
130
139
|
|
|
131
140
|
const editorTab = document.createElement("button");
|
|
132
141
|
editorTab.textContent = "Editor";
|
|
133
|
-
addClass(editorTab, "
|
|
142
|
+
addClass(editorTab, "modalNavButton");
|
|
134
143
|
editorTab.onclick = () => this.switchTab("editor");
|
|
135
144
|
|
|
136
|
-
const endpointsTab = document.createElement("button");
|
|
137
|
-
endpointsTab.textContent = "Endpoint Buttons";
|
|
138
|
-
addClass(endpointsTab, "modalTabButton");
|
|
139
|
-
endpointsTab.onclick = () => this.switchTab("endpoints");
|
|
140
|
-
|
|
141
145
|
const importExportTab = document.createElement("button");
|
|
142
146
|
importExportTab.textContent = "Import/Export";
|
|
143
|
-
addClass(importExportTab, "
|
|
147
|
+
addClass(importExportTab, "modalNavButton");
|
|
144
148
|
importExportTab.onclick = () => this.switchTab("importexport");
|
|
145
149
|
|
|
146
150
|
const shortcutsTab = document.createElement("button");
|
|
147
151
|
shortcutsTab.textContent = "Keyboard Shortcuts";
|
|
148
|
-
addClass(shortcutsTab, "
|
|
152
|
+
addClass(shortcutsTab, "modalNavButton");
|
|
149
153
|
shortcutsTab.onclick = () => this.switchTab("shortcuts");
|
|
150
154
|
|
|
151
155
|
const aboutTab = document.createElement("button");
|
|
152
156
|
aboutTab.textContent = "About";
|
|
153
|
-
addClass(aboutTab, "
|
|
157
|
+
addClass(aboutTab, "modalNavButton");
|
|
154
158
|
aboutTab.onclick = () => this.switchTab("about");
|
|
155
159
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
160
|
+
sidebar.appendChild(requestTab);
|
|
161
|
+
sidebar.appendChild(endpointsTab);
|
|
162
|
+
sidebar.appendChild(prefixTab);
|
|
163
|
+
sidebar.appendChild(editorTab);
|
|
164
|
+
sidebar.appendChild(importExportTab);
|
|
165
|
+
sidebar.appendChild(shortcutsTab);
|
|
166
|
+
sidebar.appendChild(aboutTab);
|
|
167
|
+
|
|
168
|
+
// Content area container
|
|
169
|
+
const contentArea = document.createElement("div");
|
|
170
|
+
addClass(contentArea, "modalContentArea");
|
|
171
|
+
|
|
172
|
+
body.appendChild(sidebar);
|
|
173
|
+
body.appendChild(contentArea);
|
|
164
174
|
|
|
165
175
|
// Tab content containers
|
|
166
176
|
const requestContent = document.createElement("div");
|
|
@@ -168,6 +178,11 @@ export default class TabSettingsModal {
|
|
|
168
178
|
requestContent.id = "request-content";
|
|
169
179
|
this.drawRequestSettings(requestContent);
|
|
170
180
|
|
|
181
|
+
const endpointsContent = document.createElement("div");
|
|
182
|
+
addClass(endpointsContent, "modalTabContent");
|
|
183
|
+
endpointsContent.id = "endpoints-content";
|
|
184
|
+
this.drawEndpointsSettings(endpointsContent);
|
|
185
|
+
|
|
171
186
|
const prefixContent = document.createElement("div");
|
|
172
187
|
addClass(prefixContent, "modalTabContent");
|
|
173
188
|
prefixContent.id = "prefix-content";
|
|
@@ -178,11 +193,6 @@ export default class TabSettingsModal {
|
|
|
178
193
|
editorContent.id = "editor-content";
|
|
179
194
|
this.drawEditorSettings(editorContent);
|
|
180
195
|
|
|
181
|
-
const endpointsContent = document.createElement("div");
|
|
182
|
-
addClass(endpointsContent, "modalTabContent");
|
|
183
|
-
endpointsContent.id = "endpoints-content";
|
|
184
|
-
this.drawEndpointButtonsSettings(endpointsContent);
|
|
185
|
-
|
|
186
196
|
const importExportContent = document.createElement("div");
|
|
187
197
|
addClass(importExportContent, "modalTabContent");
|
|
188
198
|
importExportContent.id = "importexport-content";
|
|
@@ -198,13 +208,13 @@ export default class TabSettingsModal {
|
|
|
198
208
|
aboutContent.id = "about-content";
|
|
199
209
|
this.drawAboutSettings(aboutContent);
|
|
200
210
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
211
|
+
contentArea.appendChild(requestContent);
|
|
212
|
+
contentArea.appendChild(endpointsContent);
|
|
213
|
+
contentArea.appendChild(prefixContent);
|
|
214
|
+
contentArea.appendChild(editorContent);
|
|
215
|
+
contentArea.appendChild(importExportContent);
|
|
216
|
+
contentArea.appendChild(shortcutsContent);
|
|
217
|
+
contentArea.appendChild(aboutContent);
|
|
208
218
|
|
|
209
219
|
this.modalContent.appendChild(body);
|
|
210
220
|
|
|
@@ -232,15 +242,15 @@ export default class TabSettingsModal {
|
|
|
232
242
|
}
|
|
233
243
|
|
|
234
244
|
private switchTab(tabName: string) {
|
|
235
|
-
const buttons = this.modalContent.querySelectorAll(".
|
|
245
|
+
const buttons = this.modalContent.querySelectorAll(".modalNavButton");
|
|
236
246
|
const contents = this.modalContent.querySelectorAll(".modalTabContent");
|
|
237
247
|
|
|
238
248
|
buttons.forEach((btn, index) => {
|
|
239
249
|
if (
|
|
240
250
|
(tabName === "request" && index === 0) ||
|
|
241
|
-
(tabName === "
|
|
242
|
-
(tabName === "
|
|
243
|
-
(tabName === "
|
|
251
|
+
(tabName === "endpoints" && index === 1) ||
|
|
252
|
+
(tabName === "prefix" && index === 2) ||
|
|
253
|
+
(tabName === "editor" && index === 3) ||
|
|
244
254
|
(tabName === "importexport" && index === 4) ||
|
|
245
255
|
(tabName === "shortcuts" && index === 5) ||
|
|
246
256
|
(tabName === "about" && index === 6)
|
|
@@ -416,7 +426,10 @@ export default class TabSettingsModal {
|
|
|
416
426
|
const snippetsBarCheckbox = document.createElement("input");
|
|
417
427
|
snippetsBarCheckbox.type = "checkbox";
|
|
418
428
|
snippetsBarCheckbox.id = "showSnippetsBar";
|
|
419
|
-
|
|
429
|
+
// Read from global config
|
|
430
|
+
const persistedValue = this.tab.yasgui.persistentConfig.getShowSnippetsBar();
|
|
431
|
+
snippetsBarCheckbox.checked =
|
|
432
|
+
persistedValue !== undefined ? persistedValue : this.tab.yasgui.config.showSnippetsBar !== false;
|
|
420
433
|
|
|
421
434
|
const snippetsBarLabel = document.createElement("label");
|
|
422
435
|
snippetsBarLabel.htmlFor = "showSnippetsBar";
|
|
@@ -436,6 +449,496 @@ export default class TabSettingsModal {
|
|
|
436
449
|
container.appendChild(snippetsBarSection);
|
|
437
450
|
}
|
|
438
451
|
|
|
452
|
+
private drawEndpointsSettings(container: HTMLElement) {
|
|
453
|
+
const section = document.createElement("div");
|
|
454
|
+
addClass(section, "settingsSection");
|
|
455
|
+
|
|
456
|
+
const label = document.createElement("label");
|
|
457
|
+
label.textContent = "SPARQL Endpoints";
|
|
458
|
+
addClass(label, "settingsLabel");
|
|
459
|
+
|
|
460
|
+
const help = document.createElement("div");
|
|
461
|
+
help.textContent =
|
|
462
|
+
"Manage your SPARQL endpoints. Each endpoint can have its own authentication and be displayed as a quick-switch button.";
|
|
463
|
+
addClass(help, "settingsHelp");
|
|
464
|
+
|
|
465
|
+
section.appendChild(label);
|
|
466
|
+
section.appendChild(help);
|
|
467
|
+
|
|
468
|
+
// List of endpoints
|
|
469
|
+
const endpointsList = document.createElement("div");
|
|
470
|
+
addClass(endpointsList, "endpointsTable");
|
|
471
|
+
this.renderEndpointsList(endpointsList);
|
|
472
|
+
section.appendChild(endpointsList);
|
|
473
|
+
|
|
474
|
+
container.appendChild(section);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
private renderEndpointsList(container: HTMLElement) {
|
|
478
|
+
container.innerHTML = "";
|
|
479
|
+
const configs = this.tab.yasgui.persistentConfig.getEndpointConfigs();
|
|
480
|
+
|
|
481
|
+
// Create table (even if empty, we'll show add form)
|
|
482
|
+
const table = document.createElement("table");
|
|
483
|
+
addClass(table, "endpointsTableElement");
|
|
484
|
+
|
|
485
|
+
// Header
|
|
486
|
+
const thead = document.createElement("thead");
|
|
487
|
+
const headerRow = document.createElement("tr");
|
|
488
|
+
const headers = ["Endpoint", "Label", "Button", "Authentication", "Actions"];
|
|
489
|
+
headers.forEach((h) => {
|
|
490
|
+
const th = document.createElement("th");
|
|
491
|
+
th.textContent = h;
|
|
492
|
+
headerRow.appendChild(th);
|
|
493
|
+
});
|
|
494
|
+
thead.appendChild(headerRow);
|
|
495
|
+
table.appendChild(thead);
|
|
496
|
+
|
|
497
|
+
// Body
|
|
498
|
+
const tbody = document.createElement("tbody");
|
|
499
|
+
|
|
500
|
+
if (configs.length === 0) {
|
|
501
|
+
// Show empty message in table
|
|
502
|
+
const emptyRow = document.createElement("tr");
|
|
503
|
+
const emptyCell = document.createElement("td");
|
|
504
|
+
emptyCell.colSpan = 5;
|
|
505
|
+
emptyCell.textContent = "No endpoints yet. Add one below or access an endpoint to have it automatically tracked.";
|
|
506
|
+
addClass(emptyCell, "emptyMessage");
|
|
507
|
+
emptyCell.style.textAlign = "center";
|
|
508
|
+
emptyCell.style.padding = "20px";
|
|
509
|
+
emptyRow.appendChild(emptyCell);
|
|
510
|
+
tbody.appendChild(emptyRow);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
configs.forEach((config, index) => {
|
|
514
|
+
const row = document.createElement("tr");
|
|
515
|
+
|
|
516
|
+
// Endpoint column
|
|
517
|
+
const endpointCell = document.createElement("td");
|
|
518
|
+
endpointCell.textContent = config.endpoint;
|
|
519
|
+
endpointCell.title = config.endpoint;
|
|
520
|
+
addClass(endpointCell, "endpointCell");
|
|
521
|
+
row.appendChild(endpointCell);
|
|
522
|
+
|
|
523
|
+
// Label column (editable)
|
|
524
|
+
const labelCell = document.createElement("td");
|
|
525
|
+
const labelInput = document.createElement("input");
|
|
526
|
+
labelInput.type = "text";
|
|
527
|
+
labelInput.value = config.label || "";
|
|
528
|
+
labelInput.placeholder = "Optional label";
|
|
529
|
+
addClass(labelInput, "endpointLabelInput");
|
|
530
|
+
labelInput.onchange = () => {
|
|
531
|
+
this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(config.endpoint, {
|
|
532
|
+
label: labelInput.value.trim() || undefined,
|
|
533
|
+
});
|
|
534
|
+
this.renderEndpointsList(container);
|
|
535
|
+
this.tab.refreshEndpointButtons();
|
|
536
|
+
};
|
|
537
|
+
labelCell.appendChild(labelInput);
|
|
538
|
+
row.appendChild(labelCell);
|
|
539
|
+
|
|
540
|
+
// Show as Button checkbox (requires label)
|
|
541
|
+
const buttonCell = document.createElement("td");
|
|
542
|
+
const buttonCheckbox = document.createElement("input");
|
|
543
|
+
buttonCheckbox.type = "checkbox";
|
|
544
|
+
buttonCheckbox.checked = !!config.showAsButton;
|
|
545
|
+
buttonCheckbox.disabled = !config.label;
|
|
546
|
+
buttonCheckbox.setAttribute(
|
|
547
|
+
"aria-label",
|
|
548
|
+
config.label ? "Show this endpoint as a quick-switch button" : "Add a label first to enable button",
|
|
549
|
+
);
|
|
550
|
+
buttonCheckbox.title = config.label
|
|
551
|
+
? "Show this endpoint as a quick-switch button"
|
|
552
|
+
: "Add a label first to enable button";
|
|
553
|
+
buttonCheckbox.onchange = () => {
|
|
554
|
+
this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(config.endpoint, {
|
|
555
|
+
showAsButton: buttonCheckbox.checked,
|
|
556
|
+
});
|
|
557
|
+
this.tab.refreshEndpointButtons();
|
|
558
|
+
};
|
|
559
|
+
buttonCell.appendChild(buttonCheckbox);
|
|
560
|
+
addClass(buttonCell, "centerCell");
|
|
561
|
+
row.appendChild(buttonCell);
|
|
562
|
+
|
|
563
|
+
// Authentication column
|
|
564
|
+
const authCell = document.createElement("td");
|
|
565
|
+
const authButton = document.createElement("button");
|
|
566
|
+
authButton.type = "button";
|
|
567
|
+
addClass(authButton, "configureAuthButton");
|
|
568
|
+
if (config.authentication) {
|
|
569
|
+
authButton.textContent = "✓ Configured";
|
|
570
|
+
addClass(authButton, "authenticated");
|
|
571
|
+
} else {
|
|
572
|
+
authButton.textContent = "Configure";
|
|
573
|
+
}
|
|
574
|
+
authButton.onclick = () => this.showAuthenticationModal(config.endpoint);
|
|
575
|
+
authCell.appendChild(authButton);
|
|
576
|
+
addClass(authCell, "centerCell");
|
|
577
|
+
row.appendChild(authCell);
|
|
578
|
+
|
|
579
|
+
// Actions column
|
|
580
|
+
const actionsCell = document.createElement("td");
|
|
581
|
+
const deleteButton = document.createElement("button");
|
|
582
|
+
deleteButton.type = "button";
|
|
583
|
+
deleteButton.textContent = "Delete";
|
|
584
|
+
addClass(deleteButton, "deleteEndpointButton");
|
|
585
|
+
deleteButton.onclick = () => {
|
|
586
|
+
if (confirm(`Delete endpoint "${config.endpoint}"?`)) {
|
|
587
|
+
this.tab.yasgui.persistentConfig.deleteEndpointConfig(config.endpoint);
|
|
588
|
+
this.renderEndpointsList(container);
|
|
589
|
+
this.tab.refreshEndpointButtons();
|
|
590
|
+
}
|
|
591
|
+
};
|
|
592
|
+
actionsCell.appendChild(deleteButton);
|
|
593
|
+
addClass(actionsCell, "centerCell");
|
|
594
|
+
row.appendChild(actionsCell);
|
|
595
|
+
|
|
596
|
+
tbody.appendChild(row);
|
|
597
|
+
});
|
|
598
|
+
table.appendChild(tbody);
|
|
599
|
+
container.appendChild(table);
|
|
600
|
+
|
|
601
|
+
// Add endpoint form
|
|
602
|
+
const addForm = document.createElement("div");
|
|
603
|
+
addClass(addForm, "addEndpointForm");
|
|
604
|
+
|
|
605
|
+
const addFormTitle = document.createElement("div");
|
|
606
|
+
addFormTitle.textContent = "Add New Endpoint";
|
|
607
|
+
addClass(addFormTitle, "addFormTitle");
|
|
608
|
+
addForm.appendChild(addFormTitle);
|
|
609
|
+
|
|
610
|
+
const formInputs = document.createElement("div");
|
|
611
|
+
addClass(formInputs, "addFormInputs");
|
|
612
|
+
|
|
613
|
+
const endpointInput = document.createElement("input");
|
|
614
|
+
endpointInput.type = "url";
|
|
615
|
+
endpointInput.placeholder = "Endpoint URL (e.g., https://dbpedia.org/sparql)";
|
|
616
|
+
addClass(endpointInput, "addEndpointInput");
|
|
617
|
+
formInputs.appendChild(endpointInput);
|
|
618
|
+
|
|
619
|
+
const addButton = document.createElement("button");
|
|
620
|
+
addButton.type = "button";
|
|
621
|
+
addButton.textContent = "+ Add Endpoint";
|
|
622
|
+
addClass(addButton, "addEndpointButton");
|
|
623
|
+
addButton.onclick = () => {
|
|
624
|
+
const endpoint = endpointInput.value.trim();
|
|
625
|
+
|
|
626
|
+
if (!endpoint) {
|
|
627
|
+
alert("Please enter an endpoint URL.");
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// Validate URL format
|
|
632
|
+
// Check for supported protocol first
|
|
633
|
+
if (!/^https?:\/\//i.test(endpoint)) {
|
|
634
|
+
alert("Endpoint URL must start with http:// or https://");
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
try {
|
|
638
|
+
new URL(endpoint);
|
|
639
|
+
} catch (e) {
|
|
640
|
+
// Show the error message if available, otherwise a generic one
|
|
641
|
+
alert(e instanceof Error && e.message ? "Malformed URL: " + e.message : "Please enter a valid URL.");
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// Check if endpoint already exists
|
|
646
|
+
const existing = this.tab.yasgui.persistentConfig.getEndpointConfig(endpoint);
|
|
647
|
+
if (existing) {
|
|
648
|
+
alert("This endpoint is already in the list.");
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// Add the endpoint
|
|
653
|
+
this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(endpoint, {});
|
|
654
|
+
|
|
655
|
+
// Clear input
|
|
656
|
+
endpointInput.value = "";
|
|
657
|
+
|
|
658
|
+
// Refresh list
|
|
659
|
+
this.renderEndpointsList(container);
|
|
660
|
+
this.tab.refreshEndpointButtons();
|
|
661
|
+
};
|
|
662
|
+
formInputs.appendChild(addButton);
|
|
663
|
+
|
|
664
|
+
addForm.appendChild(formInputs);
|
|
665
|
+
container.appendChild(addForm);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
private showAuthenticationModal(endpoint: string) {
|
|
669
|
+
const config = this.tab.yasgui.persistentConfig.getEndpointConfig(endpoint);
|
|
670
|
+
const existingAuth = config?.authentication;
|
|
671
|
+
|
|
672
|
+
// Create modal overlay
|
|
673
|
+
const authModalOverlay = document.createElement("div");
|
|
674
|
+
addClass(authModalOverlay, "authModalOverlay");
|
|
675
|
+
authModalOverlay.onclick = () => authModalOverlay.remove();
|
|
676
|
+
|
|
677
|
+
// Create modal content
|
|
678
|
+
const authModal = document.createElement("div");
|
|
679
|
+
addClass(authModal, "authModal");
|
|
680
|
+
authModal.onclick = (e) => e.stopPropagation();
|
|
681
|
+
|
|
682
|
+
// Header
|
|
683
|
+
const header = document.createElement("div");
|
|
684
|
+
addClass(header, "authModalHeader");
|
|
685
|
+
const title = document.createElement("h3");
|
|
686
|
+
title.textContent = "Configure Authentication";
|
|
687
|
+
const subtitle = document.createElement("div");
|
|
688
|
+
subtitle.textContent = endpoint;
|
|
689
|
+
addClass(subtitle, "authModalSubtitle");
|
|
690
|
+
header.appendChild(title);
|
|
691
|
+
header.appendChild(subtitle);
|
|
692
|
+
authModal.appendChild(header);
|
|
693
|
+
|
|
694
|
+
// Body
|
|
695
|
+
const body = document.createElement("div");
|
|
696
|
+
addClass(body, "authModalBody");
|
|
697
|
+
|
|
698
|
+
// Auth type
|
|
699
|
+
const typeSection = document.createElement("div");
|
|
700
|
+
addClass(typeSection, "authModalSection");
|
|
701
|
+
const typeLabel = document.createElement("label");
|
|
702
|
+
typeLabel.textContent = "Authentication Type";
|
|
703
|
+
const typeSelect = document.createElement("select");
|
|
704
|
+
|
|
705
|
+
const basicOption = document.createElement("option");
|
|
706
|
+
basicOption.value = "basic";
|
|
707
|
+
basicOption.textContent = "HTTP Basic Authentication";
|
|
708
|
+
typeSelect.appendChild(basicOption);
|
|
709
|
+
|
|
710
|
+
const bearerOption = document.createElement("option");
|
|
711
|
+
bearerOption.value = "bearer";
|
|
712
|
+
bearerOption.textContent = "Bearer Token";
|
|
713
|
+
typeSelect.appendChild(bearerOption);
|
|
714
|
+
|
|
715
|
+
const apiKeyOption = document.createElement("option");
|
|
716
|
+
apiKeyOption.value = "apiKey";
|
|
717
|
+
apiKeyOption.textContent = "API Key (Custom Header)";
|
|
718
|
+
typeSelect.appendChild(apiKeyOption);
|
|
719
|
+
|
|
720
|
+
// Set the current auth type
|
|
721
|
+
if (existingAuth) {
|
|
722
|
+
typeSelect.value = existingAuth.type;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
typeSection.appendChild(typeLabel);
|
|
726
|
+
typeSection.appendChild(typeSelect);
|
|
727
|
+
body.appendChild(typeSection);
|
|
728
|
+
|
|
729
|
+
// Basic Auth Fields
|
|
730
|
+
const basicAuthFields = document.createElement("div");
|
|
731
|
+
basicAuthFields.id = "basicAuthFields";
|
|
732
|
+
addClass(basicAuthFields, "authFieldsContainer");
|
|
733
|
+
|
|
734
|
+
const usernameSection = document.createElement("div");
|
|
735
|
+
addClass(usernameSection, "authModalSection");
|
|
736
|
+
const usernameLabel = document.createElement("label");
|
|
737
|
+
usernameLabel.textContent = "Username";
|
|
738
|
+
const usernameInput = document.createElement("input");
|
|
739
|
+
usernameInput.type = "text";
|
|
740
|
+
usernameInput.placeholder = "Enter username";
|
|
741
|
+
usernameInput.value = existingAuth?.type === "basic" ? existingAuth.username : "";
|
|
742
|
+
usernameInput.autocomplete = "username";
|
|
743
|
+
usernameSection.appendChild(usernameLabel);
|
|
744
|
+
usernameSection.appendChild(usernameInput);
|
|
745
|
+
basicAuthFields.appendChild(usernameSection);
|
|
746
|
+
|
|
747
|
+
const passwordSection = document.createElement("div");
|
|
748
|
+
addClass(passwordSection, "authModalSection");
|
|
749
|
+
const passwordLabel = document.createElement("label");
|
|
750
|
+
passwordLabel.textContent = "Password";
|
|
751
|
+
const passwordInput = document.createElement("input");
|
|
752
|
+
passwordInput.type = "password";
|
|
753
|
+
passwordInput.placeholder = "Enter password";
|
|
754
|
+
passwordInput.value = existingAuth?.type === "basic" ? existingAuth.password : "";
|
|
755
|
+
passwordInput.autocomplete = "current-password";
|
|
756
|
+
passwordSection.appendChild(passwordLabel);
|
|
757
|
+
passwordSection.appendChild(passwordInput);
|
|
758
|
+
basicAuthFields.appendChild(passwordSection);
|
|
759
|
+
|
|
760
|
+
body.appendChild(basicAuthFields);
|
|
761
|
+
|
|
762
|
+
// Bearer Token Fields
|
|
763
|
+
const bearerAuthFields = document.createElement("div");
|
|
764
|
+
bearerAuthFields.id = "bearerAuthFields";
|
|
765
|
+
addClass(bearerAuthFields, "authFieldsContainer");
|
|
766
|
+
bearerAuthFields.style.display = "none";
|
|
767
|
+
|
|
768
|
+
const tokenSection = document.createElement("div");
|
|
769
|
+
addClass(tokenSection, "authModalSection");
|
|
770
|
+
const tokenLabel = document.createElement("label");
|
|
771
|
+
tokenLabel.textContent = "Bearer Token";
|
|
772
|
+
const tokenInput = document.createElement("input");
|
|
773
|
+
tokenInput.type = "password";
|
|
774
|
+
tokenInput.placeholder = "Enter bearer token";
|
|
775
|
+
tokenInput.autocomplete = "off";
|
|
776
|
+
tokenInput.value = existingAuth?.type === "bearer" ? existingAuth.token : "";
|
|
777
|
+
tokenSection.appendChild(tokenLabel);
|
|
778
|
+
tokenSection.appendChild(tokenInput);
|
|
779
|
+
bearerAuthFields.appendChild(tokenSection);
|
|
780
|
+
|
|
781
|
+
body.appendChild(bearerAuthFields);
|
|
782
|
+
|
|
783
|
+
// API Key Fields
|
|
784
|
+
const apiKeyAuthFields = document.createElement("div");
|
|
785
|
+
apiKeyAuthFields.id = "apiKeyAuthFields";
|
|
786
|
+
addClass(apiKeyAuthFields, "authFieldsContainer");
|
|
787
|
+
apiKeyAuthFields.style.display = "none";
|
|
788
|
+
|
|
789
|
+
const headerNameSection = document.createElement("div");
|
|
790
|
+
addClass(headerNameSection, "authModalSection");
|
|
791
|
+
const headerNameLabel = document.createElement("label");
|
|
792
|
+
headerNameLabel.textContent = "Header Name";
|
|
793
|
+
const headerNameInput = document.createElement("input");
|
|
794
|
+
headerNameInput.type = "text";
|
|
795
|
+
headerNameInput.placeholder = `e.g., ${DEFAULT_API_KEY_HEADER}`;
|
|
796
|
+
headerNameInput.value = existingAuth?.type === "apiKey" ? existingAuth.headerName : DEFAULT_API_KEY_HEADER;
|
|
797
|
+
headerNameSection.appendChild(headerNameLabel);
|
|
798
|
+
headerNameSection.appendChild(headerNameInput);
|
|
799
|
+
apiKeyAuthFields.appendChild(headerNameSection);
|
|
800
|
+
|
|
801
|
+
const apiKeySection = document.createElement("div");
|
|
802
|
+
addClass(apiKeySection, "authModalSection");
|
|
803
|
+
const apiKeyLabel = document.createElement("label");
|
|
804
|
+
apiKeyLabel.textContent = "API Key";
|
|
805
|
+
const apiKeyInput = document.createElement("input");
|
|
806
|
+
apiKeyInput.type = "password";
|
|
807
|
+
apiKeyInput.placeholder = "Enter API key";
|
|
808
|
+
apiKeyInput.autocomplete = "off";
|
|
809
|
+
apiKeyInput.value = existingAuth?.type === "apiKey" ? existingAuth.apiKey : "";
|
|
810
|
+
apiKeySection.appendChild(apiKeyLabel);
|
|
811
|
+
apiKeySection.appendChild(apiKeyInput);
|
|
812
|
+
apiKeyAuthFields.appendChild(apiKeySection);
|
|
813
|
+
|
|
814
|
+
body.appendChild(apiKeyAuthFields);
|
|
815
|
+
|
|
816
|
+
// Function to toggle fields based on auth type
|
|
817
|
+
const toggleAuthFields = () => {
|
|
818
|
+
const authType = typeSelect.value;
|
|
819
|
+
basicAuthFields.style.display = authType === "basic" ? "block" : "none";
|
|
820
|
+
bearerAuthFields.style.display = authType === "bearer" ? "block" : "none";
|
|
821
|
+
apiKeyAuthFields.style.display = authType === "apiKey" ? "block" : "none";
|
|
822
|
+
};
|
|
823
|
+
|
|
824
|
+
// Set initial visibility
|
|
825
|
+
toggleAuthFields();
|
|
826
|
+
|
|
827
|
+
// Update visibility when auth type changes
|
|
828
|
+
typeSelect.onchange = toggleAuthFields;
|
|
829
|
+
|
|
830
|
+
// Security notice
|
|
831
|
+
const securityNotice = document.createElement("div");
|
|
832
|
+
addClass(securityNotice, "authSecurityNotice");
|
|
833
|
+
securityNotice.innerHTML = `
|
|
834
|
+
<strong>⚠️ Security Notice:</strong>
|
|
835
|
+
<ul>
|
|
836
|
+
<li>Credentials are stored in browser localStorage</li>
|
|
837
|
+
<li>Only use with HTTPS endpoints</li>
|
|
838
|
+
<li>Be cautious when using on shared computers</li>
|
|
839
|
+
</ul>
|
|
840
|
+
`;
|
|
841
|
+
body.appendChild(securityNotice);
|
|
842
|
+
|
|
843
|
+
authModal.appendChild(body);
|
|
844
|
+
|
|
845
|
+
// Footer
|
|
846
|
+
const footer = document.createElement("div");
|
|
847
|
+
addClass(footer, "authModalFooter");
|
|
848
|
+
|
|
849
|
+
const removeButton = document.createElement("button");
|
|
850
|
+
removeButton.textContent = "Remove Authentication";
|
|
851
|
+
removeButton.type = "button";
|
|
852
|
+
addClass(removeButton, "authRemoveButton");
|
|
853
|
+
removeButton.onclick = () => {
|
|
854
|
+
this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(endpoint, {
|
|
855
|
+
authentication: undefined,
|
|
856
|
+
});
|
|
857
|
+
authModalOverlay.remove();
|
|
858
|
+
const endpointsList = this.modalContent.querySelector(".endpointsTable");
|
|
859
|
+
if (endpointsList) this.renderEndpointsList(endpointsList as HTMLElement);
|
|
860
|
+
};
|
|
861
|
+
if (!existingAuth) {
|
|
862
|
+
removeButton.disabled = true;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
const cancelButton = document.createElement("button");
|
|
866
|
+
cancelButton.textContent = "Cancel";
|
|
867
|
+
cancelButton.type = "button";
|
|
868
|
+
addClass(cancelButton, "authCancelButton");
|
|
869
|
+
cancelButton.onclick = () => authModalOverlay.remove();
|
|
870
|
+
|
|
871
|
+
const saveButton = document.createElement("button");
|
|
872
|
+
saveButton.textContent = "Save";
|
|
873
|
+
saveButton.type = "button";
|
|
874
|
+
addClass(saveButton, "authSaveButton");
|
|
875
|
+
saveButton.onclick = () => {
|
|
876
|
+
const authType = typeSelect.value;
|
|
877
|
+
|
|
878
|
+
if (authType === "basic") {
|
|
879
|
+
const username = usernameInput.value.trim();
|
|
880
|
+
const password = passwordInput.value;
|
|
881
|
+
|
|
882
|
+
if (username && password) {
|
|
883
|
+
this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(endpoint, {
|
|
884
|
+
authentication: {
|
|
885
|
+
type: "basic",
|
|
886
|
+
username,
|
|
887
|
+
password,
|
|
888
|
+
},
|
|
889
|
+
});
|
|
890
|
+
authModalOverlay.remove();
|
|
891
|
+
const endpointsList = this.modalContent.querySelector(".endpointsTable");
|
|
892
|
+
if (endpointsList) this.renderEndpointsList(endpointsList as HTMLElement);
|
|
893
|
+
} else {
|
|
894
|
+
alert("Please enter both username and password.");
|
|
895
|
+
}
|
|
896
|
+
} else if (authType === "bearer") {
|
|
897
|
+
const token = tokenInput.value.trim();
|
|
898
|
+
|
|
899
|
+
if (token) {
|
|
900
|
+
this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(endpoint, {
|
|
901
|
+
authentication: {
|
|
902
|
+
type: "bearer",
|
|
903
|
+
token,
|
|
904
|
+
},
|
|
905
|
+
});
|
|
906
|
+
authModalOverlay.remove();
|
|
907
|
+
const endpointsList = this.modalContent.querySelector(".endpointsTable");
|
|
908
|
+
if (endpointsList) this.renderEndpointsList(endpointsList as HTMLElement);
|
|
909
|
+
} else {
|
|
910
|
+
alert("Please enter a bearer token.");
|
|
911
|
+
}
|
|
912
|
+
} else if (authType === "apiKey") {
|
|
913
|
+
const headerName = headerNameInput.value.trim();
|
|
914
|
+
const apiKey = apiKeyInput.value.trim();
|
|
915
|
+
|
|
916
|
+
if (headerName && apiKey) {
|
|
917
|
+
this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(endpoint, {
|
|
918
|
+
authentication: {
|
|
919
|
+
type: "apiKey",
|
|
920
|
+
headerName,
|
|
921
|
+
apiKey,
|
|
922
|
+
},
|
|
923
|
+
});
|
|
924
|
+
authModalOverlay.remove();
|
|
925
|
+
const endpointsList = this.modalContent.querySelector(".endpointsTable");
|
|
926
|
+
if (endpointsList) this.renderEndpointsList(endpointsList as HTMLElement);
|
|
927
|
+
} else {
|
|
928
|
+
alert("Please enter both header name and API key.");
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
};
|
|
932
|
+
|
|
933
|
+
footer.appendChild(removeButton);
|
|
934
|
+
footer.appendChild(cancelButton);
|
|
935
|
+
footer.appendChild(saveButton);
|
|
936
|
+
authModal.appendChild(footer);
|
|
937
|
+
|
|
938
|
+
authModalOverlay.appendChild(authModal);
|
|
939
|
+
document.body.appendChild(authModalOverlay);
|
|
940
|
+
}
|
|
941
|
+
|
|
439
942
|
private drawRequestSettings(container: HTMLElement) {
|
|
440
943
|
// This is a simplified version - you can expand based on TabPanel.ts
|
|
441
944
|
const reqConfig = this.tab.getRequestConfig();
|
|
@@ -557,7 +1060,23 @@ export default class TabSettingsModal {
|
|
|
557
1060
|
}
|
|
558
1061
|
const snippetsBarCheckbox = document.getElementById("showSnippetsBar") as HTMLInputElement;
|
|
559
1062
|
if (snippetsBarCheckbox) {
|
|
560
|
-
|
|
1063
|
+
// Save globally to config and persistent storage
|
|
1064
|
+
this.tab.yasgui.config.showSnippetsBar = snippetsBarCheckbox.checked;
|
|
1065
|
+
this.tab.yasgui.persistentConfig.setShowSnippetsBar(snippetsBarCheckbox.checked);
|
|
1066
|
+
|
|
1067
|
+
// Apply to all tabs by updating each Yasqe instance's config and refreshing
|
|
1068
|
+
this.tab.yasgui.persistentConfig.getTabs().forEach((tabId: string) => {
|
|
1069
|
+
const tab = this.tab.yasgui.getTab(tabId);
|
|
1070
|
+
if (tab) {
|
|
1071
|
+
const tabYasqe = tab.getYasqe();
|
|
1072
|
+
if (tabYasqe) {
|
|
1073
|
+
// Update the individual Yasqe instance's config
|
|
1074
|
+
tabYasqe.config.showSnippetsBar = snippetsBarCheckbox.checked;
|
|
1075
|
+
// Refresh the snippets bar to reflect the change
|
|
1076
|
+
tabYasqe.refreshSnippetsBar();
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
});
|
|
561
1080
|
}
|
|
562
1081
|
yasqe.saveQuery();
|
|
563
1082
|
}
|
|
@@ -576,6 +1095,9 @@ export default class TabSettingsModal {
|
|
|
576
1095
|
this.tab.setRequestConfig(updates);
|
|
577
1096
|
}
|
|
578
1097
|
|
|
1098
|
+
// Note: Authentication is now handled per-endpoint in the Endpoints tab,
|
|
1099
|
+
// not per-tab anymore. No need to save it here.
|
|
1100
|
+
|
|
579
1101
|
// Refresh endpoint buttons to show any changes
|
|
580
1102
|
this.tab.refreshEndpointButtons();
|
|
581
1103
|
|
|
@@ -674,116 +1196,8 @@ export default class TabSettingsModal {
|
|
|
674
1196
|
this.tab.yasgui.persistentConfig.setPrefixes(deduplicated);
|
|
675
1197
|
}
|
|
676
1198
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
addClass(section, "settingsSection");
|
|
680
|
-
|
|
681
|
-
const label = document.createElement("label");
|
|
682
|
-
label.textContent = "Custom Endpoint Buttons";
|
|
683
|
-
addClass(label, "settingsLabel");
|
|
684
|
-
|
|
685
|
-
const help = document.createElement("div");
|
|
686
|
-
help.textContent = "Add custom endpoint buttons that will appear next to the endpoint textbox.";
|
|
687
|
-
addClass(help, "settingsHelp");
|
|
688
|
-
|
|
689
|
-
section.appendChild(label);
|
|
690
|
-
section.appendChild(help);
|
|
691
|
-
|
|
692
|
-
// List of existing buttons
|
|
693
|
-
const buttonsList = document.createElement("div");
|
|
694
|
-
addClass(buttonsList, "endpointButtonsList");
|
|
695
|
-
this.renderEndpointButtonsList(buttonsList);
|
|
696
|
-
section.appendChild(buttonsList);
|
|
697
|
-
|
|
698
|
-
// Form to add new button
|
|
699
|
-
const addForm = document.createElement("div");
|
|
700
|
-
addClass(addForm, "addEndpointButtonForm");
|
|
701
|
-
|
|
702
|
-
const labelInput = document.createElement("input");
|
|
703
|
-
labelInput.type = "text";
|
|
704
|
-
labelInput.placeholder = "Button label (e.g., DBpedia)";
|
|
705
|
-
addClass(labelInput, "endpointButtonLabelInput");
|
|
706
|
-
|
|
707
|
-
const endpointInput = document.createElement("input");
|
|
708
|
-
endpointInput.type = "url";
|
|
709
|
-
endpointInput.placeholder = "Endpoint URL (e.g., https://dbpedia.org/sparql)";
|
|
710
|
-
addClass(endpointInput, "endpointButtonEndpointInput");
|
|
711
|
-
|
|
712
|
-
const addButton = document.createElement("button");
|
|
713
|
-
addButton.textContent = "+ Add Button";
|
|
714
|
-
addClass(addButton, "addEndpointButton");
|
|
715
|
-
addButton.type = "button";
|
|
716
|
-
addButton.onclick = () => {
|
|
717
|
-
const labelValue = labelInput.value.trim();
|
|
718
|
-
const endpointValue = endpointInput.value.trim();
|
|
719
|
-
|
|
720
|
-
if (!labelValue || !endpointValue) {
|
|
721
|
-
alert("Please enter both a label and an endpoint URL.");
|
|
722
|
-
return;
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
// Add to persistent config
|
|
726
|
-
const currentButtons = this.tab.yasgui.persistentConfig.getCustomEndpointButtons();
|
|
727
|
-
currentButtons.push({ label: labelValue, endpoint: endpointValue });
|
|
728
|
-
this.tab.yasgui.persistentConfig.setCustomEndpointButtons(currentButtons);
|
|
729
|
-
|
|
730
|
-
// Clear inputs
|
|
731
|
-
labelInput.value = "";
|
|
732
|
-
endpointInput.value = "";
|
|
733
|
-
|
|
734
|
-
// Refresh list
|
|
735
|
-
this.renderEndpointButtonsList(buttonsList);
|
|
736
|
-
};
|
|
737
|
-
|
|
738
|
-
addForm.appendChild(labelInput);
|
|
739
|
-
addForm.appendChild(endpointInput);
|
|
740
|
-
addForm.appendChild(addButton);
|
|
741
|
-
section.appendChild(addForm);
|
|
742
|
-
|
|
743
|
-
container.appendChild(section);
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
private renderEndpointButtonsList(container: HTMLElement) {
|
|
747
|
-
container.innerHTML = "";
|
|
748
|
-
const customButtons = this.tab.yasgui.persistentConfig.getCustomEndpointButtons();
|
|
749
|
-
|
|
750
|
-
if (customButtons.length === 0) {
|
|
751
|
-
const emptyMsg = document.createElement("div");
|
|
752
|
-
emptyMsg.textContent = "No custom buttons yet. Add one below.";
|
|
753
|
-
addClass(emptyMsg, "emptyMessage");
|
|
754
|
-
container.appendChild(emptyMsg);
|
|
755
|
-
return;
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
customButtons.forEach((btn, index) => {
|
|
759
|
-
const item = document.createElement("div");
|
|
760
|
-
addClass(item, "endpointButtonItem");
|
|
761
|
-
|
|
762
|
-
const labelSpan = document.createElement("span");
|
|
763
|
-
labelSpan.textContent = `${btn.label}`;
|
|
764
|
-
addClass(labelSpan, "buttonLabel");
|
|
765
|
-
|
|
766
|
-
const endpointSpan = document.createElement("span");
|
|
767
|
-
endpointSpan.textContent = btn.endpoint;
|
|
768
|
-
addClass(endpointSpan, "buttonEndpoint");
|
|
769
|
-
|
|
770
|
-
const removeBtn = document.createElement("button");
|
|
771
|
-
removeBtn.textContent = "×";
|
|
772
|
-
addClass(removeBtn, "removeButton");
|
|
773
|
-
removeBtn.type = "button";
|
|
774
|
-
removeBtn.onclick = () => {
|
|
775
|
-
const currentButtons = this.tab.yasgui.persistentConfig.getCustomEndpointButtons();
|
|
776
|
-
currentButtons.splice(index, 1);
|
|
777
|
-
this.tab.yasgui.persistentConfig.setCustomEndpointButtons(currentButtons);
|
|
778
|
-
this.renderEndpointButtonsList(container);
|
|
779
|
-
};
|
|
780
|
-
|
|
781
|
-
item.appendChild(labelSpan);
|
|
782
|
-
item.appendChild(endpointSpan);
|
|
783
|
-
item.appendChild(removeBtn);
|
|
784
|
-
container.appendChild(item);
|
|
785
|
-
});
|
|
786
|
-
}
|
|
1199
|
+
// Old endpoint buttons functionality has been merged into drawEndpointsSettings
|
|
1200
|
+
// Keeping this for reference if needed for backwards compatibility
|
|
787
1201
|
|
|
788
1202
|
private getThemeToggleIcon(): string {
|
|
789
1203
|
const currentTheme = this.tab.yasgui.getTheme();
|