@matdata/yasgui 5.10.0 → 5.11.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.
Files changed (133) hide show
  1. package/README.md +6 -2
  2. package/build/ts/src/PersistentConfig.d.ts +10 -0
  3. package/build/ts/src/PersistentConfig.js +40 -0
  4. package/build/ts/src/PersistentConfig.js.map +1 -1
  5. package/build/ts/src/Tab.d.ts +17 -0
  6. package/build/ts/src/Tab.js +372 -15
  7. package/build/ts/src/Tab.js.map +1 -1
  8. package/build/ts/src/TabContextMenu.d.ts +1 -0
  9. package/build/ts/src/TabContextMenu.js +17 -0
  10. package/build/ts/src/TabContextMenu.js.map +1 -1
  11. package/build/ts/src/TabElements.d.ts +3 -0
  12. package/build/ts/src/TabElements.js +97 -28
  13. package/build/ts/src/TabElements.js.map +1 -1
  14. package/build/ts/src/TabSettingsModal.d.ts +2 -0
  15. package/build/ts/src/TabSettingsModal.js +44 -19
  16. package/build/ts/src/TabSettingsModal.js.map +1 -1
  17. package/build/ts/src/index.d.ts +3 -0
  18. package/build/ts/src/index.js +4 -0
  19. package/build/ts/src/index.js.map +1 -1
  20. package/build/ts/src/queryManagement/QueryBrowser.d.ts +64 -0
  21. package/build/ts/src/queryManagement/QueryBrowser.js +914 -0
  22. package/build/ts/src/queryManagement/QueryBrowser.js.map +1 -0
  23. package/build/ts/src/queryManagement/SaveManagedQueryModal.d.ts +55 -0
  24. package/build/ts/src/queryManagement/SaveManagedQueryModal.js +451 -0
  25. package/build/ts/src/queryManagement/SaveManagedQueryModal.js.map +1 -0
  26. package/build/ts/src/queryManagement/WorkspaceSettingsForm.d.ts +16 -0
  27. package/build/ts/src/queryManagement/WorkspaceSettingsForm.js +452 -0
  28. package/build/ts/src/queryManagement/WorkspaceSettingsForm.js.map +1 -0
  29. package/build/ts/src/queryManagement/backends/BaseGitProviderClient.d.ts +19 -0
  30. package/build/ts/src/queryManagement/backends/BaseGitProviderClient.js +77 -0
  31. package/build/ts/src/queryManagement/backends/BaseGitProviderClient.js.map +1 -0
  32. package/build/ts/src/queryManagement/backends/BitbucketProviderClient.d.ts +16 -0
  33. package/build/ts/src/queryManagement/backends/BitbucketProviderClient.js +269 -0
  34. package/build/ts/src/queryManagement/backends/BitbucketProviderClient.js.map +1 -0
  35. package/build/ts/src/queryManagement/backends/GitWorkspaceBackend.d.ts +26 -0
  36. package/build/ts/src/queryManagement/backends/GitWorkspaceBackend.js +93 -0
  37. package/build/ts/src/queryManagement/backends/GitWorkspaceBackend.js.map +1 -0
  38. package/build/ts/src/queryManagement/backends/GiteaProviderClient.d.ts +14 -0
  39. package/build/ts/src/queryManagement/backends/GiteaProviderClient.js +244 -0
  40. package/build/ts/src/queryManagement/backends/GiteaProviderClient.js.map +1 -0
  41. package/build/ts/src/queryManagement/backends/GithubProviderClient.d.ts +14 -0
  42. package/build/ts/src/queryManagement/backends/GithubProviderClient.js +252 -0
  43. package/build/ts/src/queryManagement/backends/GithubProviderClient.js.map +1 -0
  44. package/build/ts/src/queryManagement/backends/GitlabProviderClient.d.ts +16 -0
  45. package/build/ts/src/queryManagement/backends/GitlabProviderClient.js +246 -0
  46. package/build/ts/src/queryManagement/backends/GitlabProviderClient.js.map +1 -0
  47. package/build/ts/src/queryManagement/backends/InMemoryWorkspaceBackend.d.ts +21 -0
  48. package/build/ts/src/queryManagement/backends/InMemoryWorkspaceBackend.js +175 -0
  49. package/build/ts/src/queryManagement/backends/InMemoryWorkspaceBackend.js.map +1 -0
  50. package/build/ts/src/queryManagement/backends/SparqlWorkspaceBackend.d.ts +28 -0
  51. package/build/ts/src/queryManagement/backends/SparqlWorkspaceBackend.js +687 -0
  52. package/build/ts/src/queryManagement/backends/SparqlWorkspaceBackend.js.map +1 -0
  53. package/build/ts/src/queryManagement/backends/WorkspaceBackend.d.ts +15 -0
  54. package/build/ts/src/queryManagement/backends/WorkspaceBackend.js +2 -0
  55. package/build/ts/src/queryManagement/backends/WorkspaceBackend.js.map +1 -0
  56. package/build/ts/src/queryManagement/backends/errors.d.ts +7 -0
  57. package/build/ts/src/queryManagement/backends/errors.js +18 -0
  58. package/build/ts/src/queryManagement/backends/errors.js.map +1 -0
  59. package/build/ts/src/queryManagement/backends/getWorkspaceBackend.d.ts +8 -0
  60. package/build/ts/src/queryManagement/backends/getWorkspaceBackend.js +114 -0
  61. package/build/ts/src/queryManagement/backends/getWorkspaceBackend.js.map +1 -0
  62. package/build/ts/src/queryManagement/backends/gitRemote.d.ts +5 -0
  63. package/build/ts/src/queryManagement/backends/gitRemote.js +40 -0
  64. package/build/ts/src/queryManagement/backends/gitRemote.js.map +1 -0
  65. package/build/ts/src/queryManagement/browserFilter.d.ts +2 -0
  66. package/build/ts/src/queryManagement/browserFilter.js +7 -0
  67. package/build/ts/src/queryManagement/browserFilter.js.map +1 -0
  68. package/build/ts/src/queryManagement/index.d.ts +13 -0
  69. package/build/ts/src/queryManagement/index.js +14 -0
  70. package/build/ts/src/queryManagement/index.js.map +1 -0
  71. package/build/ts/src/queryManagement/normalizeQueryFilename.d.ts +1 -0
  72. package/build/ts/src/queryManagement/normalizeQueryFilename.js +10 -0
  73. package/build/ts/src/queryManagement/normalizeQueryFilename.js.map +1 -0
  74. package/build/ts/src/queryManagement/openManagedQuery.d.ts +15 -0
  75. package/build/ts/src/queryManagement/openManagedQuery.js +27 -0
  76. package/build/ts/src/queryManagement/openManagedQuery.js.map +1 -0
  77. package/build/ts/src/queryManagement/saveManagedQuery.d.ts +20 -0
  78. package/build/ts/src/queryManagement/saveManagedQuery.js +109 -0
  79. package/build/ts/src/queryManagement/saveManagedQuery.js.map +1 -0
  80. package/build/ts/src/queryManagement/textHash.d.ts +2 -0
  81. package/build/ts/src/queryManagement/textHash.js +13 -0
  82. package/build/ts/src/queryManagement/textHash.js.map +1 -0
  83. package/build/ts/src/queryManagement/types.d.ts +76 -0
  84. package/build/ts/src/queryManagement/types.js +2 -0
  85. package/build/ts/src/queryManagement/types.js.map +1 -0
  86. package/build/ts/src/queryManagement/validateWorkspaceConfig.d.ts +6 -0
  87. package/build/ts/src/queryManagement/validateWorkspaceConfig.js +33 -0
  88. package/build/ts/src/queryManagement/validateWorkspaceConfig.js.map +1 -0
  89. package/build/ts/src/version.d.ts +1 -1
  90. package/build/ts/src/version.js +1 -1
  91. package/build/yasgui.min.css +10 -1
  92. package/build/yasgui.min.css.map +3 -3
  93. package/build/yasgui.min.js +398 -172
  94. package/build/yasgui.min.js.map +4 -4
  95. package/package.json +1 -1
  96. package/src/PersistentConfig.ts +61 -0
  97. package/src/Tab.ts +431 -20
  98. package/src/TabContextMenu.ts +10 -0
  99. package/src/TabElements.scss +46 -7
  100. package/src/TabElements.ts +95 -27
  101. package/src/TabSettingsModal.scss +102 -5
  102. package/src/TabSettingsModal.ts +48 -25
  103. package/src/endpointSelect.scss +2 -3
  104. package/src/index.scss +4 -0
  105. package/src/index.ts +7 -0
  106. package/src/queryManagement/QueryBrowser.scss +418 -0
  107. package/src/queryManagement/QueryBrowser.ts +1079 -0
  108. package/src/queryManagement/SaveManagedQueryModal.scss +245 -0
  109. package/src/queryManagement/SaveManagedQueryModal.ts +554 -0
  110. package/src/queryManagement/WorkspaceSettingsForm.ts +546 -0
  111. package/src/queryManagement/backends/BaseGitProviderClient.ts +124 -0
  112. package/src/queryManagement/backends/BitbucketProviderClient.ts +403 -0
  113. package/src/queryManagement/backends/GitWorkspaceBackend.ts +96 -0
  114. package/src/queryManagement/backends/GiteaProviderClient.ts +316 -0
  115. package/src/queryManagement/backends/GithubProviderClient.ts +319 -0
  116. package/src/queryManagement/backends/GitlabProviderClient.ts +327 -0
  117. package/src/queryManagement/backends/InMemoryWorkspaceBackend.ts +175 -0
  118. package/src/queryManagement/backends/SparqlWorkspaceBackend.ts +729 -0
  119. package/src/queryManagement/backends/WorkspaceBackend.ts +41 -0
  120. package/src/queryManagement/backends/errors.ts +28 -0
  121. package/src/queryManagement/backends/getWorkspaceBackend.ts +132 -0
  122. package/src/queryManagement/backends/gitRemote.ts +54 -0
  123. package/src/queryManagement/browserFilter.ts +8 -0
  124. package/src/queryManagement/index.ts +15 -0
  125. package/src/queryManagement/normalizeQueryFilename.ts +8 -0
  126. package/src/queryManagement/openManagedQuery.ts +31 -0
  127. package/src/queryManagement/saveManagedQuery.ts +135 -0
  128. package/src/queryManagement/textHash.ts +15 -0
  129. package/src/queryManagement/types.ts +85 -0
  130. package/src/queryManagement/validateWorkspaceConfig.ts +40 -0
  131. package/src/tab.scss +4 -14
  132. package/src/themes.scss +14 -23
  133. package/src/version.ts +1 -1
