@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.
@@ -10,6 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import { addClass, removeClass } from "@matdata/yasgui-utils";
11
11
  import "./TabSettingsModal.scss";
12
12
  import * as ConfigExportImport from "./ConfigExportImport";
13
+ import { VERSION } from "./version";
13
14
  const MOON_ICON = `<svg viewBox="0 0 24 24" fill="currentColor">
14
15
  <path d="M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9 9-4.03 9-9c0-.46-.04-.92-.1-1.36-.98 1.37-2.58 2.26-4.4 2.26-2.98 0-5.4-2.42-5.4-5.4 0-1.81.89-3.42 2.26-4.4-.44-.06-.9-.1-1.36-.1z"/>
15
16
  </svg>`;
@@ -89,38 +90,55 @@ export default class TabSettingsModal {
89
90
  this.modalContent.appendChild(header);
90
91
  const body = document.createElement("div");
91
92
  addClass(body, "modalBody");
92
- const tabsContainer = document.createElement("div");
93
- addClass(tabsContainer, "modalTabs");
93
+ const sidebar = document.createElement("div");
94
+ addClass(sidebar, "modalSidebar");
94
95
  const requestTab = document.createElement("button");
95
96
  requestTab.textContent = "Request";
96
- addClass(requestTab, "modalTabButton", "active");
97
+ addClass(requestTab, "modalNavButton", "active");
97
98
  requestTab.onclick = () => this.switchTab("request");
99
+ const endpointsTab = document.createElement("button");
100
+ endpointsTab.textContent = "SPARQL Endpoints";
101
+ addClass(endpointsTab, "modalNavButton");
102
+ endpointsTab.onclick = () => this.switchTab("endpoints");
98
103
  const prefixTab = document.createElement("button");
99
104
  prefixTab.textContent = "Prefixes";
100
- addClass(prefixTab, "modalTabButton");
105
+ addClass(prefixTab, "modalNavButton");
101
106
  prefixTab.onclick = () => this.switchTab("prefix");
102
107
  const editorTab = document.createElement("button");
103
108
  editorTab.textContent = "Editor";
104
- addClass(editorTab, "modalTabButton");
109
+ addClass(editorTab, "modalNavButton");
105
110
  editorTab.onclick = () => this.switchTab("editor");
106
- const endpointsTab = document.createElement("button");
107
- endpointsTab.textContent = "Endpoint Buttons";
108
- addClass(endpointsTab, "modalTabButton");
109
- endpointsTab.onclick = () => this.switchTab("endpoints");
110
111
  const importExportTab = document.createElement("button");
111
112
  importExportTab.textContent = "Import/Export";
112
- addClass(importExportTab, "modalTabButton");
113
+ addClass(importExportTab, "modalNavButton");
113
114
  importExportTab.onclick = () => this.switchTab("importexport");
114
- tabsContainer.appendChild(requestTab);
115
- tabsContainer.appendChild(prefixTab);
116
- tabsContainer.appendChild(editorTab);
117
- tabsContainer.appendChild(endpointsTab);
118
- tabsContainer.appendChild(importExportTab);
119
- body.appendChild(tabsContainer);
115
+ const shortcutsTab = document.createElement("button");
116
+ shortcutsTab.textContent = "Keyboard Shortcuts";
117
+ addClass(shortcutsTab, "modalNavButton");
118
+ shortcutsTab.onclick = () => this.switchTab("shortcuts");
119
+ const aboutTab = document.createElement("button");
120
+ aboutTab.textContent = "About";
121
+ addClass(aboutTab, "modalNavButton");
122
+ aboutTab.onclick = () => this.switchTab("about");
123
+ sidebar.appendChild(requestTab);
124
+ sidebar.appendChild(endpointsTab);
125
+ sidebar.appendChild(prefixTab);
126
+ sidebar.appendChild(editorTab);
127
+ sidebar.appendChild(importExportTab);
128
+ sidebar.appendChild(shortcutsTab);
129
+ sidebar.appendChild(aboutTab);
130
+ const contentArea = document.createElement("div");
131
+ addClass(contentArea, "modalContentArea");
132
+ body.appendChild(sidebar);
133
+ body.appendChild(contentArea);
120
134
  const requestContent = document.createElement("div");
121
135
  addClass(requestContent, "modalTabContent", "active");
122
136
  requestContent.id = "request-content";
123
137
  this.drawRequestSettings(requestContent);
138
+ const endpointsContent = document.createElement("div");
139
+ addClass(endpointsContent, "modalTabContent");
140
+ endpointsContent.id = "endpoints-content";
141
+ this.drawEndpointsSettings(endpointsContent);
124
142
  const prefixContent = document.createElement("div");
125
143
  addClass(prefixContent, "modalTabContent");
126
144
  prefixContent.id = "prefix-content";
@@ -129,19 +147,25 @@ export default class TabSettingsModal {
129
147
  addClass(editorContent, "modalTabContent");
130
148
  editorContent.id = "editor-content";
131
149
  this.drawEditorSettings(editorContent);
132
- const endpointsContent = document.createElement("div");
133
- addClass(endpointsContent, "modalTabContent");
134
- endpointsContent.id = "endpoints-content";
135
- this.drawEndpointButtonsSettings(endpointsContent);
136
150
  const importExportContent = document.createElement("div");
137
151
  addClass(importExportContent, "modalTabContent");
138
152
  importExportContent.id = "importexport-content";
139
153
  this.drawImportExportSettings(importExportContent);
140
- body.appendChild(requestContent);
141
- body.appendChild(prefixContent);
142
- body.appendChild(editorContent);
143
- body.appendChild(endpointsContent);
144
- body.appendChild(importExportContent);
154
+ const shortcutsContent = document.createElement("div");
155
+ addClass(shortcutsContent, "modalTabContent");
156
+ shortcutsContent.id = "shortcuts-content";
157
+ this.drawKeyboardShortcuts(shortcutsContent);
158
+ const aboutContent = document.createElement("div");
159
+ addClass(aboutContent, "modalTabContent");
160
+ aboutContent.id = "about-content";
161
+ this.drawAboutSettings(aboutContent);
162
+ contentArea.appendChild(requestContent);
163
+ contentArea.appendChild(endpointsContent);
164
+ contentArea.appendChild(prefixContent);
165
+ contentArea.appendChild(editorContent);
166
+ contentArea.appendChild(importExportContent);
167
+ contentArea.appendChild(shortcutsContent);
168
+ contentArea.appendChild(aboutContent);
145
169
  this.modalContent.appendChild(body);
146
170
  const footer = document.createElement("div");
147
171
  addClass(footer, "modalFooter");
@@ -160,14 +184,16 @@ export default class TabSettingsModal {
160
184
  document.body.appendChild(this.modalOverlay);
161
185
  }
162
186
  switchTab(tabName) {
163
- const buttons = this.modalContent.querySelectorAll(".modalTabButton");
187
+ const buttons = this.modalContent.querySelectorAll(".modalNavButton");
164
188
  const contents = this.modalContent.querySelectorAll(".modalTabContent");
165
189
  buttons.forEach((btn, index) => {
166
190
  if ((tabName === "request" && index === 0) ||
167
- (tabName === "prefix" && index === 1) ||
168
- (tabName === "editor" && index === 2) ||
169
- (tabName === "endpoints" && index === 3) ||
170
- (tabName === "importexport" && index === 4)) {
191
+ (tabName === "endpoints" && index === 1) ||
192
+ (tabName === "prefix" && index === 2) ||
193
+ (tabName === "editor" && index === 3) ||
194
+ (tabName === "importexport" && index === 4) ||
195
+ (tabName === "shortcuts" && index === 5) ||
196
+ (tabName === "about" && index === 6)) {
171
197
  addClass(btn, "active");
172
198
  }
173
199
  else {
@@ -293,6 +319,317 @@ export default class TabSettingsModal {
293
319
  constructValidationSection.appendChild(constructValidationCheckboxContainer);
294
320
  constructValidationSection.appendChild(constructValidationHelp);
295
321
  container.appendChild(constructValidationSection);
322
+ const snippetsBarSection = document.createElement("div");
323
+ addClass(snippetsBarSection, "settingsSection");
324
+ const snippetsBarCheckboxContainer = document.createElement("div");
325
+ addClass(snippetsBarCheckboxContainer, "checkboxContainer");
326
+ const snippetsBarCheckbox = document.createElement("input");
327
+ snippetsBarCheckbox.type = "checkbox";
328
+ snippetsBarCheckbox.id = "showSnippetsBar";
329
+ snippetsBarCheckbox.checked = yasqe.getSnippetsBarVisible();
330
+ const snippetsBarLabel = document.createElement("label");
331
+ snippetsBarLabel.htmlFor = "showSnippetsBar";
332
+ snippetsBarLabel.textContent = "Show code snippets bar";
333
+ const snippetsBarHelp = document.createElement("div");
334
+ snippetsBarHelp.textContent =
335
+ "Display the code snippets bar above the editor for quick insertion of common SPARQL patterns.";
336
+ addClass(snippetsBarHelp, "settingsHelp");
337
+ snippetsBarHelp.style.marginTop = "5px";
338
+ snippetsBarCheckboxContainer.appendChild(snippetsBarCheckbox);
339
+ snippetsBarCheckboxContainer.appendChild(snippetsBarLabel);
340
+ snippetsBarSection.appendChild(snippetsBarCheckboxContainer);
341
+ snippetsBarSection.appendChild(snippetsBarHelp);
342
+ container.appendChild(snippetsBarSection);
343
+ }
344
+ drawEndpointsSettings(container) {
345
+ const section = document.createElement("div");
346
+ addClass(section, "settingsSection");
347
+ const label = document.createElement("label");
348
+ label.textContent = "SPARQL Endpoints";
349
+ addClass(label, "settingsLabel");
350
+ const help = document.createElement("div");
351
+ help.textContent =
352
+ "Manage your SPARQL endpoints. Each endpoint can have its own authentication and be displayed as a quick-switch button.";
353
+ addClass(help, "settingsHelp");
354
+ section.appendChild(label);
355
+ section.appendChild(help);
356
+ const endpointsList = document.createElement("div");
357
+ addClass(endpointsList, "endpointsTable");
358
+ this.renderEndpointsList(endpointsList);
359
+ section.appendChild(endpointsList);
360
+ container.appendChild(section);
361
+ }
362
+ renderEndpointsList(container) {
363
+ container.innerHTML = "";
364
+ const configs = this.tab.yasgui.persistentConfig.getEndpointConfigs();
365
+ const table = document.createElement("table");
366
+ addClass(table, "endpointsTableElement");
367
+ const thead = document.createElement("thead");
368
+ const headerRow = document.createElement("tr");
369
+ const headers = ["Endpoint", "Label", "Button", "Authentication", "Actions"];
370
+ headers.forEach((h) => {
371
+ const th = document.createElement("th");
372
+ th.textContent = h;
373
+ headerRow.appendChild(th);
374
+ });
375
+ thead.appendChild(headerRow);
376
+ table.appendChild(thead);
377
+ const tbody = document.createElement("tbody");
378
+ if (configs.length === 0) {
379
+ const emptyRow = document.createElement("tr");
380
+ const emptyCell = document.createElement("td");
381
+ emptyCell.colSpan = 5;
382
+ emptyCell.textContent = "No endpoints yet. Add one below or access an endpoint to have it automatically tracked.";
383
+ addClass(emptyCell, "emptyMessage");
384
+ emptyCell.style.textAlign = "center";
385
+ emptyCell.style.padding = "20px";
386
+ emptyRow.appendChild(emptyCell);
387
+ tbody.appendChild(emptyRow);
388
+ }
389
+ configs.forEach((config, index) => {
390
+ const row = document.createElement("tr");
391
+ const endpointCell = document.createElement("td");
392
+ endpointCell.textContent = config.endpoint;
393
+ endpointCell.title = config.endpoint;
394
+ addClass(endpointCell, "endpointCell");
395
+ row.appendChild(endpointCell);
396
+ const labelCell = document.createElement("td");
397
+ const labelInput = document.createElement("input");
398
+ labelInput.type = "text";
399
+ labelInput.value = config.label || "";
400
+ labelInput.placeholder = "Optional label";
401
+ addClass(labelInput, "endpointLabelInput");
402
+ labelInput.onchange = () => {
403
+ this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(config.endpoint, {
404
+ label: labelInput.value.trim() || undefined,
405
+ });
406
+ this.renderEndpointsList(container);
407
+ this.tab.refreshEndpointButtons();
408
+ };
409
+ labelCell.appendChild(labelInput);
410
+ row.appendChild(labelCell);
411
+ const buttonCell = document.createElement("td");
412
+ const buttonCheckbox = document.createElement("input");
413
+ buttonCheckbox.type = "checkbox";
414
+ buttonCheckbox.checked = !!config.showAsButton;
415
+ buttonCheckbox.disabled = !config.label;
416
+ buttonCheckbox.setAttribute("aria-label", config.label ? "Show this endpoint as a quick-switch button" : "Add a label first to enable button");
417
+ buttonCheckbox.title = config.label
418
+ ? "Show this endpoint as a quick-switch button"
419
+ : "Add a label first to enable button";
420
+ buttonCheckbox.onchange = () => {
421
+ this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(config.endpoint, {
422
+ showAsButton: buttonCheckbox.checked,
423
+ });
424
+ this.tab.refreshEndpointButtons();
425
+ };
426
+ buttonCell.appendChild(buttonCheckbox);
427
+ addClass(buttonCell, "centerCell");
428
+ row.appendChild(buttonCell);
429
+ const authCell = document.createElement("td");
430
+ const authButton = document.createElement("button");
431
+ authButton.type = "button";
432
+ addClass(authButton, "configureAuthButton");
433
+ if (config.authentication) {
434
+ authButton.textContent = "✓ Configured";
435
+ addClass(authButton, "authenticated");
436
+ }
437
+ else {
438
+ authButton.textContent = "Configure";
439
+ }
440
+ authButton.onclick = () => this.showAuthenticationModal(config.endpoint);
441
+ authCell.appendChild(authButton);
442
+ addClass(authCell, "centerCell");
443
+ row.appendChild(authCell);
444
+ const actionsCell = document.createElement("td");
445
+ const deleteButton = document.createElement("button");
446
+ deleteButton.type = "button";
447
+ deleteButton.textContent = "Delete";
448
+ addClass(deleteButton, "deleteEndpointButton");
449
+ deleteButton.onclick = () => {
450
+ if (confirm(`Delete endpoint "${config.endpoint}"?`)) {
451
+ this.tab.yasgui.persistentConfig.deleteEndpointConfig(config.endpoint);
452
+ this.renderEndpointsList(container);
453
+ this.tab.refreshEndpointButtons();
454
+ }
455
+ };
456
+ actionsCell.appendChild(deleteButton);
457
+ addClass(actionsCell, "centerCell");
458
+ row.appendChild(actionsCell);
459
+ tbody.appendChild(row);
460
+ });
461
+ table.appendChild(tbody);
462
+ container.appendChild(table);
463
+ const addForm = document.createElement("div");
464
+ addClass(addForm, "addEndpointForm");
465
+ const addFormTitle = document.createElement("div");
466
+ addFormTitle.textContent = "Add New Endpoint";
467
+ addClass(addFormTitle, "addFormTitle");
468
+ addForm.appendChild(addFormTitle);
469
+ const formInputs = document.createElement("div");
470
+ addClass(formInputs, "addFormInputs");
471
+ const endpointInput = document.createElement("input");
472
+ endpointInput.type = "url";
473
+ endpointInput.placeholder = "Endpoint URL (e.g., https://dbpedia.org/sparql)";
474
+ addClass(endpointInput, "addEndpointInput");
475
+ formInputs.appendChild(endpointInput);
476
+ const addButton = document.createElement("button");
477
+ addButton.type = "button";
478
+ addButton.textContent = "+ Add Endpoint";
479
+ addClass(addButton, "addEndpointButton");
480
+ addButton.onclick = () => {
481
+ const endpoint = endpointInput.value.trim();
482
+ if (!endpoint) {
483
+ alert("Please enter an endpoint URL.");
484
+ return;
485
+ }
486
+ if (!/^https?:\/\//i.test(endpoint)) {
487
+ alert("Endpoint URL must start with http:// or https://");
488
+ return;
489
+ }
490
+ try {
491
+ new URL(endpoint);
492
+ }
493
+ catch (e) {
494
+ alert(e instanceof Error && e.message ? "Malformed URL: " + e.message : "Please enter a valid URL.");
495
+ return;
496
+ }
497
+ const existing = this.tab.yasgui.persistentConfig.getEndpointConfig(endpoint);
498
+ if (existing) {
499
+ alert("This endpoint is already in the list.");
500
+ return;
501
+ }
502
+ this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(endpoint, {});
503
+ endpointInput.value = "";
504
+ this.renderEndpointsList(container);
505
+ this.tab.refreshEndpointButtons();
506
+ };
507
+ formInputs.appendChild(addButton);
508
+ addForm.appendChild(formInputs);
509
+ container.appendChild(addForm);
510
+ }
511
+ showAuthenticationModal(endpoint) {
512
+ const config = this.tab.yasgui.persistentConfig.getEndpointConfig(endpoint);
513
+ const existingAuth = config === null || config === void 0 ? void 0 : config.authentication;
514
+ const authModalOverlay = document.createElement("div");
515
+ addClass(authModalOverlay, "authModalOverlay");
516
+ authModalOverlay.onclick = () => authModalOverlay.remove();
517
+ const authModal = document.createElement("div");
518
+ addClass(authModal, "authModal");
519
+ authModal.onclick = (e) => e.stopPropagation();
520
+ const header = document.createElement("div");
521
+ addClass(header, "authModalHeader");
522
+ const title = document.createElement("h3");
523
+ title.textContent = "Configure Authentication";
524
+ const subtitle = document.createElement("div");
525
+ subtitle.textContent = endpoint;
526
+ addClass(subtitle, "authModalSubtitle");
527
+ header.appendChild(title);
528
+ header.appendChild(subtitle);
529
+ authModal.appendChild(header);
530
+ const body = document.createElement("div");
531
+ addClass(body, "authModalBody");
532
+ const typeSection = document.createElement("div");
533
+ addClass(typeSection, "authModalSection");
534
+ const typeLabel = document.createElement("label");
535
+ typeLabel.textContent = "Authentication Type";
536
+ const typeSelect = document.createElement("select");
537
+ const basicOption = document.createElement("option");
538
+ basicOption.value = "basic";
539
+ basicOption.textContent = "HTTP Basic Authentication";
540
+ typeSelect.appendChild(basicOption);
541
+ typeSection.appendChild(typeLabel);
542
+ typeSection.appendChild(typeSelect);
543
+ body.appendChild(typeSection);
544
+ const usernameSection = document.createElement("div");
545
+ addClass(usernameSection, "authModalSection");
546
+ const usernameLabel = document.createElement("label");
547
+ usernameLabel.textContent = "Username";
548
+ const usernameInput = document.createElement("input");
549
+ usernameInput.type = "text";
550
+ usernameInput.placeholder = "Enter username";
551
+ usernameInput.value = (existingAuth === null || existingAuth === void 0 ? void 0 : existingAuth.username) || "";
552
+ usernameInput.autocomplete = "username";
553
+ usernameSection.appendChild(usernameLabel);
554
+ usernameSection.appendChild(usernameInput);
555
+ body.appendChild(usernameSection);
556
+ const passwordSection = document.createElement("div");
557
+ addClass(passwordSection, "authModalSection");
558
+ const passwordLabel = document.createElement("label");
559
+ passwordLabel.textContent = "Password";
560
+ const passwordInput = document.createElement("input");
561
+ passwordInput.type = "password";
562
+ passwordInput.placeholder = "Enter password";
563
+ passwordInput.value = (existingAuth === null || existingAuth === void 0 ? void 0 : existingAuth.password) || "";
564
+ passwordInput.autocomplete = "current-password";
565
+ passwordSection.appendChild(passwordLabel);
566
+ passwordSection.appendChild(passwordInput);
567
+ body.appendChild(passwordSection);
568
+ const securityNotice = document.createElement("div");
569
+ addClass(securityNotice, "authSecurityNotice");
570
+ securityNotice.innerHTML = `
571
+ <strong>⚠️ Security Notice:</strong>
572
+ <ul>
573
+ <li>Credentials are stored in browser localStorage</li>
574
+ <li>Only use with HTTPS endpoints</li>
575
+ <li>Be cautious when using on shared computers</li>
576
+ </ul>
577
+ `;
578
+ body.appendChild(securityNotice);
579
+ authModal.appendChild(body);
580
+ const footer = document.createElement("div");
581
+ addClass(footer, "authModalFooter");
582
+ const removeButton = document.createElement("button");
583
+ removeButton.textContent = "Remove Authentication";
584
+ removeButton.type = "button";
585
+ addClass(removeButton, "authRemoveButton");
586
+ removeButton.onclick = () => {
587
+ this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(endpoint, {
588
+ authentication: undefined,
589
+ });
590
+ authModalOverlay.remove();
591
+ const endpointsList = this.modalContent.querySelector(".endpointsTable");
592
+ if (endpointsList)
593
+ this.renderEndpointsList(endpointsList);
594
+ };
595
+ if (!existingAuth) {
596
+ removeButton.disabled = true;
597
+ }
598
+ const cancelButton = document.createElement("button");
599
+ cancelButton.textContent = "Cancel";
600
+ cancelButton.type = "button";
601
+ addClass(cancelButton, "authCancelButton");
602
+ cancelButton.onclick = () => authModalOverlay.remove();
603
+ const saveButton = document.createElement("button");
604
+ saveButton.textContent = "Save";
605
+ saveButton.type = "button";
606
+ addClass(saveButton, "authSaveButton");
607
+ saveButton.onclick = () => {
608
+ const username = usernameInput.value.trim();
609
+ const password = passwordInput.value;
610
+ if (username && password) {
611
+ this.tab.yasgui.persistentConfig.addOrUpdateEndpoint(endpoint, {
612
+ authentication: {
613
+ type: "basic",
614
+ username,
615
+ password,
616
+ },
617
+ });
618
+ authModalOverlay.remove();
619
+ const endpointsList = this.modalContent.querySelector(".endpointsTable");
620
+ if (endpointsList)
621
+ this.renderEndpointsList(endpointsList);
622
+ }
623
+ else {
624
+ alert("Please enter both username and password.");
625
+ }
626
+ };
627
+ footer.appendChild(removeButton);
628
+ footer.appendChild(cancelButton);
629
+ footer.appendChild(saveButton);
630
+ authModal.appendChild(footer);
631
+ authModalOverlay.appendChild(authModal);
632
+ document.body.appendChild(authModalOverlay);
296
633
  }
297
634
  drawRequestSettings(container) {
298
635
  const reqConfig = this.tab.getRequestConfig();
@@ -388,6 +725,10 @@ export default class TabSettingsModal {
388
725
  if (constructValidationCheckbox) {
389
726
  yasqe.setCheckConstructVariables(constructValidationCheckbox.checked);
390
727
  }
728
+ const snippetsBarCheckbox = document.getElementById("showSnippetsBar");
729
+ if (snippetsBarCheckbox) {
730
+ yasqe.setSnippetsBarVisible(snippetsBarCheckbox.checked);
731
+ }
391
732
  yasqe.saveQuery();
392
733
  }
393
734
  const requestContent = this.modalContent.querySelector("#request-content");
@@ -475,90 +816,6 @@ export default class TabSettingsModal {
475
816
  const deduplicated = this.deduplicatePrefixes(combined);
476
817
  this.tab.yasgui.persistentConfig.setPrefixes(deduplicated);
477
818
  }
478
- drawEndpointButtonsSettings(container) {
479
- const section = document.createElement("div");
480
- addClass(section, "settingsSection");
481
- const label = document.createElement("label");
482
- label.textContent = "Custom Endpoint Buttons";
483
- addClass(label, "settingsLabel");
484
- const help = document.createElement("div");
485
- help.textContent = "Add custom endpoint buttons that will appear next to the endpoint textbox.";
486
- addClass(help, "settingsHelp");
487
- section.appendChild(label);
488
- section.appendChild(help);
489
- const buttonsList = document.createElement("div");
490
- addClass(buttonsList, "endpointButtonsList");
491
- this.renderEndpointButtonsList(buttonsList);
492
- section.appendChild(buttonsList);
493
- const addForm = document.createElement("div");
494
- addClass(addForm, "addEndpointButtonForm");
495
- const labelInput = document.createElement("input");
496
- labelInput.type = "text";
497
- labelInput.placeholder = "Button label (e.g., DBpedia)";
498
- addClass(labelInput, "endpointButtonLabelInput");
499
- const endpointInput = document.createElement("input");
500
- endpointInput.type = "url";
501
- endpointInput.placeholder = "Endpoint URL (e.g., https://dbpedia.org/sparql)";
502
- addClass(endpointInput, "endpointButtonEndpointInput");
503
- const addButton = document.createElement("button");
504
- addButton.textContent = "+ Add Button";
505
- addClass(addButton, "addEndpointButton");
506
- addButton.type = "button";
507
- addButton.onclick = () => {
508
- const labelValue = labelInput.value.trim();
509
- const endpointValue = endpointInput.value.trim();
510
- if (!labelValue || !endpointValue) {
511
- alert("Please enter both a label and an endpoint URL.");
512
- return;
513
- }
514
- const currentButtons = this.tab.yasgui.persistentConfig.getCustomEndpointButtons();
515
- currentButtons.push({ label: labelValue, endpoint: endpointValue });
516
- this.tab.yasgui.persistentConfig.setCustomEndpointButtons(currentButtons);
517
- labelInput.value = "";
518
- endpointInput.value = "";
519
- this.renderEndpointButtonsList(buttonsList);
520
- };
521
- addForm.appendChild(labelInput);
522
- addForm.appendChild(endpointInput);
523
- addForm.appendChild(addButton);
524
- section.appendChild(addForm);
525
- container.appendChild(section);
526
- }
527
- renderEndpointButtonsList(container) {
528
- container.innerHTML = "";
529
- const customButtons = this.tab.yasgui.persistentConfig.getCustomEndpointButtons();
530
- if (customButtons.length === 0) {
531
- const emptyMsg = document.createElement("div");
532
- emptyMsg.textContent = "No custom buttons yet. Add one below.";
533
- addClass(emptyMsg, "emptyMessage");
534
- container.appendChild(emptyMsg);
535
- return;
536
- }
537
- customButtons.forEach((btn, index) => {
538
- const item = document.createElement("div");
539
- addClass(item, "endpointButtonItem");
540
- const labelSpan = document.createElement("span");
541
- labelSpan.textContent = `${btn.label}`;
542
- addClass(labelSpan, "buttonLabel");
543
- const endpointSpan = document.createElement("span");
544
- endpointSpan.textContent = btn.endpoint;
545
- addClass(endpointSpan, "buttonEndpoint");
546
- const removeBtn = document.createElement("button");
547
- removeBtn.textContent = "×";
548
- addClass(removeBtn, "removeButton");
549
- removeBtn.type = "button";
550
- removeBtn.onclick = () => {
551
- const currentButtons = this.tab.yasgui.persistentConfig.getCustomEndpointButtons();
552
- currentButtons.splice(index, 1);
553
- this.tab.yasgui.persistentConfig.setCustomEndpointButtons(currentButtons);
554
- this.renderEndpointButtonsList(container);
555
- };
556
- item.appendChild(labelSpan);
557
- item.appendChild(endpointSpan);
558
- item.appendChild(removeBtn);
559
- container.appendChild(item);
560
- });
561
- }
562
819
  getThemeToggleIcon() {
563
820
  const currentTheme = this.tab.yasgui.getTheme();
564
821
  return currentTheme === "dark" ? MOON_ICON : SUN_ICON;
@@ -695,6 +952,93 @@ export default class TabSettingsModal {
695
952
  importSection.appendChild(fileInput);
696
953
  container.appendChild(importSection);
697
954
  }
955
+ drawKeyboardShortcuts(container) {
956
+ const shortcutsData = [
957
+ {
958
+ category: "Query Editor (YASQE)",
959
+ shortcuts: [
960
+ { keys: ["Ctrl+Enter", "Cmd+Enter"], description: "Execute query" },
961
+ { keys: ["Ctrl+Space", "Cmd+Space"], description: "Trigger autocomplete" },
962
+ { keys: ["Ctrl+S", "Cmd+S"], description: "Save query to local storage" },
963
+ { keys: ["Ctrl+Shift+F", "Cmd+Shift+F"], description: "Format query" },
964
+ { keys: ["Ctrl+/", "Cmd+/"], description: "Toggle comment on selected lines" },
965
+ { keys: ["Ctrl+Shift+D", "Cmd+Shift+D"], description: "Duplicate current line" },
966
+ { keys: ["Ctrl+Shift+K", "Cmd+Shift+K"], description: "Delete current line" },
967
+ { keys: ["Esc"], description: "Remove focus from editor" },
968
+ { keys: ["Ctrl+Click"], description: "Explore URI connections (on URI)" },
969
+ ],
970
+ },
971
+ {
972
+ category: "Fullscreen",
973
+ shortcuts: [
974
+ { keys: ["F11"], description: "Toggle YASQE (editor) fullscreen" },
975
+ { keys: ["F10"], description: "Toggle YASR (results) fullscreen" },
976
+ { keys: ["F9"], description: "Switch between YASQE and YASR fullscreen" },
977
+ { keys: ["Esc"], description: "Exit fullscreen mode" },
978
+ ],
979
+ },
980
+ ];
981
+ shortcutsData.forEach((section) => {
982
+ const sectionEl = document.createElement("div");
983
+ addClass(sectionEl, "shortcutsSection");
984
+ const categoryLabel = document.createElement("h3");
985
+ categoryLabel.textContent = section.category;
986
+ addClass(categoryLabel, "shortcutsCategory");
987
+ sectionEl.appendChild(categoryLabel);
988
+ const table = document.createElement("table");
989
+ addClass(table, "shortcutsTable");
990
+ table.setAttribute("role", "table");
991
+ table.setAttribute("aria-label", `${section.category} keyboard shortcuts`);
992
+ const caption = document.createElement("caption");
993
+ caption.textContent = `${section.category} keyboard shortcuts`;
994
+ caption.style.position = "absolute";
995
+ caption.style.left = "-10000px";
996
+ caption.style.width = "1px";
997
+ caption.style.height = "1px";
998
+ caption.style.overflow = "hidden";
999
+ table.appendChild(caption);
1000
+ const thead = document.createElement("thead");
1001
+ const headerRow = document.createElement("tr");
1002
+ const keysHeader = document.createElement("th");
1003
+ keysHeader.textContent = "Keys";
1004
+ keysHeader.setAttribute("scope", "col");
1005
+ addClass(keysHeader, "shortcutsKeysHeader");
1006
+ headerRow.appendChild(keysHeader);
1007
+ const descHeader = document.createElement("th");
1008
+ descHeader.textContent = "Description";
1009
+ descHeader.setAttribute("scope", "col");
1010
+ addClass(descHeader, "shortcutsDescHeader");
1011
+ headerRow.appendChild(descHeader);
1012
+ thead.appendChild(headerRow);
1013
+ table.appendChild(thead);
1014
+ const tbody = document.createElement("tbody");
1015
+ section.shortcuts.forEach((shortcut) => {
1016
+ const row = document.createElement("tr");
1017
+ const keysCell = document.createElement("td");
1018
+ addClass(keysCell, "shortcutsKeys");
1019
+ shortcut.keys.forEach((key, index) => {
1020
+ if (index > 0) {
1021
+ const separator = document.createElement("span");
1022
+ separator.textContent = " / ";
1023
+ addClass(separator, "shortcutsSeparator");
1024
+ keysCell.appendChild(separator);
1025
+ }
1026
+ const kbd = document.createElement("kbd");
1027
+ kbd.textContent = key;
1028
+ keysCell.appendChild(kbd);
1029
+ });
1030
+ row.appendChild(keysCell);
1031
+ const descCell = document.createElement("td");
1032
+ addClass(descCell, "shortcutsDescription");
1033
+ descCell.textContent = shortcut.description;
1034
+ row.appendChild(descCell);
1035
+ tbody.appendChild(row);
1036
+ });
1037
+ table.appendChild(tbody);
1038
+ sectionEl.appendChild(table);
1039
+ container.appendChild(sectionEl);
1040
+ });
1041
+ }
698
1042
  importConfiguration(turtleContent) {
699
1043
  return __awaiter(this, void 0, void 0, function* () {
700
1044
  var _a;
@@ -737,6 +1081,67 @@ export default class TabSettingsModal {
737
1081
  }
738
1082
  }, 5000);
739
1083
  }
1084
+ drawAboutSettings(container) {
1085
+ const aboutSection = document.createElement("div");
1086
+ addClass(aboutSection, "settingsSection", "aboutSection");
1087
+ const titleContainer = document.createElement("div");
1088
+ addClass(titleContainer, "aboutTitle");
1089
+ const title = document.createElement("h3");
1090
+ title.textContent = "YASGUI";
1091
+ addClass(title, "aboutMainTitle");
1092
+ const versionBadge = document.createElement("span");
1093
+ versionBadge.textContent = `v${VERSION}`;
1094
+ addClass(versionBadge, "versionBadge");
1095
+ titleContainer.appendChild(title);
1096
+ titleContainer.appendChild(versionBadge);
1097
+ aboutSection.appendChild(titleContainer);
1098
+ const subtitle = document.createElement("p");
1099
+ subtitle.textContent = "Yet Another SPARQL GUI";
1100
+ addClass(subtitle, "aboutSubtitle");
1101
+ aboutSection.appendChild(subtitle);
1102
+ const linksSection = document.createElement("div");
1103
+ addClass(linksSection, "aboutLinks");
1104
+ const docsLink = this.createAboutLink("📚 Documentation", "https://yasgui-doc.matdata.eu/docs/", "View the complete documentation and guides");
1105
+ linksSection.appendChild(docsLink);
1106
+ const releasesLink = this.createAboutLink("📝 Release Notes", "https://github.com/Matdata-eu/Yasgui/releases", "See what's new in the latest releases");
1107
+ linksSection.appendChild(releasesLink);
1108
+ const issuesLink = this.createAboutLink("🐛 Report Issues & Get Support", "https://github.com/Matdata-eu/Yasgui/issues", "Report bugs, request features, or ask for help");
1109
+ linksSection.appendChild(issuesLink);
1110
+ aboutSection.appendChild(linksSection);
1111
+ const footerInfo = document.createElement("div");
1112
+ addClass(footerInfo, "aboutFooter");
1113
+ const paragraph1 = document.createElement("p");
1114
+ paragraph1.textContent = "YASGUI is an open-source project maintained by ";
1115
+ const matdataLink = document.createElement("a");
1116
+ matdataLink.href = "https://matdata.eu";
1117
+ matdataLink.target = "_blank";
1118
+ matdataLink.rel = "noopener noreferrer";
1119
+ matdataLink.textContent = "Matdata";
1120
+ paragraph1.appendChild(matdataLink);
1121
+ paragraph1.appendChild(document.createTextNode("."));
1122
+ const paragraph2 = document.createElement("p");
1123
+ paragraph2.textContent = "Licensed under the MIT License.";
1124
+ footerInfo.appendChild(paragraph1);
1125
+ footerInfo.appendChild(paragraph2);
1126
+ aboutSection.appendChild(footerInfo);
1127
+ container.appendChild(aboutSection);
1128
+ }
1129
+ createAboutLink(label, url, description) {
1130
+ const linkContainer = document.createElement("div");
1131
+ addClass(linkContainer, "aboutLinkItem");
1132
+ const link = document.createElement("a");
1133
+ link.href = url;
1134
+ link.target = "_blank";
1135
+ link.rel = "noopener noreferrer";
1136
+ link.textContent = label;
1137
+ addClass(link, "aboutLink");
1138
+ const desc = document.createElement("p");
1139
+ desc.textContent = description;
1140
+ addClass(desc, "aboutLinkDescription");
1141
+ linkContainer.appendChild(link);
1142
+ linkContainer.appendChild(desc);
1143
+ return linkContainer;
1144
+ }
740
1145
  destroy() {
741
1146
  if (this.modalOverlay && this.modalOverlay.parentNode) {
742
1147
  this.modalOverlay.parentNode.removeChild(this.modalOverlay);