@matdata/yasgui 5.17.0 → 5.19.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 (59) hide show
  1. package/build/ts/src/Tab.js +7 -1
  2. package/build/ts/src/Tab.js.map +1 -1
  3. package/build/ts/src/TabContextMenu.d.ts +1 -0
  4. package/build/ts/src/TabContextMenu.js +42 -0
  5. package/build/ts/src/TabContextMenu.js.map +1 -1
  6. package/build/ts/src/TabSettingsModal.d.ts +2 -1
  7. package/build/ts/src/TabSettingsModal.js +44 -25
  8. package/build/ts/src/TabSettingsModal.js.map +1 -1
  9. package/build/ts/src/index.d.ts +6 -0
  10. package/build/ts/src/index.js +42 -0
  11. package/build/ts/src/index.js.map +1 -1
  12. package/build/ts/src/prefixUtils.d.ts +1 -0
  13. package/build/ts/src/prefixUtils.js +23 -0
  14. package/build/ts/src/prefixUtils.js.map +1 -0
  15. package/build/ts/src/queryManagement/QueryBrowser.d.ts +8 -0
  16. package/build/ts/src/queryManagement/QueryBrowser.js +283 -116
  17. package/build/ts/src/queryManagement/QueryBrowser.js.map +1 -1
  18. package/build/ts/src/queryManagement/QueryHistoryModal.d.ts +35 -0
  19. package/build/ts/src/queryManagement/QueryHistoryModal.js +266 -0
  20. package/build/ts/src/queryManagement/QueryHistoryModal.js.map +1 -0
  21. package/build/ts/src/queryManagement/WorkspaceSettingsForm.js +8 -5
  22. package/build/ts/src/queryManagement/WorkspaceSettingsForm.js.map +1 -1
  23. package/build/ts/src/queryManagement/backends/GitWorkspaceBackend.d.ts +1 -0
  24. package/build/ts/src/queryManagement/backends/GitWorkspaceBackend.js +33 -0
  25. package/build/ts/src/queryManagement/backends/GitWorkspaceBackend.js.map +1 -1
  26. package/build/ts/src/queryManagement/backends/SparqlWorkspaceBackend.d.ts +1 -0
  27. package/build/ts/src/queryManagement/backends/SparqlWorkspaceBackend.js +3 -0
  28. package/build/ts/src/queryManagement/backends/SparqlWorkspaceBackend.js.map +1 -1
  29. package/build/ts/src/queryManagement/backends/WorkspaceBackend.d.ts +1 -0
  30. package/build/ts/src/queryManagement/countManagedQueries.d.ts +2 -0
  31. package/build/ts/src/queryManagement/countManagedQueries.js +35 -0
  32. package/build/ts/src/queryManagement/countManagedQueries.js.map +1 -0
  33. package/build/ts/src/tabNavigationHistory.d.ts +3 -0
  34. package/build/ts/src/tabNavigationHistory.js +13 -0
  35. package/build/ts/src/tabNavigationHistory.js.map +1 -0
  36. package/build/ts/src/version.d.ts +1 -1
  37. package/build/ts/src/version.js +1 -1
  38. package/build/yasgui.min.css +1 -1
  39. package/build/yasgui.min.css.map +3 -3
  40. package/build/yasgui.min.js +245 -245
  41. package/build/yasgui.min.js.map +4 -4
  42. package/package.json +1 -1
  43. package/src/Tab.ts +10 -0
  44. package/src/TabContextMenu.ts +42 -0
  45. package/src/TabSettingsModal.ts +48 -29
  46. package/src/index.ts +50 -0
  47. package/src/prefixUtils.ts +26 -0
  48. package/src/queryManagement/QueryBrowser.scss +85 -0
  49. package/src/queryManagement/QueryBrowser.ts +429 -213
  50. package/src/queryManagement/QueryHistoryModal.scss +239 -0
  51. package/src/queryManagement/QueryHistoryModal.ts +346 -0
  52. package/src/queryManagement/WorkspaceSettingsForm.ts +8 -5
  53. package/src/queryManagement/backends/GitWorkspaceBackend.ts +39 -0
  54. package/src/queryManagement/backends/SparqlWorkspaceBackend.ts +5 -0
  55. package/src/queryManagement/backends/WorkspaceBackend.ts +8 -0
  56. package/src/queryManagement/countManagedQueries.ts +28 -0
  57. package/src/tabNavigationHistory.ts +17 -0
  58. package/src/themes.scss +44 -0
  59. package/src/version.ts +1 -1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@matdata/yasgui",
