@matdata/yasgui 5.3.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/build/ts/src/ConfigExportImport.js +1 -0
- package/build/ts/src/ConfigExportImport.js.map +1 -1
- package/build/ts/src/PersistentConfig.d.ts +7 -1
- package/build/ts/src/PersistentConfig.js +32 -0
- package/build/ts/src/PersistentConfig.js.map +1 -1
- package/build/ts/src/Tab.d.ts +2 -1
- package/build/ts/src/Tab.js +46 -11
- package/build/ts/src/Tab.js.map +1 -1
- package/build/ts/src/TabSettingsModal.d.ts +6 -2
- package/build/ts/src/TabSettingsModal.js +519 -114
- package/build/ts/src/TabSettingsModal.js.map +1 -1
- package/build/ts/src/index.d.ts +10 -0
- package/build/ts/src/index.js.map +1 -1
- package/build/ts/src/version.d.ts +1 -0
- package/build/ts/src/version.js +2 -0
- package/build/ts/src/version.js.map +1 -0
- package/build/yasgui.min.css +1 -1
- package/build/yasgui.min.css.map +3 -3
- package/build/yasgui.min.js +143 -122
- package/build/yasgui.min.js.map +4 -4
- package/package.json +1 -1
- package/src/ConfigExportImport.ts +1 -0
- package/src/PersistentConfig.ts +44 -2
- package/src/Tab.ts +76 -18
- package/src/TabSettingsModal.scss +565 -14
- package/src/TabSettingsModal.ts +676 -143
- package/src/index.ts +11 -0
- package/src/tab.scss +3 -4
- package/src/themes.scss +1 -1
- package/src/version.ts +3 -0
package/src/TabSettingsModal.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { addClass, removeClass } from "@matdata/yasgui-utils";
|
|
|
2
2
|
import "./TabSettingsModal.scss";
|
|
3
3
|
import Tab from "./Tab";
|
|
4
4
|
import * as ConfigExportImport from "./ConfigExportImport";
|
|
5
|
+
import { VERSION } from "./version";
|
|
5
6
|
|
|
6
7
|
// Theme toggle icons
|
|
7
8
|
const MOON_ICON = `<svg viewBox="0 0 24 24" fill="currentColor">
|
|
@@ -110,44 +111,63 @@ export default class TabSettingsModal {
|
|
|
110
111
|
|
|
111
112
|
this.modalContent.appendChild(header);
|
|
112
113
|
|
|
113
|
-
// Body with
|
|
114
|
+
// Body with sidebar navigation
|
|
114
115
|
const body = document.createElement("div");
|
|
115
116
|
addClass(body, "modalBody");
|
|
116
117
|
|
|
117
|
-
|
|
118
|
-
|
|
118
|
+
// Sidebar navigation
|
|
119
|
+
const sidebar = document.createElement("div");
|
|
120
|
+
addClass(sidebar, "modalSidebar");
|
|
119
121
|
|
|
120
122
|
const requestTab = document.createElement("button");
|
|
121
123
|
requestTab.textContent = "Request";
|
|
122
|
-
addClass(requestTab, "
|
|
124
|
+
addClass(requestTab, "modalNavButton", "active");
|
|
123
125
|
requestTab.onclick = () => this.switchTab("request");
|
|
124
126
|
|
|
127
|
+
const endpointsTab = document.createElement("button");
|
|
128
|
+
endpointsTab.textContent = "SPARQL Endpoints";
|
|
129
|
+
addClass(endpointsTab, "modalNavButton");
|
|
130
|
+
endpointsTab.onclick = () => this.switchTab("endpoints");
|
|
131
|
+
|
|
125
132
|
const prefixTab = document.createElement("button");
|
|
126
133
|
prefixTab.textContent = "Prefixes";
|
|
127
|
-
addClass(prefixTab, "
|
|
134
|
+
addClass(prefixTab, "modalNavButton");
|
|
128
135
|
prefixTab.onclick = () => this.switchTab("prefix");
|
|
129
136
|
|
|
130
137
|
const editorTab = document.createElement("button");
|
|
131
138
|
editorTab.textContent = "Editor";
|
|
132
|
-
addClass(editorTab, "
|
|
139
|
+
addClass(editorTab, "modalNavButton");
|
|
133
140
|
editorTab.onclick = () => this.switchTab("editor");
|
|
134
141
|
|
|
135
|
-
const endpointsTab = document.createElement("button");
|
|
136
|
-
endpointsTab.textContent = "Endpoint Buttons";
|
|
137
|
-
addClass(endpointsTab, "modalTabButton");
|
|
138
|
-
endpointsTab.onclick = () => this.switchTab("endpoints");
|
|
139
|
-
|
|
140
142
|
const importExportTab = document.createElement("button");
|
|
141
143
|
importExportTab.textContent = "Import/Export";
|
|
142
|
-
addClass(importExportTab, "
|
|
144
|
+
addClass(importExportTab, "modalNavButton");
|
|
143
145
|
importExportTab.onclick = () => this.switchTab("importexport");
|
|
144
146
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
147
|
+
const shortcutsTab = document.createElement("button");
|
|
148
|
+
shortcutsTab.textContent = "Keyboard Shortcuts";
|
|
149
|
+
addClass(shortcutsTab, "modalNavButton");
|
|
150
|
+
shortcutsTab.onclick = () => this.switchTab("shortcuts");
|
|
151
|
+
|
|
152
|
+
const aboutTab = document.createElement("button");
|
|
153
|
+
aboutTab.textContent = "About";
|
|
154
|
+
addClass(aboutTab, "modalNavButton");
|
|
155
|
+
aboutTab.onclick = () => this.switchTab("about");
|
|
156
|
+
|
|
157
|
+
sidebar.appendChild(requestTab);
|
|
158
|
+
sidebar.appendChild(endpointsTab);
|
|
159
|
+
sidebar.appendChild(prefixTab);
|
|
160
|
+
sidebar.appendChild(editorTab);
|
|
161
|
+
sidebar.appendChild(importExportTab);
|
|
162
|
+
sidebar.appendChild(shortcutsTab);
|
|
163
|
+
sidebar.appendChild(aboutTab);
|
|
164
|
+
|
|
165
|
+
// Content area container
|
|
166
|
+
const contentArea = document.createElement("div");
|
|
167
|
+
addClass(contentArea, "modalContentArea");
|
|
168
|
+
|
|
169
|
+
body.appendChild(sidebar);
|
|
170
|
+
body.appendChild(contentArea);
|
|
151
171
|
|
|
152
172
|
// Tab content containers
|
|
153
173
|
const requestContent = document.createElement("div");
|
|
@@ -155,6 +175,11 @@ export default class TabSettingsModal {
|
|
|
155
175
|
requestContent.id = "request-content";
|
|
156
176
|
this.drawRequestSettings(requestContent);
|
|
157
177
|
|
|
178
|
+
const endpointsContent = document.createElement("div");
|
|
179
|
+
addClass(endpointsContent, "modalTabContent");
|
|
180
|
+
endpointsContent.id = "endpoints-content";
|
|
181
|
+
this.drawEndpointsSettings(endpointsContent);
|
|
182
|
+
|
|
158
183
|
const prefixContent = document.createElement("div");
|
|
159
184
|
addClass(prefixContent, "modalTabContent");
|
|
160
185
|
prefixContent.id = "prefix-content";
|
|
@@ -165,21 +190,28 @@ export default class TabSettingsModal {
|
|
|
165
190
|
editorContent.id = "editor-content";
|
|
166
191
|
this.drawEditorSettings(editorContent);
|
|
167
192
|
|
|
168
|
-
const endpointsContent = document.createElement("div");
|
|
169
|
-
addClass(endpointsContent, "modalTabContent");
|
|
170
|
-
endpointsContent.id = "endpoints-content";
|
|
171
|
-
this.drawEndpointButtonsSettings(endpointsContent);
|
|
172
|
-
|
|
173
193
|
const importExportContent = document.createElement("div");
|
|
174
194
|
addClass(importExportContent, "modalTabContent");
|
|
175
195
|
importExportContent.id = "importexport-content";
|
|
176
196
|
this.drawImportExportSettings(importExportContent);
|
|
177
197
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
198
|
+
const shortcutsContent = document.createElement("div");
|
|
199
|
+
addClass(shortcutsContent, "modalTabContent");
|
|
200
|
+
shortcutsContent.id = "shortcuts-content";
|
|
201
|
+
this.drawKeyboardShortcuts(shortcutsContent);
|
|
202
|
+
|
|
203
|
+
const aboutContent = document.createElement("div");
|
|
204
|
+
addClass(aboutContent, "modalTabContent");
|
|
205
|
+
aboutContent.id = "about-content";
|
|
206
|
+
this.drawAboutSettings(aboutContent);
|
|
207
|
+
|
|
208
|
+
contentArea.appendChild(requestContent);
|
|
209
|
+
contentArea.appendChild(endpointsContent);
|
|
210
|
+
contentArea.appendChild(prefixContent);
|
|
211
|
+
contentArea.appendChild(editorContent);
|
|
212
|
+
contentArea.appendChild(importExportContent);
|
|
213
|
+
contentArea.appendChild(shortcutsContent);
|
|
214
|
+
contentArea.appendChild(aboutContent);
|
|
183
215
|
|
|
184
216
|
this.modalContent.appendChild(body);
|
|
185
217
|
|
|
@@ -207,16 +239,18 @@ export default class TabSettingsModal {
|
|
|
207
239
|
}
|
|
208
240
|
|
|
209
241
|
private switchTab(tabName: string) {
|
|
210
|
-
const buttons = this.modalContent.querySelectorAll(".
|
|
242
|
+
const buttons = this.modalContent.querySelectorAll(".modalNavButton");
|
|
211
243
|
const contents = this.modalContent.querySelectorAll(".modalTabContent");
|
|
212
244
|
|
|
213
245
|
buttons.forEach((btn, index) => {
|
|
214
246
|
if (
|
|
215
247
|
(tabName === "request" && index === 0) ||
|
|
216
|
-
(tabName === "
|
|
217
|
-
(tabName === "
|
|
218
|
-
(tabName === "
|
|
219
|
-
(tabName === "importexport" && index === 4)
|
|
248
|
+
(tabName === "endpoints" && index === 1) ||
|
|
249
|
+
(tabName === "prefix" && index === 2) ||
|
|
250
|
+
(tabName === "editor" && index === 3) ||
|
|
251
|
+
(tabName === "importexport" && index === 4) ||
|
|
252
|
+
(tabName === "shortcuts" && index === 5) ||
|
|
253
|
+
(tabName === "about" && index === 6)
|
|
220
254
|
) {
|
|
221
255
|
addClass(btn as HTMLElement, "active");
|
|
222
256
|
} else {
|
|
@@ -378,6 +412,398 @@ export default class TabSettingsModal {
|
|
|
378
412
|
constructValidationSection.appendChild(constructValidationCheckboxContainer);
|
|
379
413
|
constructValidationSection.appendChild(constructValidationHelp);
|
|
380
414
|
container.appendChild(constructValidationSection);
|
|
415
|
+
|
|
416
|
+
// Code Snippets Bar Visibility Section
|
|
417
|
+
const snippetsBarSection = document.createElement("div");
|
|
418
|
+
addClass(snippetsBarSection, "settingsSection");
|
|
419
|
+
|
|
420
|
+
const snippetsBarCheckboxContainer = document.createElement("div");
|
|
421
|
+
addClass(snippetsBarCheckboxContainer, "checkboxContainer");
|
|
422
|
+
|
|
423
|
+
const snippetsBarCheckbox = document.createElement("input");
|
|
424
|
+
snippetsBarCheckbox.type = "checkbox";
|
|
425
|
+
snippetsBarCheckbox.id = "showSnippetsBar";
|
|
426
|
+
snippetsBarCheckbox.checked = yasqe.getSnippetsBarVisible();
|
|
427
|
+
|
|
428
|
+
const snippetsBarLabel = document.createElement("label");
|
|
429
|
+
snippetsBarLabel.htmlFor = "showSnippetsBar";
|
|
430
|
+
snippetsBarLabel.textContent = "Show code snippets bar";
|
|
431
|
+
|
|
432
|
+
const snippetsBarHelp = document.createElement("div");
|
|
433
|
+
snippetsBarHelp.textContent =
|
|
434
|
+
"Display the code snippets bar above the editor for quick insertion of common SPARQL patterns.";
|
|
435
|
+
addClass(snippetsBarHelp, "settingsHelp");
|
|
436
|
+
snippetsBarHelp.style.marginTop = "5px";
|
|
437
|
+
|
|
438
|
+
snippetsBarCheckboxContainer.appendChild(snippetsBarCheckbox);
|
|
439
|
+
snippetsBarCheckboxContainer.appendChild(snippetsBarLabel);
|
|
440
|
+
|
|
441
|
+
snippetsBarSection.appendChild(snippetsBarCheckboxContainer);
|
|
442
|
+
snippetsBarSection.appendChild(snippetsBarHelp);
|
|
443
|
+
container.appendChild(snippetsBarSection);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
private drawEndpointsSettings(container: HTMLElement) {
|
|
447
|
+
const section = document.createElement("div");
|
|
448
|
+
addClass(section, "settingsSection");
|
|
449
|
+
|
|
450
|
+
const label = document.createElement("label");
|
|
451
|
+
label.textContent = "SPARQL Endpoints";
|
|
452
|
+
addClass(label, "settingsLabel");
|
|
453
|
+
|
|
454
|
+
const help = document.createElement("div");
|
|
455
|
+
help.textContent =
|
|
456
|
+
"Manage your SPARQL endpoints. Each endpoint can have its own authentication and be displayed as a quick-switch button.";
|
|
457
|
+
addClass(help, "settingsHelp");
|
|
458
|
+
|
|
459
|
+
section.appendChild(label);
|
|
460
|
+
section.appendChild(help);
|
|
461
|
+
|
|
462
|
+
// List of endpoints
|
|
463
|
+
const endpointsList = document.createElement("div");
|
|
464
|
+
addClass(endpointsList, "endpointsTable");
|
|
465
|
+
this.renderEndpointsList(endpointsList);
|
|
466
|
+
section.appendChild(endpointsList);
|
|
467
|
+
|
|
468
|
+
container.appendChild(section);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
private renderEndpointsList(container: HTMLElement) {
|
|
472
|
+
container.innerHTML = "";
|
|
473
|
+
const configs = this.tab.yasgui.persistentConfig.getEndpointConfigs();
|
|
474
|
+
|
|
475
|
+
// Create table (even if empty, we'll show add form)
|
|
476
|
+
const table = document.createElement("table");
|
|
477
|
+
addClass(table, "endpointsTableElement");
|
|
478
|
+
|
|
479
|
+
// Header
|
|
480
|
+
const thead = document.createElement("thead");
|
|
481
|
+
const headerRow = document.createElement("tr");
|
|
482
|
+
const headers = ["Endpoint", "Label", "Button", "Authentication", "Actions"];
|
|
483
|
+
headers.forEach((h) => {
|
|
484
|
+
const th = document.createElement("th");
|
|
485
|
+
th.textContent = h;
|
|
486
|
+
headerRow.appendChild(th);
|
|
487
|
+
});
|
|
488
|
+
thead.appendChild(headerRow);
|
|
489
|
+
table.appendChild(thead);
|
|
490
|
+
|
|
491
|
+
// Body
|
|
492
|
+
const tbody = document.createElement("tbody");
|
|
493
|
+
|
|
494
|
+
if (configs.length === 0) {
|
|
495
|
+
// Show empty message in table
|
|
496
|
+
const emptyRow = document.createElement("tr");
|
|
497
|
+
const emptyCell = document.createElement("td");
|
|
498
|
+
emptyCell.colSpan = 5;
|
|
499
|
+
emptyCell.textContent = "No endpoints yet. Add one below or access an endpoint to have it automatically tracked.";
|
|
500
|
+
addClass(emptyCell, "emptyMessage");
|
|
501
|
+
emptyCell.style.textAlign = "center";
|
|
502
|
+
emptyCell.style.padding = "20px";
|
|
503
|
+
emptyRow.appendChild(emptyCell);
|
|
504
|
+
tbody.appendChild(emptyRow);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
configs.forEach((config, index) => {
|
|
508
|
+
const row = document.createElement("tr");
|
|
509
|
+
|
|
510
|
+
// Endpoint column
|
|
511
|
+
const endpointCell = document.createElement("td");
|
|
512
|
+
endpointCell.textContent = config.endpoint;
|
|
513
|
+
endpointCell.title = config.endpoint;
|
|
514
|
+
addClass(endpointCell, "endpointCell");
|
|
515
|
+
row.appendChild(endpointCell);
|
|
516
|
+
|
|
517
|
+
// Label column (editable)
|
|
518
|
+
const labelCell = document.createElement("td");
|
|
519
|
+
const labelInput = document.createElement("input");
|
|
520
|
+
labelInput.type = "text";
|
|
521
|
+
labelInput.value = config.label || "";
|
|
522
|
+
labelInput.placeholder = "Optional label";
|
|
523
|
+
addClass(labelInput, "endpointLabelInput");
|
|
524
|
+
labelInput.onchange = () => {
|
|
525
|
+
this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(config.endpoint, {
|
|
526
|
+
label: labelInput.value.trim() || undefined,
|
|
527
|
+
});
|
|
528
|
+
this.renderEndpointsList(container);
|
|
529
|
+
this.tab.refreshEndpointButtons();
|
|
530
|
+
};
|
|
531
|
+
labelCell.appendChild(labelInput);
|
|
532
|
+
row.appendChild(labelCell);
|
|
533
|
+
|
|
534
|
+
// Show as Button checkbox (requires label)
|
|
535
|
+
const buttonCell = document.createElement("td");
|
|
536
|
+
const buttonCheckbox = document.createElement("input");
|
|
537
|
+
buttonCheckbox.type = "checkbox";
|
|
538
|
+
buttonCheckbox.checked = !!config.showAsButton;
|
|
539
|
+
buttonCheckbox.disabled = !config.label;
|
|
540
|
+
buttonCheckbox.setAttribute(
|
|
541
|
+
"aria-label",
|
|
542
|
+
config.label ? "Show this endpoint as a quick-switch button" : "Add a label first to enable button",
|
|
543
|
+
);
|
|
544
|
+
buttonCheckbox.title = config.label
|
|
545
|
+
? "Show this endpoint as a quick-switch button"
|
|
546
|
+
: "Add a label first to enable button";
|
|
547
|
+
buttonCheckbox.onchange = () => {
|
|
548
|
+
this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(config.endpoint, {
|
|
549
|
+
showAsButton: buttonCheckbox.checked,
|
|
550
|
+
});
|
|
551
|
+
this.tab.refreshEndpointButtons();
|
|
552
|
+
};
|
|
553
|
+
buttonCell.appendChild(buttonCheckbox);
|
|
554
|
+
addClass(buttonCell, "centerCell");
|
|
555
|
+
row.appendChild(buttonCell);
|
|
556
|
+
|
|
557
|
+
// Authentication column
|
|
558
|
+
const authCell = document.createElement("td");
|
|
559
|
+
const authButton = document.createElement("button");
|
|
560
|
+
authButton.type = "button";
|
|
561
|
+
addClass(authButton, "configureAuthButton");
|
|
562
|
+
if (config.authentication) {
|
|
563
|
+
authButton.textContent = "✓ Configured";
|
|
564
|
+
addClass(authButton, "authenticated");
|
|
565
|
+
} else {
|
|
566
|
+
authButton.textContent = "Configure";
|
|
567
|
+
}
|
|
568
|
+
authButton.onclick = () => this.showAuthenticationModal(config.endpoint);
|
|
569
|
+
authCell.appendChild(authButton);
|
|
570
|
+
addClass(authCell, "centerCell");
|
|
571
|
+
row.appendChild(authCell);
|
|
572
|
+
|
|
573
|
+
// Actions column
|
|
574
|
+
const actionsCell = document.createElement("td");
|
|
575
|
+
const deleteButton = document.createElement("button");
|
|
576
|
+
deleteButton.type = "button";
|
|
577
|
+
deleteButton.textContent = "Delete";
|
|
578
|
+
addClass(deleteButton, "deleteEndpointButton");
|
|
579
|
+
deleteButton.onclick = () => {
|
|
580
|
+
if (confirm(`Delete endpoint "${config.endpoint}"?`)) {
|
|
581
|
+
this.tab.yasgui.persistentConfig.deleteEndpointConfig(config.endpoint);
|
|
582
|
+
this.renderEndpointsList(container);
|
|
583
|
+
this.tab.refreshEndpointButtons();
|
|
584
|
+
}
|
|
585
|
+
};
|
|
586
|
+
actionsCell.appendChild(deleteButton);
|
|
587
|
+
addClass(actionsCell, "centerCell");
|
|
588
|
+
row.appendChild(actionsCell);
|
|
589
|
+
|
|
590
|
+
tbody.appendChild(row);
|
|
591
|
+
});
|
|
592
|
+
table.appendChild(tbody);
|
|
593
|
+
container.appendChild(table);
|
|
594
|
+
|
|
595
|
+
// Add endpoint form
|
|
596
|
+
const addForm = document.createElement("div");
|
|
597
|
+
addClass(addForm, "addEndpointForm");
|
|
598
|
+
|
|
599
|
+
const addFormTitle = document.createElement("div");
|
|
600
|
+
addFormTitle.textContent = "Add New Endpoint";
|
|
601
|
+
addClass(addFormTitle, "addFormTitle");
|
|
602
|
+
addForm.appendChild(addFormTitle);
|
|
603
|
+
|
|
604
|
+
const formInputs = document.createElement("div");
|
|
605
|
+
addClass(formInputs, "addFormInputs");
|
|
606
|
+
|
|
607
|
+
const endpointInput = document.createElement("input");
|
|
608
|
+
endpointInput.type = "url";
|
|
609
|
+
endpointInput.placeholder = "Endpoint URL (e.g., https://dbpedia.org/sparql)";
|
|
610
|
+
addClass(endpointInput, "addEndpointInput");
|
|
611
|
+
formInputs.appendChild(endpointInput);
|
|
612
|
+
|
|
613
|
+
const addButton = document.createElement("button");
|
|
614
|
+
addButton.type = "button";
|
|
615
|
+
addButton.textContent = "+ Add Endpoint";
|
|
616
|
+
addClass(addButton, "addEndpointButton");
|
|
617
|
+
addButton.onclick = () => {
|
|
618
|
+
const endpoint = endpointInput.value.trim();
|
|
619
|
+
|
|
620
|
+
if (!endpoint) {
|
|
621
|
+
alert("Please enter an endpoint URL.");
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// Validate URL format
|
|
626
|
+
// Check for supported protocol first
|
|
627
|
+
if (!/^https?:\/\//i.test(endpoint)) {
|
|
628
|
+
alert("Endpoint URL must start with http:// or https://");
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
try {
|
|
632
|
+
new URL(endpoint);
|
|
633
|
+
} catch (e) {
|
|
634
|
+
// Show the error message if available, otherwise a generic one
|
|
635
|
+
alert(e instanceof Error && e.message ? "Malformed URL: " + e.message : "Please enter a valid URL.");
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// Check if endpoint already exists
|
|
640
|
+
const existing = this.tab.yasgui.persistentConfig.getEndpointConfig(endpoint);
|
|
641
|
+
if (existing) {
|
|
642
|
+
alert("This endpoint is already in the list.");
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// Add the endpoint
|
|
647
|
+
this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(endpoint, {});
|
|
648
|
+
|
|
649
|
+
// Clear input
|
|
650
|
+
endpointInput.value = "";
|
|
651
|
+
|
|
652
|
+
// Refresh list
|
|
653
|
+
this.renderEndpointsList(container);
|
|
654
|
+
this.tab.refreshEndpointButtons();
|
|
655
|
+
};
|
|
656
|
+
formInputs.appendChild(addButton);
|
|
657
|
+
|
|
658
|
+
addForm.appendChild(formInputs);
|
|
659
|
+
container.appendChild(addForm);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
private showAuthenticationModal(endpoint: string) {
|
|
663
|
+
const config = this.tab.yasgui.persistentConfig.getEndpointConfig(endpoint);
|
|
664
|
+
const existingAuth = config?.authentication;
|
|
665
|
+
|
|
666
|
+
// Create modal overlay
|
|
667
|
+
const authModalOverlay = document.createElement("div");
|
|
668
|
+
addClass(authModalOverlay, "authModalOverlay");
|
|
669
|
+
authModalOverlay.onclick = () => authModalOverlay.remove();
|
|
670
|
+
|
|
671
|
+
// Create modal content
|
|
672
|
+
const authModal = document.createElement("div");
|
|
673
|
+
addClass(authModal, "authModal");
|
|
674
|
+
authModal.onclick = (e) => e.stopPropagation();
|
|
675
|
+
|
|
676
|
+
// Header
|
|
677
|
+
const header = document.createElement("div");
|
|
678
|
+
addClass(header, "authModalHeader");
|
|
679
|
+
const title = document.createElement("h3");
|
|
680
|
+
title.textContent = "Configure Authentication";
|
|
681
|
+
const subtitle = document.createElement("div");
|
|
682
|
+
subtitle.textContent = endpoint;
|
|
683
|
+
addClass(subtitle, "authModalSubtitle");
|
|
684
|
+
header.appendChild(title);
|
|
685
|
+
header.appendChild(subtitle);
|
|
686
|
+
authModal.appendChild(header);
|
|
687
|
+
|
|
688
|
+
// Body
|
|
689
|
+
const body = document.createElement("div");
|
|
690
|
+
addClass(body, "authModalBody");
|
|
691
|
+
|
|
692
|
+
// Auth type (for now only basic, but designed for future)
|
|
693
|
+
const typeSection = document.createElement("div");
|
|
694
|
+
addClass(typeSection, "authModalSection");
|
|
695
|
+
const typeLabel = document.createElement("label");
|
|
696
|
+
typeLabel.textContent = "Authentication Type";
|
|
697
|
+
const typeSelect = document.createElement("select");
|
|
698
|
+
const basicOption = document.createElement("option");
|
|
699
|
+
basicOption.value = "basic";
|
|
700
|
+
basicOption.textContent = "HTTP Basic Authentication";
|
|
701
|
+
typeSelect.appendChild(basicOption);
|
|
702
|
+
// Future: Add OAuth, Bearer Token, etc.
|
|
703
|
+
typeSection.appendChild(typeLabel);
|
|
704
|
+
typeSection.appendChild(typeSelect);
|
|
705
|
+
body.appendChild(typeSection);
|
|
706
|
+
|
|
707
|
+
// Username
|
|
708
|
+
const usernameSection = document.createElement("div");
|
|
709
|
+
addClass(usernameSection, "authModalSection");
|
|
710
|
+
const usernameLabel = document.createElement("label");
|
|
711
|
+
usernameLabel.textContent = "Username";
|
|
712
|
+
const usernameInput = document.createElement("input");
|
|
713
|
+
usernameInput.type = "text";
|
|
714
|
+
usernameInput.placeholder = "Enter username";
|
|
715
|
+
usernameInput.value = existingAuth?.username || "";
|
|
716
|
+
usernameInput.autocomplete = "username";
|
|
717
|
+
usernameSection.appendChild(usernameLabel);
|
|
718
|
+
usernameSection.appendChild(usernameInput);
|
|
719
|
+
body.appendChild(usernameSection);
|
|
720
|
+
|
|
721
|
+
// Password
|
|
722
|
+
const passwordSection = document.createElement("div");
|
|
723
|
+
addClass(passwordSection, "authModalSection");
|
|
724
|
+
const passwordLabel = document.createElement("label");
|
|
725
|
+
passwordLabel.textContent = "Password";
|
|
726
|
+
const passwordInput = document.createElement("input");
|
|
727
|
+
passwordInput.type = "password";
|
|
728
|
+
passwordInput.placeholder = "Enter password";
|
|
729
|
+
passwordInput.value = existingAuth?.password || "";
|
|
730
|
+
passwordInput.autocomplete = "current-password";
|
|
731
|
+
passwordSection.appendChild(passwordLabel);
|
|
732
|
+
passwordSection.appendChild(passwordInput);
|
|
733
|
+
body.appendChild(passwordSection);
|
|
734
|
+
|
|
735
|
+
// Security notice
|
|
736
|
+
const securityNotice = document.createElement("div");
|
|
737
|
+
addClass(securityNotice, "authSecurityNotice");
|
|
738
|
+
securityNotice.innerHTML = `
|
|
739
|
+
<strong>⚠️ Security Notice:</strong>
|
|
740
|
+
<ul>
|
|
741
|
+
<li>Credentials are stored in browser localStorage</li>
|
|
742
|
+
<li>Only use with HTTPS endpoints</li>
|
|
743
|
+
<li>Be cautious when using on shared computers</li>
|
|
744
|
+
</ul>
|
|
745
|
+
`;
|
|
746
|
+
body.appendChild(securityNotice);
|
|
747
|
+
|
|
748
|
+
authModal.appendChild(body);
|
|
749
|
+
|
|
750
|
+
// Footer
|
|
751
|
+
const footer = document.createElement("div");
|
|
752
|
+
addClass(footer, "authModalFooter");
|
|
753
|
+
|
|
754
|
+
const removeButton = document.createElement("button");
|
|
755
|
+
removeButton.textContent = "Remove Authentication";
|
|
756
|
+
removeButton.type = "button";
|
|
757
|
+
addClass(removeButton, "authRemoveButton");
|
|
758
|
+
removeButton.onclick = () => {
|
|
759
|
+
this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(endpoint, {
|
|
760
|
+
authentication: undefined,
|
|
761
|
+
});
|
|
762
|
+
authModalOverlay.remove();
|
|
763
|
+
const endpointsList = this.modalContent.querySelector(".endpointsTable");
|
|
764
|
+
if (endpointsList) this.renderEndpointsList(endpointsList as HTMLElement);
|
|
765
|
+
};
|
|
766
|
+
if (!existingAuth) {
|
|
767
|
+
removeButton.disabled = true;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
const cancelButton = document.createElement("button");
|
|
771
|
+
cancelButton.textContent = "Cancel";
|
|
772
|
+
cancelButton.type = "button";
|
|
773
|
+
addClass(cancelButton, "authCancelButton");
|
|
774
|
+
cancelButton.onclick = () => authModalOverlay.remove();
|
|
775
|
+
|
|
776
|
+
const saveButton = document.createElement("button");
|
|
777
|
+
saveButton.textContent = "Save";
|
|
778
|
+
saveButton.type = "button";
|
|
779
|
+
addClass(saveButton, "authSaveButton");
|
|
780
|
+
saveButton.onclick = () => {
|
|
781
|
+
const username = usernameInput.value.trim();
|
|
782
|
+
const password = passwordInput.value;
|
|
783
|
+
|
|
784
|
+
if (username && password) {
|
|
785
|
+
this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(endpoint, {
|
|
786
|
+
authentication: {
|
|
787
|
+
type: "basic",
|
|
788
|
+
username,
|
|
789
|
+
password,
|
|
790
|
+
},
|
|
791
|
+
});
|
|
792
|
+
authModalOverlay.remove();
|
|
793
|
+
const endpointsList = this.modalContent.querySelector(".endpointsTable");
|
|
794
|
+
if (endpointsList) this.renderEndpointsList(endpointsList as HTMLElement);
|
|
795
|
+
} else {
|
|
796
|
+
alert("Please enter both username and password.");
|
|
797
|
+
}
|
|
798
|
+
};
|
|
799
|
+
|
|
800
|
+
footer.appendChild(removeButton);
|
|
801
|
+
footer.appendChild(cancelButton);
|
|
802
|
+
footer.appendChild(saveButton);
|
|
803
|
+
authModal.appendChild(footer);
|
|
804
|
+
|
|
805
|
+
authModalOverlay.appendChild(authModal);
|
|
806
|
+
document.body.appendChild(authModalOverlay);
|
|
381
807
|
}
|
|
382
808
|
|
|
383
809
|
private drawRequestSettings(container: HTMLElement) {
|
|
@@ -499,6 +925,10 @@ export default class TabSettingsModal {
|
|
|
499
925
|
if (constructValidationCheckbox) {
|
|
500
926
|
yasqe.setCheckConstructVariables(constructValidationCheckbox.checked);
|
|
501
927
|
}
|
|
928
|
+
const snippetsBarCheckbox = document.getElementById("showSnippetsBar") as HTMLInputElement;
|
|
929
|
+
if (snippetsBarCheckbox) {
|
|
930
|
+
yasqe.setSnippetsBarVisible(snippetsBarCheckbox.checked);
|
|
931
|
+
}
|
|
502
932
|
yasqe.saveQuery();
|
|
503
933
|
}
|
|
504
934
|
|
|
@@ -516,6 +946,9 @@ export default class TabSettingsModal {
|
|
|
516
946
|
this.tab.setRequestConfig(updates);
|
|
517
947
|
}
|
|
518
948
|
|
|
949
|
+
// Note: Authentication is now handled per-endpoint in the Endpoints tab,
|
|
950
|
+
// not per-tab anymore. No need to save it here.
|
|
951
|
+
|
|
519
952
|
// Refresh endpoint buttons to show any changes
|
|
520
953
|
this.tab.refreshEndpointButtons();
|
|
521
954
|
|
|
@@ -614,116 +1047,8 @@ export default class TabSettingsModal {
|
|
|
614
1047
|
this.tab.yasgui.persistentConfig.setPrefixes(deduplicated);
|
|
615
1048
|
}
|
|
616
1049
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
addClass(section, "settingsSection");
|
|
620
|
-
|
|
621
|
-
const label = document.createElement("label");
|
|
622
|
-
label.textContent = "Custom Endpoint Buttons";
|
|
623
|
-
addClass(label, "settingsLabel");
|
|
624
|
-
|
|
625
|
-
const help = document.createElement("div");
|
|
626
|
-
help.textContent = "Add custom endpoint buttons that will appear next to the endpoint textbox.";
|
|
627
|
-
addClass(help, "settingsHelp");
|
|
628
|
-
|
|
629
|
-
section.appendChild(label);
|
|
630
|
-
section.appendChild(help);
|
|
631
|
-
|
|
632
|
-
// List of existing buttons
|
|
633
|
-
const buttonsList = document.createElement("div");
|
|
634
|
-
addClass(buttonsList, "endpointButtonsList");
|
|
635
|
-
this.renderEndpointButtonsList(buttonsList);
|
|
636
|
-
section.appendChild(buttonsList);
|
|
637
|
-
|
|
638
|
-
// Form to add new button
|
|
639
|
-
const addForm = document.createElement("div");
|
|
640
|
-
addClass(addForm, "addEndpointButtonForm");
|
|
641
|
-
|
|
642
|
-
const labelInput = document.createElement("input");
|
|
643
|
-
labelInput.type = "text";
|
|
644
|
-
labelInput.placeholder = "Button label (e.g., DBpedia)";
|
|
645
|
-
addClass(labelInput, "endpointButtonLabelInput");
|
|
646
|
-
|
|
647
|
-
const endpointInput = document.createElement("input");
|
|
648
|
-
endpointInput.type = "url";
|
|
649
|
-
endpointInput.placeholder = "Endpoint URL (e.g., https://dbpedia.org/sparql)";
|
|
650
|
-
addClass(endpointInput, "endpointButtonEndpointInput");
|
|
651
|
-
|
|
652
|
-
const addButton = document.createElement("button");
|
|
653
|
-
addButton.textContent = "+ Add Button";
|
|
654
|
-
addClass(addButton, "addEndpointButton");
|
|
655
|
-
addButton.type = "button";
|
|
656
|
-
addButton.onclick = () => {
|
|
657
|
-
const labelValue = labelInput.value.trim();
|
|
658
|
-
const endpointValue = endpointInput.value.trim();
|
|
659
|
-
|
|
660
|
-
if (!labelValue || !endpointValue) {
|
|
661
|
-
alert("Please enter both a label and an endpoint URL.");
|
|
662
|
-
return;
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
// Add to persistent config
|
|
666
|
-
const currentButtons = this.tab.yasgui.persistentConfig.getCustomEndpointButtons();
|
|
667
|
-
currentButtons.push({ label: labelValue, endpoint: endpointValue });
|
|
668
|
-
this.tab.yasgui.persistentConfig.setCustomEndpointButtons(currentButtons);
|
|
669
|
-
|
|
670
|
-
// Clear inputs
|
|
671
|
-
labelInput.value = "";
|
|
672
|
-
endpointInput.value = "";
|
|
673
|
-
|
|
674
|
-
// Refresh list
|
|
675
|
-
this.renderEndpointButtonsList(buttonsList);
|
|
676
|
-
};
|
|
677
|
-
|
|
678
|
-
addForm.appendChild(labelInput);
|
|
679
|
-
addForm.appendChild(endpointInput);
|
|
680
|
-
addForm.appendChild(addButton);
|
|
681
|
-
section.appendChild(addForm);
|
|
682
|
-
|
|
683
|
-
container.appendChild(section);
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
private renderEndpointButtonsList(container: HTMLElement) {
|
|
687
|
-
container.innerHTML = "";
|
|
688
|
-
const customButtons = this.tab.yasgui.persistentConfig.getCustomEndpointButtons();
|
|
689
|
-
|
|
690
|
-
if (customButtons.length === 0) {
|
|
691
|
-
const emptyMsg = document.createElement("div");
|
|
692
|
-
emptyMsg.textContent = "No custom buttons yet. Add one below.";
|
|
693
|
-
addClass(emptyMsg, "emptyMessage");
|
|
694
|
-
container.appendChild(emptyMsg);
|
|
695
|
-
return;
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
customButtons.forEach((btn, index) => {
|
|
699
|
-
const item = document.createElement("div");
|
|
700
|
-
addClass(item, "endpointButtonItem");
|
|
701
|
-
|
|
702
|
-
const labelSpan = document.createElement("span");
|
|
703
|
-
labelSpan.textContent = `${btn.label}`;
|
|
704
|
-
addClass(labelSpan, "buttonLabel");
|
|
705
|
-
|
|
706
|
-
const endpointSpan = document.createElement("span");
|
|
707
|
-
endpointSpan.textContent = btn.endpoint;
|
|
708
|
-
addClass(endpointSpan, "buttonEndpoint");
|
|
709
|
-
|
|
710
|
-
const removeBtn = document.createElement("button");
|
|
711
|
-
removeBtn.textContent = "×";
|
|
712
|
-
addClass(removeBtn, "removeButton");
|
|
713
|
-
removeBtn.type = "button";
|
|
714
|
-
removeBtn.onclick = () => {
|
|
715
|
-
const currentButtons = this.tab.yasgui.persistentConfig.getCustomEndpointButtons();
|
|
716
|
-
currentButtons.splice(index, 1);
|
|
717
|
-
this.tab.yasgui.persistentConfig.setCustomEndpointButtons(currentButtons);
|
|
718
|
-
this.renderEndpointButtonsList(container);
|
|
719
|
-
};
|
|
720
|
-
|
|
721
|
-
item.appendChild(labelSpan);
|
|
722
|
-
item.appendChild(endpointSpan);
|
|
723
|
-
item.appendChild(removeBtn);
|
|
724
|
-
container.appendChild(item);
|
|
725
|
-
});
|
|
726
|
-
}
|
|
1050
|
+
// Old endpoint buttons functionality has been merged into drawEndpointsSettings
|
|
1051
|
+
// Keeping this for reference if needed for backwards compatibility
|
|
727
1052
|
|
|
728
1053
|
private getThemeToggleIcon(): string {
|
|
729
1054
|
const currentTheme = this.tab.yasgui.getTheme();
|
|
@@ -885,6 +1210,112 @@ export default class TabSettingsModal {
|
|
|
885
1210
|
container.appendChild(importSection);
|
|
886
1211
|
}
|
|
887
1212
|
|
|
1213
|
+
private drawKeyboardShortcuts(container: HTMLElement) {
|
|
1214
|
+
const shortcutsData = [
|
|
1215
|
+
{
|
|
1216
|
+
category: "Query Editor (YASQE)",
|
|
1217
|
+
shortcuts: [
|
|
1218
|
+
{ keys: ["Ctrl+Enter", "Cmd+Enter"], description: "Execute query" },
|
|
1219
|
+
{ keys: ["Ctrl+Space", "Cmd+Space"], description: "Trigger autocomplete" },
|
|
1220
|
+
{ keys: ["Ctrl+S", "Cmd+S"], description: "Save query to local storage" },
|
|
1221
|
+
{ keys: ["Ctrl+Shift+F", "Cmd+Shift+F"], description: "Format query" },
|
|
1222
|
+
{ keys: ["Ctrl+/", "Cmd+/"], description: "Toggle comment on selected lines" },
|
|
1223
|
+
{ keys: ["Ctrl+Shift+D", "Cmd+Shift+D"], description: "Duplicate current line" },
|
|
1224
|
+
{ keys: ["Ctrl+Shift+K", "Cmd+Shift+K"], description: "Delete current line" },
|
|
1225
|
+
{ keys: ["Esc"], description: "Remove focus from editor" },
|
|
1226
|
+
{ keys: ["Ctrl+Click"], description: "Explore URI connections (on URI)" },
|
|
1227
|
+
],
|
|
1228
|
+
},
|
|
1229
|
+
{
|
|
1230
|
+
category: "Fullscreen",
|
|
1231
|
+
shortcuts: [
|
|
1232
|
+
{ keys: ["F11"], description: "Toggle YASQE (editor) fullscreen" },
|
|
1233
|
+
{ keys: ["F10"], description: "Toggle YASR (results) fullscreen" },
|
|
1234
|
+
{ keys: ["F9"], description: "Switch between YASQE and YASR fullscreen" },
|
|
1235
|
+
{ keys: ["Esc"], description: "Exit fullscreen mode" },
|
|
1236
|
+
],
|
|
1237
|
+
},
|
|
1238
|
+
];
|
|
1239
|
+
|
|
1240
|
+
shortcutsData.forEach((section) => {
|
|
1241
|
+
const sectionEl = document.createElement("div");
|
|
1242
|
+
addClass(sectionEl, "shortcutsSection");
|
|
1243
|
+
|
|
1244
|
+
const categoryLabel = document.createElement("h3");
|
|
1245
|
+
categoryLabel.textContent = section.category;
|
|
1246
|
+
addClass(categoryLabel, "shortcutsCategory");
|
|
1247
|
+
sectionEl.appendChild(categoryLabel);
|
|
1248
|
+
|
|
1249
|
+
const table = document.createElement("table");
|
|
1250
|
+
addClass(table, "shortcutsTable");
|
|
1251
|
+
table.setAttribute("role", "table");
|
|
1252
|
+
table.setAttribute("aria-label", `${section.category} keyboard shortcuts`);
|
|
1253
|
+
|
|
1254
|
+
// Add table caption for screen readers
|
|
1255
|
+
const caption = document.createElement("caption");
|
|
1256
|
+
caption.textContent = `${section.category} keyboard shortcuts`;
|
|
1257
|
+
caption.style.position = "absolute";
|
|
1258
|
+
caption.style.left = "-10000px";
|
|
1259
|
+
caption.style.width = "1px";
|
|
1260
|
+
caption.style.height = "1px";
|
|
1261
|
+
caption.style.overflow = "hidden";
|
|
1262
|
+
table.appendChild(caption);
|
|
1263
|
+
|
|
1264
|
+
// Add thead with proper headers
|
|
1265
|
+
const thead = document.createElement("thead");
|
|
1266
|
+
const headerRow = document.createElement("tr");
|
|
1267
|
+
|
|
1268
|
+
const keysHeader = document.createElement("th");
|
|
1269
|
+
keysHeader.textContent = "Keys";
|
|
1270
|
+
keysHeader.setAttribute("scope", "col");
|
|
1271
|
+
addClass(keysHeader, "shortcutsKeysHeader");
|
|
1272
|
+
headerRow.appendChild(keysHeader);
|
|
1273
|
+
|
|
1274
|
+
const descHeader = document.createElement("th");
|
|
1275
|
+
descHeader.textContent = "Description";
|
|
1276
|
+
descHeader.setAttribute("scope", "col");
|
|
1277
|
+
addClass(descHeader, "shortcutsDescHeader");
|
|
1278
|
+
headerRow.appendChild(descHeader);
|
|
1279
|
+
|
|
1280
|
+
thead.appendChild(headerRow);
|
|
1281
|
+
table.appendChild(thead);
|
|
1282
|
+
|
|
1283
|
+
// Add tbody
|
|
1284
|
+
const tbody = document.createElement("tbody");
|
|
1285
|
+
|
|
1286
|
+
section.shortcuts.forEach((shortcut) => {
|
|
1287
|
+
const row = document.createElement("tr");
|
|
1288
|
+
|
|
1289
|
+
const keysCell = document.createElement("td");
|
|
1290
|
+
addClass(keysCell, "shortcutsKeys");
|
|
1291
|
+
shortcut.keys.forEach((key, index) => {
|
|
1292
|
+
if (index > 0) {
|
|
1293
|
+
const separator = document.createElement("span");
|
|
1294
|
+
separator.textContent = " / ";
|
|
1295
|
+
addClass(separator, "shortcutsSeparator");
|
|
1296
|
+
keysCell.appendChild(separator);
|
|
1297
|
+
}
|
|
1298
|
+
const kbd = document.createElement("kbd");
|
|
1299
|
+
kbd.textContent = key;
|
|
1300
|
+
keysCell.appendChild(kbd);
|
|
1301
|
+
});
|
|
1302
|
+
row.appendChild(keysCell);
|
|
1303
|
+
|
|
1304
|
+
const descCell = document.createElement("td");
|
|
1305
|
+
addClass(descCell, "shortcutsDescription");
|
|
1306
|
+
descCell.textContent = shortcut.description;
|
|
1307
|
+
row.appendChild(descCell);
|
|
1308
|
+
|
|
1309
|
+
tbody.appendChild(row);
|
|
1310
|
+
});
|
|
1311
|
+
|
|
1312
|
+
table.appendChild(tbody);
|
|
1313
|
+
|
|
1314
|
+
sectionEl.appendChild(table);
|
|
1315
|
+
container.appendChild(sectionEl);
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
|
|
888
1319
|
private async importConfiguration(turtleContent: string) {
|
|
889
1320
|
try {
|
|
890
1321
|
const parsedConfig = ConfigExportImport.parseFromTurtle(turtleContent);
|
|
@@ -940,6 +1371,108 @@ export default class TabSettingsModal {
|
|
|
940
1371
|
}, 5000);
|
|
941
1372
|
}
|
|
942
1373
|
|
|
1374
|
+
private drawAboutSettings(container: HTMLElement) {
|
|
1375
|
+
// About Section
|
|
1376
|
+
const aboutSection = document.createElement("div");
|
|
1377
|
+
addClass(aboutSection, "settingsSection", "aboutSection");
|
|
1378
|
+
|
|
1379
|
+
// YASGUI Title and Version
|
|
1380
|
+
const titleContainer = document.createElement("div");
|
|
1381
|
+
addClass(titleContainer, "aboutTitle");
|
|
1382
|
+
|
|
1383
|
+
const title = document.createElement("h3");
|
|
1384
|
+
title.textContent = "YASGUI";
|
|
1385
|
+
addClass(title, "aboutMainTitle");
|
|
1386
|
+
|
|
1387
|
+
const versionBadge = document.createElement("span");
|
|
1388
|
+
versionBadge.textContent = `v${VERSION}`;
|
|
1389
|
+
addClass(versionBadge, "versionBadge");
|
|
1390
|
+
|
|
1391
|
+
titleContainer.appendChild(title);
|
|
1392
|
+
titleContainer.appendChild(versionBadge);
|
|
1393
|
+
aboutSection.appendChild(titleContainer);
|
|
1394
|
+
|
|
1395
|
+
// Subtitle
|
|
1396
|
+
const subtitle = document.createElement("p");
|
|
1397
|
+
subtitle.textContent = "Yet Another SPARQL GUI";
|
|
1398
|
+
addClass(subtitle, "aboutSubtitle");
|
|
1399
|
+
aboutSection.appendChild(subtitle);
|
|
1400
|
+
|
|
1401
|
+
// Links Section
|
|
1402
|
+
const linksSection = document.createElement("div");
|
|
1403
|
+
addClass(linksSection, "aboutLinks");
|
|
1404
|
+
|
|
1405
|
+
// Documentation Link
|
|
1406
|
+
const docsLink = this.createAboutLink(
|
|
1407
|
+
"📚 Documentation",
|
|
1408
|
+
"https://yasgui-doc.matdata.eu/docs/",
|
|
1409
|
+
"View the complete documentation and guides",
|
|
1410
|
+
);
|
|
1411
|
+
linksSection.appendChild(docsLink);
|
|
1412
|
+
|
|
1413
|
+
// Release Notes Link
|
|
1414
|
+
const releasesLink = this.createAboutLink(
|
|
1415
|
+
"📝 Release Notes",
|
|
1416
|
+
"https://github.com/Matdata-eu/Yasgui/releases",
|
|
1417
|
+
"See what's new in the latest releases",
|
|
1418
|
+
);
|
|
1419
|
+
linksSection.appendChild(releasesLink);
|
|
1420
|
+
|
|
1421
|
+
// Issues/Support Link
|
|
1422
|
+
const issuesLink = this.createAboutLink(
|
|
1423
|
+
"🐛 Report Issues & Get Support",
|
|
1424
|
+
"https://github.com/Matdata-eu/Yasgui/issues",
|
|
1425
|
+
"Report bugs, request features, or ask for help",
|
|
1426
|
+
);
|
|
1427
|
+
linksSection.appendChild(issuesLink);
|
|
1428
|
+
|
|
1429
|
+
aboutSection.appendChild(linksSection);
|
|
1430
|
+
|
|
1431
|
+
// Footer info
|
|
1432
|
+
const footerInfo = document.createElement("div");
|
|
1433
|
+
addClass(footerInfo, "aboutFooter");
|
|
1434
|
+
|
|
1435
|
+
const paragraph1 = document.createElement("p");
|
|
1436
|
+
paragraph1.textContent = "YASGUI is an open-source project maintained by ";
|
|
1437
|
+
const matdataLink = document.createElement("a");
|
|
1438
|
+
matdataLink.href = "https://matdata.eu";
|
|
1439
|
+
matdataLink.target = "_blank";
|
|
1440
|
+
matdataLink.rel = "noopener noreferrer";
|
|
1441
|
+
matdataLink.textContent = "Matdata";
|
|
1442
|
+
paragraph1.appendChild(matdataLink);
|
|
1443
|
+
paragraph1.appendChild(document.createTextNode("."));
|
|
1444
|
+
|
|
1445
|
+
const paragraph2 = document.createElement("p");
|
|
1446
|
+
paragraph2.textContent = "Licensed under the MIT License.";
|
|
1447
|
+
|
|
1448
|
+
footerInfo.appendChild(paragraph1);
|
|
1449
|
+
footerInfo.appendChild(paragraph2);
|
|
1450
|
+
aboutSection.appendChild(footerInfo);
|
|
1451
|
+
|
|
1452
|
+
container.appendChild(aboutSection);
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
private createAboutLink(label: string, url: string, description: string): HTMLElement {
|
|
1456
|
+
const linkContainer = document.createElement("div");
|
|
1457
|
+
addClass(linkContainer, "aboutLinkItem");
|
|
1458
|
+
|
|
1459
|
+
const link = document.createElement("a");
|
|
1460
|
+
link.href = url;
|
|
1461
|
+
link.target = "_blank";
|
|
1462
|
+
link.rel = "noopener noreferrer";
|
|
1463
|
+
link.textContent = label;
|
|
1464
|
+
addClass(link, "aboutLink");
|
|
1465
|
+
|
|
1466
|
+
const desc = document.createElement("p");
|
|
1467
|
+
desc.textContent = description;
|
|
1468
|
+
addClass(desc, "aboutLinkDescription");
|
|
1469
|
+
|
|
1470
|
+
linkContainer.appendChild(link);
|
|
1471
|
+
linkContainer.appendChild(desc);
|
|
1472
|
+
|
|
1473
|
+
return linkContainer;
|
|
1474
|
+
}
|
|
1475
|
+
|
|
943
1476
|
public destroy() {
|
|
944
1477
|
if (this.modalOverlay && this.modalOverlay.parentNode) {
|
|
945
1478
|
this.modalOverlay.parentNode.removeChild(this.modalOverlay);
|