@@ -0,0 +1,914 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { addClass, removeClass } from "@matdata/yasgui-utils";
11
+ import { filterFolderEntriesByName } from "./browserFilter";
12
+ import { getWorkspaceBackend } from "./backends/getWorkspaceBackend";
13
+ import { asWorkspaceBackendError } from "./backends/errors";
14
+ import { getEndpointToAutoSwitch } from "./openManagedQuery";
15
+ import { hashQueryText } from "./textHash";
16
+ import { normalizeQueryFilename } from "./normalizeQueryFilename";
17
+ import "./QueryBrowser.scss";
18
+ export default class QueryBrowser {
19
+ entrySignature(entry) {
20
+ const parent = entry.parentId || "";
21
+ return `${entry.kind}:${entry.id}:${entry.label}:${parent}`;
22
+ }
23
+ invalidateRenderCache() {
24
+ this.lastRenderedSignature = undefined;
25
+ }
26
+ invalidateAndRefresh(workspaceId) {
27
+ if (!workspaceId || this.treeWorkspaceId === workspaceId) {
28
+ this.folderEntriesById.clear();
29
+ this.folderLoadingById.clear();
30
+ this.folderErrorById.clear();
31
+ this.queryPreviewById.clear();
32
+ this.queryPreviewLoadingById.clear();
33
+ this.invalidateRenderCache();
34
+ if (this.isOpen)
35
+ void this.refresh();
36
+ }
37
+ }
38
+ constructor(yasgui) {
39
+ this.isOpen = false;
40
+ this.expandedFolderIds = new Set();
41
+ this.folderEntriesById = new Map();
42
+ this.folderLoadingById = new Set();
43
+ this.folderErrorById = new Map();
44
+ this.refreshRunId = 0;
45
+ this.queryPreviewById = new Map();
46
+ this.queryPreviewLoadingById = new Set();
47
+ this.yasgui = yasgui;
48
+ this.rootEl = document.createElement("div");
49
+ addClass(this.rootEl, "yasgui-query-browser");
50
+ this.tooltipEl = document.createElement("div");
51
+ addClass(this.tooltipEl, "yasgui-query-browser__tooltip");
52
+ this.tooltipEl.setAttribute("role", "tooltip");
53
+ this.tooltipEl.setAttribute("aria-hidden", "true");
54
+ this.drawerEl = document.createElement("div");
55
+ addClass(this.drawerEl, "yasgui-query-browser__drawer");
56
+ this.headerEl = document.createElement("div");
57
+ addClass(this.headerEl, "yasgui-query-browser__header");
58
+ this.bodyEl = document.createElement("div");
59
+ addClass(this.bodyEl, "yasgui-query-browser__body");
60
+ this.bodyEl.addEventListener("mousemove", (e) => {
61
+ this.lastPointerPos = { x: e.clientX, y: e.clientY };
62
+ if (this.tooltipEl.classList.contains("open"))
63
+ this.positionTooltip();
64
+ });
65
+ this.footerEl = document.createElement("div");
66
+ addClass(this.footerEl, "yasgui-query-browser__footer");
67
+ this.statusEl = document.createElement("div");
68
+ addClass(this.statusEl, "yasgui-query-browser__status");
69
+ this.workspaceSelectEl = document.createElement("select");
70
+ this.workspaceSelectEl.setAttribute("aria-label", "Select workspace");
71
+ this.workspaceSelectEl.addEventListener("change", () => {
72
+ this.selectedWorkspaceId = this.workspaceSelectEl.value || undefined;
73
+ this.currentFolderId = undefined;
74
+ this.resetTreeState();
75
+ this.searchEl.value = "";
76
+ void this.refresh();
77
+ });
78
+ this.searchEl = document.createElement("input");
79
+ this.searchEl.type = "search";
80
+ this.searchEl.placeholder = "Search queries";
81
+ this.searchEl.setAttribute("aria-label", "Search managed queries by name");
82
+ this.searchEl.addEventListener("input", () => {
83
+ if (this.debouncedSearchHandle)
84
+ window.clearTimeout(this.debouncedSearchHandle);
85
+ this.debouncedSearchHandle = window.setTimeout(() => {
86
+ void this.refresh();
87
+ }, 250);
88
+ });
89
+ this.backButtonEl = document.createElement("button");
90
+ this.backButtonEl.type = "button";
91
+ this.backButtonEl.textContent = "Back";
92
+ addClass(this.backButtonEl, "yasgui-query-browser__back");
93
+ this.backButtonEl.setAttribute("aria-label", "Go to parent folder");
94
+ this.backButtonEl.addEventListener("click", () => {
95
+ if (!this.currentFolderId)
96
+ return;
97
+ const parts = this.currentFolderId.split("/").filter(Boolean);
98
+ parts.pop();
99
+ this.currentFolderId = parts.join("/") || undefined;
100
+ void this.refresh();
101
+ });
102
+ const titleEl = document.createElement("div");
103
+ addClass(titleEl, "yasgui-query-browser__title");
104
+ titleEl.textContent = "Query Browser";
105
+ const headerButtons = document.createElement("div");
106
+ addClass(headerButtons, "yasgui-query-browser__header-buttons");
107
+ const helpButton = document.createElement("button");
108
+ helpButton.type = "button";
109
+ addClass(helpButton, "yasgui-query-browser__help");
110
+ helpButton.setAttribute("aria-label", "Open documentation");
111
+ helpButton.innerHTML = '<i class="fas fa-circle-question"></i>';
112
+ helpButton.addEventListener("click", () => {
113
+ window.open("https://yasgui-doc.matdata.eu/docs/user-guide#managed-queries-and-workspaces", "_blank", "noopener,noreferrer");
114
+ });
115
+ const closeButton = document.createElement("button");
116
+ closeButton.type = "button";
117
+ addClass(closeButton, "yasgui-query-browser__close");
118
+ closeButton.setAttribute("aria-label", "Close query browser");
119
+ closeButton.innerHTML = '<i class="fas fa-xmark"></i>';
120
+ closeButton.addEventListener("click", () => this.close());
121
+ headerButtons.appendChild(helpButton);
122
+ headerButtons.appendChild(closeButton);
123
+ const headerControls = document.createElement("div");
124
+ addClass(headerControls, "yasgui-query-browser__header-controls");
125
+ headerControls.appendChild(this.workspaceSelectEl);
126
+ headerControls.appendChild(this.searchEl);
127
+ const headerTop = document.createElement("div");
128
+ addClass(headerTop, "yasgui-query-browser__header-top");
129
+ headerTop.appendChild(titleEl);
130
+ headerTop.appendChild(headerButtons);
131
+ this.headerEl.appendChild(headerTop);
132
+ this.headerEl.appendChild(headerControls);
133
+ this.listEl = document.createElement("div");
134
+ addClass(this.listEl, "yasgui-query-browser__list");
135
+ this.bodyEl.appendChild(this.backButtonEl);
136
+ this.bodyEl.appendChild(this.statusEl);
137
+ this.bodyEl.appendChild(this.listEl);
138
+ this.footerEl.textContent = "";
139
+ this.drawerEl.appendChild(this.headerEl);
140
+ this.drawerEl.appendChild(this.bodyEl);
141
+ this.drawerEl.appendChild(this.footerEl);
142
+ this.rootEl.appendChild(this.drawerEl);
143
+ this.rootEl.appendChild(this.tooltipEl);
144
+ this.rootEl.addEventListener("click", (e) => {
145
+ if (e.target === this.rootEl)
146
+ this.close();
147
+ });
148
+ document.addEventListener("keydown", (e) => {
149
+ if (!this.isOpen)
150
+ return;
151
+ if (e.key === "Escape") {
152
+ e.preventDefault();
153
+ this.close();
154
+ }
155
+ });
156
+ this.close();
157
+ }
158
+ getElement() {
159
+ return this.rootEl;
160
+ }
161
+ open(openerEl) {
162
+ this.openerEl = openerEl;
163
+ this.isOpen = true;
164
+ this.rootEl.style.display = "flex";
165
+ addClass(this.rootEl, "open");
166
+ this.rootEl.setAttribute("aria-hidden", "false");
167
+ this.drawerEl.setAttribute("role", "dialog");
168
+ this.drawerEl.setAttribute("aria-modal", "true");
169
+ void this.refresh().then(() => {
170
+ this.workspaceSelectEl.focus();
171
+ });
172
+ }
173
+ close() {
174
+ this.isOpen = false;
175
+ removeClass(this.rootEl, "open");
176
+ this.rootEl.setAttribute("aria-hidden", "true");
177
+ this.rootEl.style.display = "none";
178
+ if (this.openerEl) {
179
+ this.openerEl.focus();
180
+ this.openerEl = undefined;
181
+ }
182
+ }
183
+ toggle(openerEl) {
184
+ if (this.isOpen)
185
+ this.close();
186
+ else
187
+ this.open(openerEl);
188
+ }
189
+ getWorkspaces() {
190
+ return this.yasgui.persistentConfig.getWorkspaces();
191
+ }
192
+ ensureWorkspaceSelection() {
193
+ const workspaces = this.getWorkspaces();
194
+ if (workspaces.length === 0) {
195
+ this.selectedWorkspaceId = undefined;
196
+ return;
197
+ }
198
+ if (!this.selectedWorkspaceId) {
199
+ const persistedActive = this.yasgui.persistentConfig.getActiveWorkspaceId();
200
+ this.selectedWorkspaceId = persistedActive || workspaces[0].id;
201
+ }
202
+ const exists = workspaces.some((w) => w.id === this.selectedWorkspaceId);
203
+ if (!exists)
204
+ this.selectedWorkspaceId = workspaces[0].id;
205
+ this.yasgui.persistentConfig.setActiveWorkspaceId(this.selectedWorkspaceId);
206
+ }
207
+ renderWorkspaceSelect() {
208
+ const workspaces = this.getWorkspaces();
209
+ this.workspaceSelectEl.innerHTML = "";
210
+ const placeholder = document.createElement("option");
211
+ placeholder.value = "";
212
+ placeholder.textContent = workspaces.length ? "Select workspace" : "No workspaces";
213
+ this.workspaceSelectEl.appendChild(placeholder);
214
+ for (const w of workspaces) {
215
+ const opt = document.createElement("option");
216
+ opt.value = w.id;
217
+ opt.textContent = w.label;
218
+ this.workspaceSelectEl.appendChild(opt);
219
+ }
220
+ this.workspaceSelectEl.value = this.selectedWorkspaceId || "";
221
+ }
222
+ setStatus(text) {
223
+ this.statusEl.textContent = text;
224
+ }
225
+ resetTreeState() {
226
+ this.treeWorkspaceId = undefined;
227
+ this.expandedFolderIds.clear();
228
+ this.folderEntriesById.clear();
229
+ this.folderLoadingById.clear();
230
+ this.folderErrorById.clear();
231
+ this.invalidateRenderCache();
232
+ }
233
+ folderKey(folderId) {
234
+ return folderId || "";
235
+ }
236
+ ensureFolderLoaded(backend, folderId) {
237
+ return __awaiter(this, void 0, void 0, function* () {
238
+ const key = this.folderKey(folderId);
239
+ if (this.folderEntriesById.has(key) || this.folderLoadingById.has(key))
240
+ return;
241
+ this.folderLoadingById.add(key);
242
+ this.folderErrorById.delete(key);
243
+ try {
244
+ const entries = yield backend.listFolder(folderId);
245
+ this.folderEntriesById.set(key, entries);
246
+ }
247
+ catch (e) {
248
+ const err = asWorkspaceBackendError(e);
249
+ this.folderErrorById.set(key, err.message || "Failed to load folder");
250
+ this.folderEntriesById.set(key, []);
251
+ }
252
+ finally {
253
+ this.folderLoadingById.delete(key);
254
+ }
255
+ });
256
+ }
257
+ formatStatusError(err, workspaceType) {
258
+ const message = err.message || "Unknown error";
259
+ if (message.includes("No GitProviderClient configured")) {
260
+ return "Git workspaces require a supported provider. Supported: GitHub, GitLab, Bitbucket Cloud (bitbucket.org), and Gitea. For self-hosted/enterprise instances, configure a git workspace 'provider' and/or 'apiBaseUrl'.";
261
+ }
262
+ if (message.toLowerCase().includes("not implemented")) {
263
+ return "This workspace backend is not supported yet.";
264
+ }
265
+ return message;
266
+ }
267
+ clearList() {
268
+ this.listEl.innerHTML = "";
269
+ }
270
+ renderEmptyWorkspaceState() {
271
+ const emptyState = document.createElement("div");
272
+ addClass(emptyState, "yasgui-query-browser__empty-state");
273
+ const icon = document.createElement("div");
274
+ addClass(icon, "yasgui-query-browser__empty-icon");
275
+ icon.innerHTML = '<i class="fas fa-file-lines"></i>';
276
+ const title = document.createElement("h3");
277
+ addClass(title, "yasgui-query-browser__empty-title");
278
+ title.textContent = "No Workspaces Configured";
279
+ const description = document.createElement("p");
280
+ addClass(description, "yasgui-query-browser__empty-description");
281
+ description.innerHTML = `Workspaces allow you to save and manage SPARQL queries in a shared, versioned store.
282
+ You can use SPARQL endpoints (recommended) or Git repositories (GitHub, GitLab, Bitbucket, Gitea) to store your queries.`;
283
+ const actionsContainer = document.createElement("div");
284
+ addClass(actionsContainer, "yasgui-query-browser__empty-actions");
285
+ const addWorkspaceBtn = document.createElement("button");
286
+ addClass(addWorkspaceBtn, "yasgui-query-browser__empty-button");
287
+ addClass(addWorkspaceBtn, "yasgui-query-browser__empty-button--primary");
288
+ addWorkspaceBtn.textContent = "Add Workspace";
289
+ addWorkspaceBtn.addEventListener("click", () => {
290
+ this.close();
291
+ const tab = this.yasgui.getTab();
292
+ if (tab && tab.settingsModal) {
293
+ const modal = tab.settingsModal;
294
+ modal.open();
295
+ setTimeout(() => {
296
+ var _a;
297
+ const workspacesButton = (_a = modal.modalContent) === null || _a === void 0 ? void 0 : _a.querySelector(".modalNavButton:nth-child(3)");
298
+ if (workspacesButton)
299
+ workspacesButton.click();
300
+ }, 50);
301
+ }
302
+ });
303
+ const learnMoreLink = document.createElement("a");
304
+ addClass(learnMoreLink, "yasgui-query-browser__empty-link");
305
+ learnMoreLink.href = "https://yasgui-doc.matdata.eu/docs/user-guide#managed-queries-and-workspaces";
306
+ learnMoreLink.target = "_blank";
307
+ learnMoreLink.rel = "noopener noreferrer";
308
+ learnMoreLink.textContent = "Learn more about workspaces";
309
+ actionsContainer.appendChild(addWorkspaceBtn);
310
+ actionsContainer.appendChild(learnMoreLink);
311
+ emptyState.appendChild(icon);
312
+ emptyState.appendChild(title);
313
+ emptyState.appendChild(description);
314
+ emptyState.appendChild(actionsContainer);
315
+ this.listEl.appendChild(emptyState);
316
+ }
317
+ formatQueryPreview(queryText, description) {
318
+ const normalized = queryText.replace(/\r\n?/g, "\n").trim();
319
+ const lines = normalized.split("\n").map((l) => l.trimEnd());
320
+ const maxLines = 12;
321
+ const selected = lines.slice(0, maxLines);
322
+ let out = selected.join("\n");
323
+ const maxChars = 800;
324
+ if (out.length > maxChars)
325
+ out = out.slice(0, maxChars - 1).trimEnd() + "…";
326
+ if (lines.length > maxLines && !out.endsWith("…"))
327
+ out = out + "\n…";
328
+ const trimmedDesc = description === null || description === void 0 ? void 0 : description.replace(/\r\n?/g, "\n").trim();
329
+ if (!trimmedDesc)
330
+ return out;
331
+ return `${trimmedDesc}\n\n${out}`;
332
+ }
333
+ ensureQueryPreview(backend, queryId) {
334
+ return __awaiter(this, void 0, void 0, function* () {
335
+ const cached = this.queryPreviewById.get(queryId);
336
+ if (cached)
337
+ return cached;
338
+ if (this.queryPreviewLoadingById.has(queryId))
339
+ return undefined;
340
+ this.queryPreviewLoadingById.add(queryId);
341
+ try {
342
+ const res = yield backend.readQuery(queryId);
343
+ const preview = this.formatQueryPreview(res.queryText, res.description);
344
+ this.queryPreviewById.set(queryId, preview);
345
+ return preview;
346
+ }
347
+ catch (_a) {
348
+ return undefined;
349
+ }
350
+ finally {
351
+ this.queryPreviewLoadingById.delete(queryId);
352
+ }
353
+ });
354
+ }
355
+ getManagedQueryIdFromMetadata(meta) {
356
+ var _a, _b;
357
+ if (meta.backendType === "git")
358
+ return (_a = meta.queryRef) === null || _a === void 0 ? void 0 : _a.path;
359
+ return (_b = meta.queryRef) === null || _b === void 0 ? void 0 : _b.managedQueryIri;
360
+ }
361
+ findOpenManagedTabId(workspaceId, backendType, queryId) {
362
+ for (const tab of Object.values(this.yasgui._tabs)) {
363
+ const meta = tab.getManagedQueryMetadata();
364
+ if (!meta)
365
+ continue;
366
+ if (meta.workspaceId !== workspaceId)
367
+ continue;
368
+ if (meta.backendType !== backendType)
369
+ continue;
370
+ const openQueryId = this.getManagedQueryIdFromMetadata(meta);
371
+ if (openQueryId === queryId)
372
+ return tab.getId();
373
+ }
374
+ return undefined;
375
+ }
376
+ showTooltip(text) {
377
+ this.tooltipEl.textContent = text;
378
+ this.tooltipEl.setAttribute("aria-hidden", "false");
379
+ addClass(this.tooltipEl, "open");
380
+ this.positionTooltip();
381
+ }
382
+ hideTooltip() {
383
+ this.tooltipEl.textContent = "";
384
+ this.tooltipEl.setAttribute("aria-hidden", "true");
385
+ removeClass(this.tooltipEl, "open");
386
+ }
387
+ positionTooltip() {
388
+ const pos = this.lastPointerPos;
389
+ if (!pos)
390
+ return;
391
+ let left = pos.x + 12;
392
+ let top = pos.y + 12;
393
+ const rect = this.tooltipEl.getBoundingClientRect();
394
+ const vw = window.innerWidth;
395
+ const vh = window.innerHeight;
396
+ if (left + rect.width > vw - 8)
397
+ left = Math.max(8, vw - rect.width - 8);
398
+ if (top + rect.height > vh - 8)
399
+ top = Math.max(8, vh - rect.height - 8);
400
+ this.tooltipEl.style.left = `${left}px`;
401
+ this.tooltipEl.style.top = `${top}px`;
402
+ }
403
+ attachQueryHoverPreview(row, backend, queryId) {
404
+ const onEnter = () => {
405
+ const cached = this.queryPreviewById.get(queryId);
406
+ if (cached) {
407
+ this.showTooltip(cached);
408
+ return;
409
+ }
410
+ this.showTooltip("Loading preview…");
411
+ void this.ensureQueryPreview(backend, queryId).then((preview) => {
412
+ if (!this.tooltipEl.classList.contains("open"))
413
+ return;
414
+ if (preview)
415
+ this.showTooltip(preview);
416
+ });
417
+ };
418
+ const onLeave = () => {
419
+ this.hideTooltip();
420
+ };
421
+ row.addEventListener("mouseenter", onEnter);
422
+ row.addEventListener("mouseleave", onLeave);
423
+ row.addEventListener("focusin", onEnter);
424
+ row.addEventListener("focusout", onLeave);
425
+ }
426
+ getBackendForSelectedWorkspace() {
427
+ const workspace = this.getWorkspaces().find((w) => w.id === this.selectedWorkspaceId);
428
+ if (!workspace)
429
+ return undefined;
430
+ return getWorkspaceBackend(workspace, { persistentConfig: this.yasgui.persistentConfig });
431
+ }
432
+ versionRefFromVersionTag(backendType, versionTag) {
433
+ if (!versionTag)
434
+ return undefined;
435
+ if (backendType === "git")
436
+ return { commitSha: versionTag };
437
+ return { managedQueryVersionIri: versionTag };
438
+ }
439
+ openManagedQueryInNewTab(backend, backendType, queryId, queryLabel) {
440
+ return __awaiter(this, void 0, void 0, function* () {
441
+ const workspaceId = this.selectedWorkspaceId;
442
+ const alreadyOpenTabId = this.findOpenManagedTabId(workspaceId, backendType, queryId);
443
+ if (alreadyOpenTabId) {
444
+ this.yasgui.selectTabId(alreadyOpenTabId);
445
+ return;
446
+ }
447
+ const read = yield backend.readQuery(queryId);
448
+ const tab = this.yasgui.addTab(true);
449
+ if (queryLabel)
450
+ tab.setName(queryLabel);
451
+ const endpoint = getEndpointToAutoSwitch(backendType, read);
452
+ if (endpoint)
453
+ tab.setEndpoint(endpoint);
454
+ tab.setQuery(read.queryText);
455
+ const managedMetadata = {
456
+ workspaceId,
457
+ backendType,
458
+ queryRef: backendType === "git" ? { path: queryId } : { managedQueryIri: queryId },
459
+ lastSavedVersionRef: this.versionRefFromVersionTag(backendType, read.versionTag),
460
+ lastSavedTextHash: hashQueryText(read.queryText),
461
+ };
462
+ tab.setManagedQueryMetadata(managedMetadata);
463
+ });
464
+ }
465
+ addQueryRowActions(row, backend, entry) {
466
+ const actions = document.createElement("span");
467
+ addClass(actions, "yasgui-query-browser__actions");
468
+ if (entry.kind === "folder") {
469
+ if (backend.renameFolder) {
470
+ const renameBtn = document.createElement("button");
471
+ renameBtn.type = "button";
472
+ addClass(renameBtn, "yasgui-query-browser__action");
473
+ renameBtn.textContent = "Rename";
474
+ renameBtn.setAttribute("aria-label", `Rename folder ${entry.label}`);
475
+ renameBtn.addEventListener("click", (e) => __awaiter(this, void 0, void 0, function* () {
476
+ e.preventDefault();
477
+ e.stopPropagation();
478
+ const next = window.prompt("Rename folder", entry.label);
479
+ if (!next)
480
+ return;
481
+ const trimmed = next.trim();
482
+ if (!trimmed || trimmed === entry.label)
483
+ return;
484
+ try {
485
+ yield backend.renameFolder(entry.id, trimmed);
486
+ this.folderEntriesById.clear();
487
+ this.invalidateRenderCache();
488
+ yield this.refresh();
489
+ }
490
+ catch (err) {
491
+ window.alert(asWorkspaceBackendError(err).message);
492
+ }
493
+ }));
494
+ actions.appendChild(renameBtn);
495
+ }
496
+ if (backend.deleteFolder) {
497
+ const deleteBtn = document.createElement("button");
498
+ deleteBtn.type = "button";
499
+ addClass(deleteBtn, "yasgui-query-browser__action");
500
+ addClass(deleteBtn, "yasgui-query-browser__action--danger");
501
+ deleteBtn.textContent = "Delete";
502
+ deleteBtn.setAttribute("aria-label", `Delete folder ${entry.label}`);
503
+ deleteBtn.addEventListener("click", (e) => __awaiter(this, void 0, void 0, function* () {
504
+ e.preventDefault();
505
+ e.stopPropagation();
506
+ const ok = window.confirm(`Delete folder '${entry.label}' and everything inside it? This cannot be undone.`);
507
+ if (!ok)
508
+ return;
509
+ try {
510
+ yield backend.deleteFolder(entry.id);
511
+ this.folderEntriesById.clear();
512
+ this.invalidateRenderCache();
513
+ yield this.refresh();
514
+ }
515
+ catch (err) {
516
+ window.alert(asWorkspaceBackendError(err).message);
517
+ }
518
+ }));
519
+ actions.appendChild(deleteBtn);
520
+ }
521
+ if (actions.childElementCount > 0)
522
+ row.appendChild(actions);
523
+ return;
524
+ }
525
+ if (entry.kind !== "query")
526
+ return;
527
+ if (backend.renameQuery) {
528
+ const renameBtn = document.createElement("button");
529
+ renameBtn.type = "button";
530
+ addClass(renameBtn, "yasgui-query-browser__action");
531
+ renameBtn.textContent = "Rename";
532
+ renameBtn.setAttribute("aria-label", `Rename ${entry.label}`);
533
+ renameBtn.addEventListener("click", (e) => __awaiter(this, void 0, void 0, function* () {
534
+ var _a, _b, _c, _d, _e, _f, _g;
535
+ e.preventDefault();
536
+ e.stopPropagation();
537
+ const next = window.prompt("Rename query", entry.label);
538
+ if (!next)
539
+ return;
540
+ const trimmed = next.trim();
541
+ if (!trimmed || trimmed === entry.label)
542
+ return;
543
+ const gitRenameInfo = (() => {
544
+ if (backend.type !== "git")
545
+ return undefined;
546
+ const parts = entry.id.split("/").filter(Boolean);
547
+ parts.pop();
548
+ const folderPrefix = parts.join("/");
549
+ const safe = trimmed.replace(/[\\/]/g, "-");
550
+ const newFilename = normalizeQueryFilename(safe);
551
+ const newPath = folderPrefix ? `${folderPrefix}/${newFilename}` : newFilename;
552
+ return { oldPath: entry.id, newPath };
553
+ })();
554
+ const originalText = renameBtn.textContent;
555
+ renameBtn.disabled = true;
556
+ renameBtn.textContent = "Renaming…";
557
+ addClass(renameBtn, "loading");
558
+ try {
559
+ yield backend.renameQuery(entry.id, trimmed);
560
+ if (gitRenameInfo && gitRenameInfo.newPath && gitRenameInfo.oldPath) {
561
+ for (const tab of Object.values(this.yasgui._tabs)) {
562
+ const meta = (_b = (_a = tab).getManagedQueryMetadata) === null || _b === void 0 ? void 0 : _b.call(_a);
563
+ if (!meta)
564
+ continue;
565
+ if (meta.backendType !== "git")
566
+ continue;
567
+ if (meta.workspaceId !== this.selectedWorkspaceId)
568
+ continue;
569
+ const currentPath = (_c = meta.queryRef) === null || _c === void 0 ? void 0 : _c.path;
570
+ if (currentPath !== gitRenameInfo.oldPath)
571
+ continue;
572
+ try {
573
+ const read = yield backend.readQuery(gitRenameInfo.newPath);
574
+ const lastSavedTextHash = hashQueryText(read.queryText);
575
+ const lastSavedVersionRef = this.versionRefFromVersionTag("git", read.versionTag);
576
+ (_e = (_d = tab).setManagedQueryMetadata) === null || _e === void 0 ? void 0 : _e.call(_d, Object.assign(Object.assign({}, meta), { queryRef: Object.assign(Object.assign({}, meta.queryRef), { path: gitRenameInfo.newPath }), lastSavedTextHash,
577
+ lastSavedVersionRef }));
578
+ (_g = (_f = tab).setName) === null || _g === void 0 ? void 0 : _g.call(_f, trimmed);
579
+ }
580
+ catch (_h) {
581
+ }
582
+ }
583
+ }
584
+ this.queryPreviewById.delete(entry.id);
585
+ this.folderEntriesById.clear();
586
+ this.invalidateRenderCache();
587
+ yield this.refresh();
588
+ }
589
+ catch (err) {
590
+ renameBtn.disabled = false;
591
+ renameBtn.textContent = originalText || "Rename";
592
+ removeClass(renameBtn, "loading");
593
+ window.alert(asWorkspaceBackendError(err).message);
594
+ }
595
+ }));
596
+ actions.appendChild(renameBtn);
597
+ }
598
+ if (backend.deleteQuery) {
599
+ const deleteBtn = document.createElement("button");
600
+ deleteBtn.type = "button";
601
+ addClass(deleteBtn, "yasgui-query-browser__action");
602
+ addClass(deleteBtn, "yasgui-query-browser__action--danger");
603
+ deleteBtn.textContent = "Delete";
604
+ deleteBtn.setAttribute("aria-label", `Delete ${entry.label}`);
605
+ deleteBtn.addEventListener("click", (e) => __awaiter(this, void 0, void 0, function* () {
606
+ e.preventDefault();
607
+ e.stopPropagation();
608
+ const ok = window.confirm(`Delete '${entry.label}'? This cannot be undone.`);
609
+ if (!ok)
610
+ return;
611
+ const originalText = deleteBtn.textContent;
612
+ deleteBtn.disabled = true;
613
+ deleteBtn.textContent = "Deleting…";
614
+ addClass(deleteBtn, "loading");
615
+ try {
616
+ yield backend.deleteQuery(entry.id);
617
+ this.queryPreviewById.delete(entry.id);
618
+ this.folderEntriesById.clear();
619
+ this.invalidateRenderCache();
620
+ yield this.refresh();
621
+ }
622
+ catch (err) {
623
+ deleteBtn.disabled = false;
624
+ deleteBtn.textContent = originalText || "Delete";
625
+ removeClass(deleteBtn, "loading");
626
+ window.alert(asWorkspaceBackendError(err).message);
627
+ }
628
+ }));
629
+ actions.appendChild(deleteBtn);
630
+ }
631
+ if (actions.childElementCount > 0) {
632
+ row.appendChild(actions);
633
+ }
634
+ }
635
+ renderFlatEntries(backend, entries) {
636
+ this.clearList();
637
+ const makeIndentStyle = (depth) => `padding-left: ${depth * 16}px;`;
638
+ for (const entry of entries) {
639
+ const row = document.createElement("div");
640
+ addClass(row, "yasgui-query-browser__tree-row");
641
+ addClass(row, entry.kind === "folder" ? "yasgui-query-browser__tree-row--folder" : "yasgui-query-browser__tree-row--query");
642
+ row.setAttribute("role", "button");
643
+ row.setAttribute("tabindex", "0");
644
+ row.setAttribute("aria-label", entry.kind === "folder" ? `Open folder ${entry.label}` : `Open query ${entry.label}`);
645
+ row.setAttribute("style", makeIndentStyle(0));
646
+ if (entry.kind === "folder") {
647
+ const isExpanded = this.expandedFolderIds.has(entry.id);
648
+ const caret = document.createElement("span");
649
+ addClass(caret, "yasgui-query-browser__tree-caret");
650
+ caret.textContent = isExpanded ? "▾" : "▸";
651
+ caret.setAttribute("aria-hidden", "true");
652
+ const folderIcon = document.createElement("span");
653
+ addClass(folderIcon, "yasgui-query-browser__tree-icon");
654
+ folderIcon.textContent = "📁";
655
+ const label = document.createElement("span");
656
+ addClass(label, "yasgui-query-browser__tree-label");
657
+ label.textContent = entry.label;
658
+ row.appendChild(caret);
659
+ row.appendChild(folderIcon);
660
+ row.appendChild(label);
661
+ this.addQueryRowActions(row, backend, entry);
662
+ }
663
+ else {
664
+ const caretPlaceholder = document.createElement("span");
665
+ addClass(caretPlaceholder, "yasgui-query-browser__tree-caret");
666
+ caretPlaceholder.textContent = "▸";
667
+ caretPlaceholder.setAttribute("aria-hidden", "true");
668
+ addClass(caretPlaceholder, "yasgui-query-browser__tree-caret--placeholder");
669
+ const icon = document.createElement("span");
670
+ addClass(icon, "yasgui-query-browser__tree-icon");
671
+ icon.textContent = "🕸️";
672
+ const label = document.createElement("span");
673
+ addClass(label, "yasgui-query-browser__tree-label");
674
+ label.textContent = entry.label;
675
+ row.appendChild(caretPlaceholder);
676
+ row.appendChild(icon);
677
+ row.appendChild(label);
678
+ this.attachQueryHoverPreview(row, backend, entry.id);
679
+ this.addQueryRowActions(row, backend, entry);
680
+ }
681
+ const activate = () => __awaiter(this, void 0, void 0, function* () {
682
+ if (entry.kind === "folder") {
683
+ this.searchEl.value = "";
684
+ if (this.expandedFolderIds.has(entry.id)) {
685
+ this.expandedFolderIds.delete(entry.id);
686
+ }
687
+ else {
688
+ this.expandedFolderIds.add(entry.id);
689
+ yield this.ensureFolderLoaded(backend, entry.id);
690
+ }
691
+ yield this.refresh();
692
+ return;
693
+ }
694
+ yield this.openManagedQueryInNewTab(backend, backend.type, entry.id, entry.label);
695
+ this.close();
696
+ });
697
+ row.addEventListener("keydown", (e) => {
698
+ if (e.key === "Enter" || e.key === " ") {
699
+ e.preventDefault();
700
+ void activate();
701
+ }
702
+ });
703
+ row.addEventListener("click", () => __awaiter(this, void 0, void 0, function* () {
704
+ yield activate();
705
+ }));
706
+ this.listEl.appendChild(row);
707
+ }
708
+ }
709
+ renderTree(backend) {
710
+ this.clearList();
711
+ const makeIndentStyle = (depth) => `padding-left: ${depth * 16}px;`;
712
+ const renderFolderChildren = (folderId, depth) => {
713
+ const key = this.folderKey(folderId);
714
+ const entries = this.folderEntriesById.get(key) || [];
715
+ const folders = entries
716
+ .filter((e) => e.kind === "folder")
717
+ .sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: "base" }));
718
+ const queries = entries
719
+ .filter((e) => e.kind === "query")
720
+ .sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: "base" }));
721
+ for (const entry of [...folders, ...queries]) {
722
+ if (entry.kind === "folder") {
723
+ const isExpanded = this.expandedFolderIds.has(entry.id);
724
+ const row = document.createElement("div");
725
+ addClass(row, "yasgui-query-browser__tree-row");
726
+ addClass(row, "yasgui-query-browser__tree-row--folder");
727
+ row.setAttribute("aria-label", `${isExpanded ? "Collapse" : "Expand"} folder ${entry.label}`);
728
+ row.setAttribute("style", makeIndentStyle(depth));
729
+ row.setAttribute("role", "button");
730
+ row.setAttribute("tabindex", "0");
731
+ const caret = document.createElement("span");
732
+ addClass(caret, "yasgui-query-browser__tree-caret");
733
+ caret.textContent = isExpanded ? "▾" : "▸";
734
+ caret.setAttribute("aria-hidden", "true");
735
+ const folderIcon = document.createElement("span");
736
+ addClass(folderIcon, "yasgui-query-browser__tree-icon");
737
+ folderIcon.textContent = "📁";
738
+ const label = document.createElement("span");
739
+ addClass(label, "yasgui-query-browser__tree-label");
740
+ label.textContent = entry.label;
741
+ row.appendChild(caret);
742
+ row.appendChild(folderIcon);
743
+ row.appendChild(label);
744
+ this.addQueryRowActions(row, backend, entry);
745
+ const activate = () => __awaiter(this, void 0, void 0, function* () {
746
+ if (this.expandedFolderIds.has(entry.id)) {
747
+ this.expandedFolderIds.delete(entry.id);
748
+ yield this.refresh();
749
+ return;
750
+ }
751
+ this.expandedFolderIds.add(entry.id);
752
+ yield this.ensureFolderLoaded(backend, entry.id);
753
+ yield this.refresh();
754
+ });
755
+ row.addEventListener("keydown", (e) => {
756
+ if (e.key === "Enter" || e.key === " ") {
757
+ e.preventDefault();
758
+ void activate();
759
+ }
760
+ });
761
+ row.addEventListener("click", () => __awaiter(this, void 0, void 0, function* () {
762
+ yield activate();
763
+ }));
764
+ this.listEl.appendChild(row);
765
+ if (isExpanded) {
766
+ const childKey = this.folderKey(entry.id);
767
+ if (this.folderLoadingById.has(childKey)) {
768
+ const loading = document.createElement("div");
769
+ addClass(loading, "yasgui-query-browser__tree-meta");
770
+ loading.setAttribute("style", makeIndentStyle(depth + 1));
771
+ loading.textContent = "Loading…";
772
+ this.listEl.appendChild(loading);
773
+ }
774
+ const err = this.folderErrorById.get(childKey);
775
+ if (err) {
776
+ const error = document.createElement("div");
777
+ addClass(error, "yasgui-query-browser__tree-meta");
778
+ addClass(error, "yasgui-query-browser__tree-meta--error");
779
+ error.setAttribute("style", makeIndentStyle(depth + 1));
780
+ error.textContent = err;
781
+ this.listEl.appendChild(error);
782
+ }
783
+ renderFolderChildren(entry.id, depth + 1);
784
+ }
785
+ }
786
+ else {
787
+ const row = document.createElement("div");
788
+ addClass(row, "yasgui-query-browser__tree-row");
789
+ addClass(row, "yasgui-query-browser__tree-row--query");
790
+ row.setAttribute("aria-label", `Open query ${entry.label}`);
791
+ row.setAttribute("style", makeIndentStyle(depth));
792
+ row.setAttribute("role", "button");
793
+ row.setAttribute("tabindex", "0");
794
+ const caretPlaceholder = document.createElement("span");
795
+ addClass(caretPlaceholder, "yasgui-query-browser__tree-caret");
796
+ caretPlaceholder.textContent = "▸";
797
+ caretPlaceholder.setAttribute("aria-hidden", "true");
798
+ addClass(caretPlaceholder, "yasgui-query-browser__tree-caret--placeholder");
799
+ const icon = document.createElement("span");
800
+ addClass(icon, "yasgui-query-browser__tree-icon");
801
+ icon.textContent = "🕸️";
802
+ const label = document.createElement("span");
803
+ addClass(label, "yasgui-query-browser__tree-label");
804
+ label.textContent = entry.label;
805
+ row.appendChild(caretPlaceholder);
806
+ row.appendChild(icon);
807
+ row.appendChild(label);
808
+ this.attachQueryHoverPreview(row, backend, entry.id);
809
+ this.addQueryRowActions(row, backend, entry);
810
+ const activate = () => __awaiter(this, void 0, void 0, function* () {
811
+ yield this.openManagedQueryInNewTab(backend, backend.type, entry.id, entry.label);
812
+ this.close();
813
+ });
814
+ row.addEventListener("keydown", (e) => {
815
+ if (e.key === "Enter" || e.key === " ") {
816
+ e.preventDefault();
817
+ void activate();
818
+ }
819
+ });
820
+ row.addEventListener("click", () => __awaiter(this, void 0, void 0, function* () {
821
+ yield activate();
822
+ }));
823
+ this.listEl.appendChild(row);
824
+ }
825
+ }
826
+ };
827
+ renderFolderChildren(undefined, 0);
828
+ }
829
+ refresh() {
830
+ return __awaiter(this, void 0, void 0, function* () {
831
+ const runId = ++this.refreshRunId;
832
+ this.ensureWorkspaceSelection();
833
+ this.renderWorkspaceSelect();
834
+ const workspaces = this.getWorkspaces();
835
+ if (!this.selectedWorkspaceId || workspaces.length === 0) {
836
+ this.backButtonEl.disabled = true;
837
+ this.setStatus("");
838
+ this.clearList();
839
+ this.renderEmptyWorkspaceState();
840
+ return;
841
+ }
842
+ const workspace = workspaces.find((w) => w.id === this.selectedWorkspaceId);
843
+ if (!workspace)
844
+ return;
845
+ const backend = getWorkspaceBackend(workspace, { persistentConfig: this.yasgui.persistentConfig });
846
+ const searchQuery = this.searchEl.value;
847
+ this.backButtonEl.disabled = true;
848
+ if (this.treeWorkspaceId !== workspace.id) {
849
+ this.resetTreeState();
850
+ this.treeWorkspaceId = workspace.id;
851
+ }
852
+ try {
853
+ this.setStatus("Loading…");
854
+ const trimmed = searchQuery.trim();
855
+ if (trimmed) {
856
+ let entries;
857
+ if (backend.searchByName) {
858
+ entries = yield backend.searchByName(trimmed);
859
+ }
860
+ else {
861
+ const root = yield backend.listFolder(undefined);
862
+ entries = filterFolderEntriesByName(root, trimmed);
863
+ }
864
+ if (runId !== this.refreshRunId)
865
+ return;
866
+ const signature = entries.map((e) => this.entrySignature(e)).join("|");
867
+ this.setStatus(entries.length ? "" : "No results");
868
+ if (signature !== this.lastRenderedSignature) {
869
+ this.lastRenderedSignature = signature;
870
+ this.renderFlatEntries(backend, entries);
871
+ }
872
+ return;
873
+ }
874
+ yield this.ensureFolderLoaded(backend, undefined);
875
+ if (runId !== this.refreshRunId)
876
+ return;
877
+ const rootKey = this.folderKey(undefined);
878
+ const rootEntries = this.folderEntriesById.get(rootKey) || [];
879
+ const expandedIds = Array.from(this.expandedFolderIds).sort();
880
+ const relevantFolderKeys = [rootKey, ...expandedIds.map((id) => this.folderKey(id))].sort();
881
+ const foldersPart = relevantFolderKeys
882
+ .map((key) => {
883
+ const entries = this.folderEntriesById.get(key) || [];
884
+ const entryPart = entries
885
+ .map((e) => this.entrySignature(e))
886
+ .sort()
887
+ .join("|");
888
+ const loading = this.folderLoadingById.has(key) ? "1" : "0";
889
+ const err = this.folderErrorById.get(key) || "";
890
+ return `${key}:${loading}:${err}:${entryPart}`;
891
+ })
892
+ .join(";");
893
+ const signature = [
894
+ `rootCount:${rootEntries.length}`,
895
+ `expanded:${expandedIds.join(",")}`,
896
+ `folders:${foldersPart}`,
897
+ ].join(";");
898
+ this.setStatus(rootEntries.length ? "" : "No queries, add one by saving a tab to this workspace.");
899
+ if (signature !== this.lastRenderedSignature) {
900
+ this.lastRenderedSignature = signature;
901
+ this.renderTree(backend);
902
+ }
903
+ }
904
+ catch (e) {
905
+ if (runId !== this.refreshRunId)
906
+ return;
907
+ const err = asWorkspaceBackendError(e);
908
+ this.setStatus(this.formatStatusError(err, workspace.type));
909
+ this.clearList();
910
+ }
911
+ });
912
+ }
913
+ }
914
+ //# sourceMappingURL=QueryBrowser.js.map