3
3
  "description": "Yet Another SPARQL GUI",
4
- "version": "5.17.0",
4
+ "version": "5.19.0",
5
5
  "main": "build/yasgui.min.js",
6
6
  "types": "build/ts/src/index.d.ts",
7
7
  "license": "MIT",
package/src/Tab.ts CHANGED
@@ -436,6 +436,15 @@ export class Tab extends EventEmitter {
436
436
  private handleKeyDown = (event: KeyboardEvent) => {
437
437
  if (event.defaultPrevented) return;
438
438
 
439
+ // Ctrl+, → backward (less recently used), Ctrl+Alt+, → forward (more recently used)
440
+ // Ctrl+Tab / Ctrl+Shift+Tab cannot be used: the browser intercepts them before keydown fires
441
+ const isTabSwitchShortcut = (event.ctrlKey || event.metaKey) && !event.shiftKey && event.key === ",";
442
+ if (isTabSwitchShortcut) {
443
+ event.preventDefault();
444
+ this.yasgui.selectRecentlyUsedTab(event.altKey ? "forward" : "backward");
445
+ return;
446
+ }
447
+
439
448
  const saveModalOpen = !!document.querySelector(".saveManagedQueryModalOverlay.open");
440
449
  if (!saveModalOpen) {
441
450
  const isSaveShortcut =
@@ -1278,6 +1287,7 @@ export class Tab extends EventEmitter {
1278
1287
  createShareableLink: () => this.getShareableLink(),
1279
1288
  // Use global showSnippetsBar setting if it exists
1280
1289
  showSnippetsBar: this.yasgui.config.showSnippetsBar !== false,
1290
+ showMapButton: false,
1281
1291
  requestConfig: () => {
1282
1292
  const processedReqConfig: YasguiRequestConfig = {
1283
1293
  //setting defaults
@@ -3,6 +3,8 @@ import { default as Yasgui, getRandomId } from "./";
3
3
  import Tab from "./Tab";
4
4
  import { TabListEl } from "./TabElements";
5
5
  import { cloneDeep } from "lodash-es";
6
+ import { getWorkspaceBackend } from "./queryManagement/backends/getWorkspaceBackend";
7
+ import type { GitQueryRef, SparqlQueryRef } from "./queryManagement/types";
6
8
  import "./TabContextMenu.scss";
7
9
  export interface TabContextConfig {
8
10
  name: string;
@@ -17,6 +19,7 @@ export default class TabContextMenu {
17
19
  private duplicateTabEl!: HTMLElement;
18
20
  private saveManagedQueryEl!: HTMLElement;
19
21
  private saveAsRqFileEl!: HTMLElement;
22
+ private copyManagedQueryUriEl!: HTMLElement;
20
23
  private closeTabEl!: HTMLElement;
21
24
  private closeOtherTabsEl!: HTMLElement;
22
25
  private reOpenOldTab!: HTMLElement;
@@ -55,6 +58,8 @@ export default class TabContextMenu {
55
58
 
56
59
  this.saveAsRqFileEl = this.getMenuItemEl("Save as .rq file");
57
60
 
61
+ this.copyManagedQueryUriEl = this.getMenuItemEl("Copy URI");
62
+
58
63
  this.closeTabEl = this.getMenuItemEl("Close Tab");
59
64
 
60
65
  this.closeOtherTabsEl = this.getMenuItemEl("Close other tabs");
@@ -67,6 +72,7 @@ export default class TabContextMenu {
67
72
  dropDownList.appendChild(this.duplicateTabEl);
68
73
  dropDownList.appendChild(this.saveManagedQueryEl);
69
74
  dropDownList.appendChild(this.saveAsRqFileEl);
75
+ dropDownList.appendChild(this.copyManagedQueryUriEl);
70
76
  // Add divider
71
77
  dropDownList.appendChild(document.createElement("hr"));
72
78
  dropDownList.appendChild(this.closeTabEl);
@@ -125,6 +131,42 @@ export default class TabContextMenu {
125
131
  this.closeConfigMenu();
126
132
  };
127
133
 
134
+ // Copy URI for managed query tabs
135
+ const meta = tab?.getManagedQueryMetadata();
136
+ if (meta) {
137
+ const workspaceConfig = this.yasgui.persistentConfig?.getWorkspaces().find((w) => w.id === meta.workspaceId);
138
+ if (workspaceConfig) {
139
+ const queryId =
140
+ meta.backendType === "git"
141
+ ? (meta.queryRef as GitQueryRef).path
142
+ : (meta.queryRef as SparqlQueryRef).managedQueryIri;
143
+ if (queryId) {
144
+ const backend = getWorkspaceBackend(workspaceConfig, {
145
+ persistentConfig: this.yasgui.persistentConfig,
146
+ });
147
+ const uri = backend.getQueryUri?.(queryId);
148
+ if (uri) {
149
+ this.copyManagedQueryUriEl.onclick = async () => {
150
+ try {
151
+ await navigator.clipboard.writeText(uri);
152
+ } catch {
153
+ window.prompt("Copy this URI:", uri);
154
+ }
155
+ this.closeConfigMenu();
156
+ };
157
+ } else {
158
+ addClass(this.copyManagedQueryUriEl, "disabled");
159
+ }
160
+ } else {
161
+ addClass(this.copyManagedQueryUriEl, "disabled");
162
+ }
163
+ } else {
164
+ addClass(this.copyManagedQueryUriEl, "disabled");
165
+ }
166
+ } else {
167
+ addClass(this.copyManagedQueryUriEl, "disabled");
168
+ }
169
+
128
170
  // Close tab functionality
129
171
  this.closeTabEl.onclick = () => tab?.close();
130
172
 
@@ -5,6 +5,7 @@ import * as ConfigExportImport from "./ConfigExportImport";
5
5
  import { VERSION } from "./version";
6
6
  import * as OAuth2Utils from "./OAuth2Utils";
7
7
  import PersistentConfig from "./PersistentConfig";
8
+ import { deduplicatePrefixes } from "./prefixUtils";
8
9
  import { WorkspaceSettingsForm } from "./queryManagement/WorkspaceSettingsForm";
9
10
 
10
11
  const AcceptOptionsMap: { key: string; value: string }[] = [
@@ -37,6 +38,7 @@ export default class TabSettingsModal {
37
38
  private settingsButton!: HTMLButtonElement;
38
39
  private themeToggleButton!: HTMLButtonElement;
39
40
  private prefixButton!: HTMLButtonElement;
41
+ private mapButton!: HTMLButtonElement;
40
42
  private prefixTextarea!: HTMLTextAreaElement;
41
43
  private autoCaptureCheckbox!: HTMLInputElement;
42
44
  private hamburgerMenuButton!: HTMLButtonElement;
@@ -88,6 +90,15 @@ export default class TabSettingsModal {
88
90
  controlBarEl.appendChild(this.prefixButton);
89
91
  this.prefixButton.onclick = () => this.insertPrefixesIntoQuery();
90
92
 
93
+ // Map button
94
+ this.mapButton = document.createElement("button");
95
+ this.mapButton.setAttribute("aria-label", "Open map");
96
+ this.mapButton.title = "Open map";
97
+ this.mapButton.innerHTML = '<i class="fas fa-map-location-dot"></i>';
98
+ addClass(this.mapButton, "tabContextButton", "desktopOnly");
99
+ controlBarEl.appendChild(this.mapButton);
100
+ this.mapButton.onclick = (event: MouseEvent) => this.openMapWidget(event);
101
+
91
102
  // Hamburger menu button and dropdown (mobile only)
92
103
  const hamburgerContainer = document.createElement("div");
93
104
  addClass(hamburgerContainer, "hamburgerContainer", "mobileOnly");
@@ -147,6 +158,15 @@ export default class TabSettingsModal {
147
158
  };
148
159
  this.hamburgerDropdown.appendChild(prefixItem);
149
160
 
161
+ const mapItem = document.createElement("button");
162
+ addClass(mapItem, "hamburgerMenuItem");
163
+ mapItem.innerHTML = '<i class="fas fa-map-location-dot"></i><span>Open map</span>';
164
+ mapItem.onclick = (event) => {
165
+ this.openMapWidget(event);
166
+ this.closeHamburgerMenu();
167
+ };
168
+ this.hamburgerDropdown.appendChild(mapItem);
169
+
150
170
  // Theme toggle menu item (if enabled)
151
171
  if (this.tab.yasgui.config.showThemeToggle) {
152
172
  const themeItem = document.createElement("button");
@@ -847,7 +867,12 @@ export default class TabSettingsModal {
847
867
 
848
868
  private renderEndpointsList(container: HTMLElement) {
849
869
  container.innerHTML = "";
850
- const configs = this.tab.yasgui.persistentConfig.getEndpointConfigs();
870
+ const configs = [...this.tab.yasgui.persistentConfig.getEndpointConfigs()];
871
+ configs.sort((a, b) => {
872
+ const aKey = (a.label || a.endpoint).toLowerCase();
873
+ const bKey = (b.label || b.endpoint).toLowerCase();
874
+ return aKey.localeCompare(bKey);
875
+ });
851
876
 
852
877
  // Create table (even if empty, we'll show add form)
853
878
  const table = document.createElement("table");
@@ -1687,7 +1712,7 @@ export default class TabSettingsModal {
1687
1712
  const autoCapture = this.autoCaptureCheckbox.checked;
1688
1713
 
1689
1714
  // Parse and deduplicate prefixes
1690
- const deduplicated = this.deduplicatePrefixes(prefixText);
1715
+ const deduplicated = deduplicatePrefixes(prefixText);
1691
1716
  this.tab.yasgui.persistentConfig.setPrefixes(deduplicated);
1692
1717
  this.tab.yasgui.persistentConfig.setAutoCaptureEnabled(autoCapture);
1693
1718
 
@@ -1798,32 +1823,6 @@ export default class TabSettingsModal {
1798
1823
  this.close();
1799
1824
  }
1800
1825
 
1801
- private deduplicatePrefixes(prefixText: string): string {
1802
- const lines = prefixText.split("\n");
1803
- const seen = new Map<string, string>();
1804
- const result: string[] = [];
1805
-
1806
- for (const line of lines) {
1807
- const trimmed = line.trim();
1808
- if (!trimmed) continue;
1809
-
1810
- // Extract prefix label from line
1811
- const match = trimmed.match(/^\s*PREFIX\s+(\w+):\s*<(.+)>\s*$/i);
1812
- if (match) {
1813
- const label = match[1].toLowerCase();
1814
- if (!seen.has(label)) {
1815
- seen.set(label, trimmed);
1816
- result.push(trimmed);
1817
- }
1818
- } else {
1819
- // Keep non-prefix lines as-is
1820
- result.push(trimmed);
1821
- }
1822
- }
1823
-
1824
- return result.join("\n");
1825
- }
1826
-
1827
1826
  private insertPrefixesIntoQuery() {
1828
1827
  const yasqe = this.tab.getYasqe();
1829
1828
  if (!yasqe) return;
@@ -1866,6 +1865,13 @@ export default class TabSettingsModal {
1866
1865
  yasqe.focus();
1867
1866
  }
1868
1867
 
1868
+ private openMapWidget(event?: MouseEvent) {
1869
+ const yasqe = this.tab.getYasqe();
1870
+ if (!yasqe) return;
1871
+ const anchorPoint = event ? { x: event.clientX, y: event.clientY } : undefined;
1872
+ yasqe.toggleMapWidget(anchorPoint);
1873
+ }
1874
+
1869
1875
  public capturePrefixesFromQuery() {
1870
1876
  const autoCapture = this.tab.yasgui.persistentConfig.getAutoCaptureEnabled();
1871
1877
  if (!autoCapture) return;
@@ -1885,7 +1891,7 @@ export default class TabSettingsModal {
1885
1891
  // Merge with existing prefixes
1886
1892
  const existingPrefixes = this.tab.yasgui.persistentConfig.getPrefixes();
1887
1893
  const combined = existingPrefixes + "\n" + newPrefixLines.join("\n");
1888
- const deduplicated = this.deduplicatePrefixes(combined);
1894
+ const deduplicated = deduplicatePrefixes(combined);
1889
1895
 
1890
1896
  this.tab.yasgui.persistentConfig.setPrefixes(deduplicated);
1891
1897
  }
@@ -2078,6 +2084,19 @@ export default class TabSettingsModal {
2078
2084
  { keys: ["Esc"], description: "Exit fullscreen mode" },
2079
2085
  ],
2080
2086
  },
2087
+ {
2088
+ category: "YASGUI Tabs",
2089
+ shortcuts: [
2090
+ {
2091
+ keys: ["Ctrl+Alt+Tab", "Cmd+Alt+Tab"],
2092
+ description: "Switch to previously used YASGUI tab",
2093
+ },
2094
+ {
2095
+ keys: ["Ctrl+Alt+Shift+Tab", "Cmd+Alt+Shift+Tab"],
2096
+ description: "Switch to next YASGUI tab in recently used order",
2097
+ },
2098
+ ],
2099
+ },
2081
2100
  ];
2082
2101
 
2083
2102
  shortcutsData.forEach((section) => {
package/src/index.ts CHANGED
@@ -13,6 +13,7 @@ import { addClass, removeClass } from "@matdata/yasgui-utils";
13
13
  import GeoPlugin from "yasgui-geo-tg";
14
14
  import GraphPlugin from "@matdata/yasgui-graph-plugin";
15
15
  import TablePlugin from "@matdata/yasgui-table-plugin";
16
+ import { moveTabIdToFront, removeTabId } from "./tabNavigationHistory";
16
17
  import "@matdata/yasgui-graph-plugin/dist/yasgui-graph-plugin.min.css";
17
18
  import "@matdata/yasgui-table-plugin/dist/yasgui-table-plugin.min.css";
18
19
  import { ThemeManager, Theme } from "./ThemeManager";
@@ -155,6 +156,9 @@ export class Yasgui extends EventEmitter {
155
156
  public persistentConfig: PersistentConfig;
156
157
  public themeManager: ThemeManager;
157
158
  public queryBrowser: QueryBrowser;
159
+ private recentTabIds: string[] = [];
160
+ private navigationSnapshot: string[] | null = null;
161
+ private navigationCursor = 0;
158
162
  public static Tab = Tab;
159
163
  constructor(parent: HTMLElement, config: PartialConfig) {
160
164
  super();
@@ -185,6 +189,9 @@ export class Yasgui extends EventEmitter {
185
189
  this.tabPanelsEl = document.createElement("div");
186
190
 
187
191
  this.queryBrowser = new QueryBrowser(this);
192
+ this.on("tabClose", (_yasgui, tab) => {
193
+ this.removeTabFromRecentHistory(tab.getId());
194
+ });
188
195
 
189
196
  this.rootEl.appendChild(this.tabElements.drawTabsList());
190
197
  this.rootEl.appendChild(this.tabPanelsEl);
@@ -227,6 +234,7 @@ export class Yasgui extends EventEmitter {
227
234
  const newTab = this.addTab(true);
228
235
  this.persistentConfig.setActive(newTab.getId());
229
236
  this.emit("tabChange", this, newTab);
237
+ this.recordTabInRecentHistory(newTab.getId());
230
238
  } else {
231
239
  for (const tabId of tabs) {
232
240
  this._tabs[tabId] = new Tab(this, this.persistentConfig.getTab(tabId));
@@ -237,6 +245,7 @@ export class Yasgui extends EventEmitter {
237
245
  const activeTabId = this.persistentConfig.getActiveId();
238
246
  if (activeTabId) {
239
247
  this.markTabSelected(activeTabId);
248
+ this.recordTabInRecentHistory(activeTabId);
240
249
  if (executeIdAfterInit && executeIdAfterInit === activeTabId) {
241
250
  (this.getTab(activeTabId) as Tab).query().catch(() => {});
242
251
  }
@@ -300,6 +309,7 @@ export class Yasgui extends EventEmitter {
300
309
  const tab = this.getTab();
301
310
  if (tab && tab.getId() !== tabId) {
302
311
  if (this.markTabSelected(tabId)) {
312
+ this.recordTabInRecentHistory(tabId);
303
313
  //emit
304
314
  this.emit("tabSelect", this, tabId);
305
315
  this.persistentConfig.setActive(tabId);
@@ -307,6 +317,45 @@ export class Yasgui extends EventEmitter {
307
317
  }
308
318
  return tab;
309
319
  }
320
+ private recordTabInRecentHistory(tabId: string) {
321
+ // Any non-navigation selection commits the navigation and ends navigation mode.
322
+ this.navigationSnapshot = null;
323
+ this.recentTabIds = moveTabIdToFront(this.recentTabIds, tabId);
324
+ }
325
+ private removeTabFromRecentHistory(tabId: string) {
326
+ this.recentTabIds = removeTabId(this.recentTabIds, tabId);
327
+ }
328
+ public selectRecentlyUsedTab(direction: "backward" | "forward" = "backward"): void {
329
+ const activeTab = this.getTab();
330
+ if (!activeTab) return;
331
+ const activeTabId = activeTab.getId();
332
+
333
+ // Initialize (or re-initialize) the navigation snapshot when starting a new navigation
334
+ // sequence or when the active tab no longer matches the cursor position.
335
+ // Snapshotting the history order lets repeated presses cycle through all tabs
336
+ // instead of ping-ponging between the two most-recently-used ones.
337
+ if (this.navigationSnapshot === null || this.navigationSnapshot[this.navigationCursor] !== activeTabId) {
338
+ this.navigationSnapshot = [...this.recentTabIds];
339
+ const activeIdx = this.navigationSnapshot.indexOf(activeTabId);
340
+ this.navigationCursor = activeIdx >= 0 ? activeIdx : 0;
341
+ }
342
+
343
+ if (direction === "backward") {
344
+ this.navigationCursor = Math.min(this.navigationCursor + 1, this.navigationSnapshot.length - 1);
345
+ } else {
346
+ this.navigationCursor = Math.max(this.navigationCursor - 1, 0);
347
+ }
348
+
349
+ const nextTabId = this.navigationSnapshot[this.navigationCursor];
350
+ if (!nextTabId || nextTabId === activeTabId) return;
351
+
352
+ // Select the tab without updating recentTabIds. The history is committed when the
353
+ // user performs any non-navigation action that calls recordTabInRecentHistory.
354
+ if (this.markTabSelected(nextTabId)) {
355
+ this.emit("tabSelect", this, nextTabId);
356
+ this.persistentConfig.setActive(nextTabId);
357
+ }
358
+ }
310
359
  /**
311
360
  * Checks if two persistent tab configuration are the same based.
312
361
  * It isnt a strict equality, as falsy values (e.g. a header that isnt set in one tabjson) isnt taken into consideration
@@ -417,6 +466,7 @@ export class Yasgui extends EventEmitter {
417
466
  if (setActive) {
418
467
  this.persistentConfig.setActive(tabId);
419
468
  this._tabs[tabId].show();
469
+ this.recordTabInRecentHistory(tabId);
420
470
  }
421
471
  return this._tabs[tabId];
422
472
  }
@@ -0,0 +1,26 @@
1
+ export function deduplicatePrefixes(prefixText: string): string {
2
+ const lines = prefixText.split("\n");
3
+ const seen = new Set<string>();
4
+ const result: string[] = [];
5
+
6
+ for (const line of lines) {
7
+ const trimmed = line.trim();
8
+ if (!trimmed) continue;
9
+
10
+ // Allow an empty prefix label so the default prefix form `PREFIX : <...>` is
11
+ // deduplicated together with named prefixes, while keeping the original
12
+ // word-character restriction for named labels.
13
+ const match = trimmed.match(/^\s*PREFIX\s+(\w*):\s*<([^>]+)>\s*$/i);
14
+ if (match) {
15
+ const label = match[1].toLowerCase();
16
+ if (!seen.has(label)) {
17
+ seen.add(label);
18
+ result.push(trimmed);
19
+ }
20
+ } else {
21
+ result.push(trimmed);
22
+ }
23
+ }
24
+
25
+ return result.join("\n");
26
+ }
@@ -325,6 +325,91 @@
325
325
  white-space: nowrap;
326
326
  }
327
327
 
328
+ &__context-menu {
329
+ position: fixed;
330
+ z-index: 10001;
331
+ list-style: none;
332
+ padding: 4px 0;
333
+ margin: 0;
334
+ min-width: 140px;
335
+ background: var(--yasgui-bg-primary, white);
336
+ border: 1px solid var(--yasgui-border-color, #e0e0e0);
337
+ border-radius: 4px;
338
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
339
+ }
340
+
341
+ &__context-menu-item {
342
+ padding: 7px 16px;
343
+ cursor: pointer;
344
+ white-space: nowrap;
345
+ color: var(--yasgui-text-primary, #000);
346
+ font-size: 13px;
347
+ line-height: 1.4;
348
+
349
+ &:hover {
350
+ background: var(--yasgui-bg-secondary, #f5f5f5);
351
+ color: var(--yasgui-button-hover, #000);
352
+ }
353
+
354
+ &--danger {
355
+ color: var(--yasgui-error-color, #c00);
356
+
357
+ &:hover {
358
+ background: var(--yasgui-bg-secondary, #f5f5f5);
359
+ color: var(--yasgui-error-color, #c00);
360
+ }
361
+ }
362
+ }
363
+
364
+ &__rename-input {
365
+ flex: 1;
366
+ min-width: 0;
367
+ font: inherit;
368
+ font-size: 13px;
369
+ color: var(--yasgui-text-primary, #000);
370
+ background: var(--yasgui-bg-primary, white);
371
+ border: 1px solid var(--yasgui-accent-color, #337ab7);
372
+ border-radius: 3px;
373
+ padding: 1px 4px;
374
+ outline: none;
375
+ box-shadow: 0 0 0 2px rgba(51, 122, 183, 0.25);
376
+ }
377
+
378
+ &__inline-confirm {
379
+ flex: 1;
380
+ font-size: 13px;
381
+ color: var(--yasgui-text-primary, #000);
382
+ white-space: nowrap;
383
+ overflow: hidden;
384
+ text-overflow: ellipsis;
385
+ }
386
+
387
+ &__inline-confirm-btn {
388
+ appearance: none;
389
+ background: transparent;
390
+ border: 1px solid var(--yasgui-border-color, #e0e0e0);
391
+ border-radius: 3px;
392
+ padding: 1px 8px;
393
+ font: inherit;
394
+ font-size: 12px;
395
+ cursor: pointer;
396
+ color: var(--yasgui-text-primary, #000);
397
+ flex-shrink: 0;
398
+
399
+ &:hover {
400
+ background: var(--yasgui-bg-secondary, #f5f5f5);
401
+ }
402
+
403
+ &--danger {
404
+ color: var(--yasgui-error-color, #c00);
405
+ border-color: var(--yasgui-error-color, #c00);
406
+
407
+ &:hover {
408
+ background: rgba(204, 0, 0, 0.08);
409
+ }
410
+ }
411
+ }
412
+
328
413
  &__tree-meta {
329
414
  padding: 4px 8px;
330
415
  font-size: 13px;