From d86a08806f4d28bb85601606a3ee80709f0bbc3a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 11 May 2026 11:46:44 +0000 Subject: [PATCH 1/5] Initial plan From 53e609f99822b196b2ea7c599804b670add41d5c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 11 May 2026 11:53:42 +0000 Subject: [PATCH 2/5] feat: add query history modal to managed query right-click menu - Add QueryHistoryModal.ts: modal to list versions, preview text, and open historical version in a new linked tab - Add QueryHistoryModal.scss: responsive split-panel styles for the history dialog - QueryBrowser.ts: import modal, add private field, wire 'History' item in openQueryContextMenu - docs/user-guide.md: document 'View version history of a managed query' feature Agent-Logs-Url: https://github.com/Matdata-eu/Yasgui/sessions/aec69354-f6b3-4b13-8808-88827fb8970c --- docs/user-guide.md | 7 + .../src/queryManagement/QueryBrowser.ts | 11 + .../queryManagement/QueryHistoryModal.scss | 239 ++++++++++++ .../src/queryManagement/QueryHistoryModal.ts | 346 ++++++++++++++++++ 4 files changed, 603 insertions(+) create mode 100644 packages/yasgui/src/queryManagement/QueryHistoryModal.scss create mode 100644 packages/yasgui/src/queryManagement/QueryHistoryModal.ts diff --git a/docs/user-guide.md b/docs/user-guide.md index 93c91ae5..53f5da18 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -1186,6 +1186,13 @@ The token must have access to the specific repository you're configuring as a wo - For SPARQL workspaces the URI is the managed-query IRI stored in the triplestore - For Git workspaces the URI is the browser-accessible link to the file in the repository (GitHub/GitLab/Bitbucket/Gitea) +**View version history of a managed query:** + +- In the **Query Browser**: right-click any query and choose **History** +- A dialog opens listing all saved versions of that query (newest first), each showing its date, author, and save message +- Click a version in the list to preview its SPARQL text on the right side of the dialog +- Click **Open in tab** to open the selected historical version in a new tab linked to the managed query; from there you can edit and re-save it to create a new version + **Credentials:** - Tokens are stored locally, not saved together with the managed query and are not re-displayed after entry diff --git a/packages/yasgui/src/queryManagement/QueryBrowser.ts b/packages/yasgui/src/queryManagement/QueryBrowser.ts index 7fa9c8b8..ad7488fe 100644 --- a/packages/yasgui/src/queryManagement/QueryBrowser.ts +++ b/packages/yasgui/src/queryManagement/QueryBrowser.ts @@ -9,6 +9,7 @@ import { hashQueryText } from "./textHash"; import type { BackendType, VersionRef, ManagedTabMetadata } from "./types"; import { normalizeQueryFilename } from "./normalizeQueryFilename"; import SaveManagedQueryModal from "./SaveManagedQueryModal"; +import QueryHistoryModal from "./QueryHistoryModal"; import "./QueryBrowser.scss"; @@ -53,6 +54,8 @@ export default class QueryBrowser { private queryContextMenuEl?: HTMLElement; private queryContextMenuCleanup?: () => void; + private queryHistoryModal?: QueryHistoryModal; + private entrySignature(entry: FolderEntry): string { const parent = entry.parentId || ""; // Include label + parent so renames/moves force a re-render. @@ -719,6 +722,14 @@ export default class QueryBrowser { } } + // History + menu.appendChild( + makeItem("History", async () => { + if (!this.queryHistoryModal) this.queryHistoryModal = new QueryHistoryModal(this.yasgui); + await this.queryHistoryModal.show(this.selectedWorkspaceId!, backend, backend.type, entry.id, entry.label); + }), + ); + // Rename if (backend.renameQuery) { const renameQuery = backend.renameQuery.bind(backend); diff --git a/packages/yasgui/src/queryManagement/QueryHistoryModal.scss b/packages/yasgui/src/queryManagement/QueryHistoryModal.scss new file mode 100644 index 00000000..b314851e --- /dev/null +++ b/packages/yasgui/src/queryManagement/QueryHistoryModal.scss @@ -0,0 +1,239 @@ +.queryHistoryModalOverlay { + display: none; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: var(--yasgui-overlay-bg, rgba(0, 0, 0, 0.5)); + z-index: 10000; + align-items: center; + justify-content: center; + + &.open { + display: flex; + } +} + +.queryHistoryModal { + background: var(--yasgui-bg-primary, white); + border-radius: 8px; + width: 860px; + max-width: 95vw; + max-height: 90vh; + display: flex; + flex-direction: column; + overflow: hidden; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); +} + +.queryHistoryModalHeader { + display: flex; + justify-content: space-between; + align-items: center; + padding: 16px; + border-bottom: 1px solid var(--yasgui-border-color, #e0e0e0); + flex-shrink: 0; + + h2 { + margin: 0; + font-size: 18px; + font-weight: 600; + color: var(--yasgui-text-primary, #000); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .closeButton { + background: none; + border: none; + font-size: 28px; + line-height: 1; + cursor: pointer; + color: var(--yasgui-text-secondary, #666); + padding: 0; + width: 32px; + height: 32px; + flex-shrink: 0; + + &:hover { + color: var(--yasgui-text-primary, #333); + } + } +} + +.queryHistoryModalBody { + display: flex; + flex: 1; + overflow: hidden; + min-height: 0; +} + +.queryHistoryVersionsPanel { + width: 280px; + min-width: 200px; + flex-shrink: 0; + border-right: 1px solid var(--yasgui-border-color, #e0e0e0); + display: flex; + flex-direction: column; + overflow: hidden; +} + +.queryHistoryPreviewPanel { + flex: 1; + display: flex; + flex-direction: column; + overflow: hidden; + min-width: 0; +} + +.queryHistoryPanelTitle { + padding: 10px 12px 8px; + font-size: 12px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--yasgui-text-secondary, #666); + border-bottom: 1px solid var(--yasgui-border-color, #e0e0e0); + flex-shrink: 0; +} + +.queryHistoryLoading, +.queryHistoryError, +.queryHistoryEmpty { + padding: 12px; + font-size: 13px; + color: var(--yasgui-text-secondary, #666); +} + +.queryHistoryError { + color: var(--yasgui-error-color, #c00); +} + +.queryHistoryVersionsList { + flex: 1; + overflow-y: auto; + padding: 6px 0; +} + +.queryHistoryVersionItem { + padding: 10px 12px; + cursor: pointer; + border-bottom: 1px solid var(--yasgui-border-color, #e0e0e0); + outline: none; + + &:last-child { + border-bottom: none; + } + + &:hover { + background: var(--yasgui-bg-secondary, #f5f5f5); + } + + &:focus-visible { + background: var(--yasgui-bg-secondary, #f5f5f5); + box-shadow: inset 0 0 0 2px var(--yasgui-accent-color, #337ab7); + } + + &--selected { + background: var(--yasgui-accent-bg, #e8f0fe); + border-left: 3px solid var(--yasgui-accent-color, #337ab7); + padding-left: 9px; + + &:hover { + background: var(--yasgui-accent-bg, #e8f0fe); + } + } +} + +.queryHistoryVersionDate { + font-size: 13px; + font-weight: 600; + color: var(--yasgui-text-primary, #000); +} + +.queryHistoryVersionMeta { + font-size: 12px; + color: var(--yasgui-text-secondary, #666); + margin-top: 2px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.queryHistoryVersionMessage { + font-size: 12px; + color: var(--yasgui-text-secondary, #555); + margin-top: 2px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-style: italic; +} + +.queryHistoryPreviewEmpty { + padding: 24px 16px; + font-size: 13px; + color: var(--yasgui-text-secondary, #999); + text-align: center; + flex: 1; + display: flex; + align-items: center; + justify-content: center; +} + +.queryHistoryPreviewContent { + flex: 1; + margin: 0; + padding: 12px 16px; + overflow: auto; + font-family: "SFMono-Regular", "Consolas", "Liberation Mono", monospace; + font-size: 12px; + line-height: 1.5; + color: var(--yasgui-text-primary, #000); + background: var(--yasgui-bg-secondary, #f7f7f7); + white-space: pre-wrap; + word-break: break-word; + border-top: 1px solid var(--yasgui-border-color, #e0e0e0); +} + +.queryHistoryModalFooter { + display: flex; + justify-content: flex-end; + gap: 8px; + padding: 12px 16px; + border-top: 1px solid var(--yasgui-border-color, #e0e0e0); + flex-shrink: 0; + + button { + padding: 8px 16px; + border-radius: 4px; + border: 1px solid var(--yasgui-border-color, #e0e0e0); + background: var(--yasgui-bg-primary, white); + color: var(--yasgui-text-primary, #000); + font-size: 14px; + cursor: pointer; + + &:hover:not(:disabled) { + background: var(--yasgui-bg-secondary, #f5f5f5); + } + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + } + + &.primary { + background: var(--yasgui-accent-color, #337ab7); + color: #fff; + border-color: var(--yasgui-accent-color, #337ab7); + font-weight: 600; + + &:hover:not(:disabled) { + background: var(--yasgui-accent-color-hover, #2a6496); + border-color: var(--yasgui-accent-color-hover, #2a6496); + } + } + } +} diff --git a/packages/yasgui/src/queryManagement/QueryHistoryModal.ts b/packages/yasgui/src/queryManagement/QueryHistoryModal.ts new file mode 100644 index 00000000..5e62bea3 --- /dev/null +++ b/packages/yasgui/src/queryManagement/QueryHistoryModal.ts @@ -0,0 +1,346 @@ +import type Yasgui from "../index"; +import { addClass, removeClass } from "@matdata/yasgui-utils"; +import { getWorkspaceBackend } from "./backends/getWorkspaceBackend"; +import type { BackendType, ManagedTabMetadata, ReadResult, VersionInfo, VersionRef } from "./types"; +import { hashQueryText } from "./textHash"; +import { getEndpointToAutoSwitch } from "./openManagedQuery"; +import { asWorkspaceBackendError } from "./backends/errors"; + +import "./QueryHistoryModal.scss"; + +export default class QueryHistoryModal { + private yasgui: Yasgui; + + private overlayEl: HTMLDivElement; + private modalEl: HTMLDivElement; + private titleEl: HTMLHeadingElement; + private versionsListEl: HTMLDivElement; + private loadingEl: HTMLDivElement; + private errorEl: HTMLDivElement; + private previewEmptyEl: HTMLDivElement; + private previewContentEl: HTMLPreElement; + private openTabBtn: HTMLButtonElement; + + private workspaceId?: string; + private backend?: ReturnType; + private backendType?: BackendType; + private queryId?: string; + private queryLabel?: string; + + private selectedVersionId?: string; + private selectedVersionRead?: ReadResult; + + private mouseDownOnOverlay = false; + private resolve?: () => void; + + constructor(yasgui: Yasgui) { + this.yasgui = yasgui; + + this.overlayEl = document.createElement("div"); + addClass(this.overlayEl, "queryHistoryModalOverlay"); + + this.modalEl = document.createElement("div"); + addClass(this.modalEl, "queryHistoryModal"); + this.modalEl.setAttribute("role", "dialog"); + this.modalEl.setAttribute("aria-modal", "true"); + this.modalEl.addEventListener("click", (e) => e.stopPropagation()); + + // ── Header ─────────────────────────────────────────────────────────────── + const headerEl = document.createElement("div"); + addClass(headerEl, "queryHistoryModalHeader"); + + this.titleEl = document.createElement("h2"); + this.titleEl.textContent = "Query History"; + + const closeBtn = document.createElement("button"); + closeBtn.type = "button"; + closeBtn.className = "closeButton"; + closeBtn.setAttribute("aria-label", "Close"); + closeBtn.innerHTML = "×"; + closeBtn.addEventListener("click", () => this.close()); + + headerEl.appendChild(this.titleEl); + headerEl.appendChild(closeBtn); + + // ── Body ───────────────────────────────────────────────────────────────── + const bodyEl = document.createElement("div"); + addClass(bodyEl, "queryHistoryModalBody"); + + // Left panel – version list + const versionsPanel = document.createElement("div"); + addClass(versionsPanel, "queryHistoryVersionsPanel"); + + const versionsPanelTitle = document.createElement("div"); + addClass(versionsPanelTitle, "queryHistoryPanelTitle"); + versionsPanelTitle.textContent = "Versions"; + + this.loadingEl = document.createElement("div"); + addClass(this.loadingEl, "queryHistoryLoading"); + this.loadingEl.textContent = "Loading versions…"; + + this.errorEl = document.createElement("div"); + addClass(this.errorEl, "queryHistoryError"); + + this.versionsListEl = document.createElement("div"); + addClass(this.versionsListEl, "queryHistoryVersionsList"); + + versionsPanel.appendChild(versionsPanelTitle); + versionsPanel.appendChild(this.loadingEl); + versionsPanel.appendChild(this.errorEl); + versionsPanel.appendChild(this.versionsListEl); + + // Right panel – preview + const previewPanel = document.createElement("div"); + addClass(previewPanel, "queryHistoryPreviewPanel"); + + const previewPanelTitle = document.createElement("div"); + addClass(previewPanelTitle, "queryHistoryPanelTitle"); + previewPanelTitle.textContent = "Preview"; + + this.previewEmptyEl = document.createElement("div"); + addClass(this.previewEmptyEl, "queryHistoryPreviewEmpty"); + this.previewEmptyEl.textContent = "Select a version to preview"; + + this.previewContentEl = document.createElement("pre"); + addClass(this.previewContentEl, "queryHistoryPreviewContent"); + + previewPanel.appendChild(previewPanelTitle); + previewPanel.appendChild(this.previewEmptyEl); + previewPanel.appendChild(this.previewContentEl); + + bodyEl.appendChild(versionsPanel); + bodyEl.appendChild(previewPanel); + + // ── Footer ──────────────────────────────────────────────────────────────── + const footerEl = document.createElement("div"); + addClass(footerEl, "queryHistoryModalFooter"); + + const cancelBtn = document.createElement("button"); + cancelBtn.type = "button"; + cancelBtn.textContent = "Close"; + cancelBtn.addEventListener("click", () => this.close()); + + this.openTabBtn = document.createElement("button"); + this.openTabBtn.type = "button"; + this.openTabBtn.textContent = "Open in tab"; + addClass(this.openTabBtn, "primary"); + this.openTabBtn.disabled = true; + this.openTabBtn.addEventListener("click", () => void this.openSelectedVersionInTab()); + + footerEl.appendChild(cancelBtn); + footerEl.appendChild(this.openTabBtn); + + // ── Assemble ────────────────────────────────────────────────────────────── + this.modalEl.appendChild(headerEl); + this.modalEl.appendChild(bodyEl); + this.modalEl.appendChild(footerEl); + + this.overlayEl.appendChild(this.modalEl); + + this.overlayEl.addEventListener("mousedown", (e) => { + if (e.target === this.overlayEl) this.mouseDownOnOverlay = true; + }); + this.overlayEl.addEventListener("mouseup", (e) => { + if (e.target === this.overlayEl && this.mouseDownOnOverlay) this.close(); + this.mouseDownOnOverlay = false; + }); + + document.addEventListener("keydown", (e) => { + if (!this.overlayEl.classList.contains("open")) return; + if (e.key === "Escape") { + e.stopPropagation(); + this.close(); + } + }); + } + + /** + * Show the history modal for the given managed query. + * Returns a promise that resolves when the modal is closed. + */ + public show( + workspaceId: string, + backend: ReturnType, + backendType: BackendType, + queryId: string, + queryLabel: string, + ): Promise { + this.workspaceId = workspaceId; + this.backend = backend; + this.backendType = backendType; + this.queryId = queryId; + this.queryLabel = queryLabel; + + // Reset state + this.selectedVersionId = undefined; + this.selectedVersionRead = undefined; + this.versionsListEl.innerHTML = ""; + this.previewContentEl.textContent = ""; + this.previewContentEl.style.display = "none"; + this.previewEmptyEl.style.display = "block"; + this.loadingEl.style.display = "block"; + this.errorEl.textContent = ""; + this.errorEl.style.display = "none"; + this.openTabBtn.disabled = true; + this.titleEl.textContent = `History: ${queryLabel}`; + this.mouseDownOnOverlay = false; + + document.body.appendChild(this.overlayEl); + addClass(this.overlayEl, "open"); + + void this.loadVersions(); + + return new Promise((resolve) => { + this.resolve = resolve; + }); + } + + private close() { + removeClass(this.overlayEl, "open"); + this.overlayEl.remove(); + const resolve = this.resolve; + this.resolve = undefined; + resolve?.(); + } + + private async loadVersions() { + if (!this.backend || !this.queryId) return; + + try { + const versions = await this.backend.listVersions(this.queryId); + this.loadingEl.style.display = "none"; + this.renderVersions(versions); + } catch (err) { + this.loadingEl.style.display = "none"; + this.errorEl.textContent = `Failed to load versions: ${asWorkspaceBackendError(err).message}`; + this.errorEl.style.display = "block"; + } + } + + private renderVersions(versions: VersionInfo[]) { + this.versionsListEl.innerHTML = ""; + + if (versions.length === 0) { + const emptyEl = document.createElement("div"); + addClass(emptyEl, "queryHistoryEmpty"); + emptyEl.textContent = "No version history found."; + this.versionsListEl.appendChild(emptyEl); + return; + } + + for (const version of versions) { + const item = document.createElement("div"); + addClass(item, "queryHistoryVersionItem"); + item.setAttribute("role", "button"); + item.setAttribute("tabindex", "0"); + + const dateEl = document.createElement("div"); + addClass(dateEl, "queryHistoryVersionDate"); + dateEl.textContent = this.formatDate(version.createdAt); + item.appendChild(dateEl); + + if (version.author) { + const authorEl = document.createElement("div"); + addClass(authorEl, "queryHistoryVersionMeta"); + authorEl.textContent = version.author; + item.appendChild(authorEl); + } + + if (version.message) { + const msgEl = document.createElement("div"); + addClass(msgEl, "queryHistoryVersionMessage"); + msgEl.textContent = version.message; + item.appendChild(msgEl); + } + + const selectFn = () => void this.selectVersion(version.id, item); + item.addEventListener("click", selectFn); + item.addEventListener("keydown", (e) => { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + selectFn(); + } + }); + + this.versionsListEl.appendChild(item); + } + } + + private async selectVersion(versionId: string, itemEl: HTMLElement) { + // Highlight the selected item + this.versionsListEl.querySelectorAll(".queryHistoryVersionItem").forEach((el) => { + removeClass(el as HTMLElement, "queryHistoryVersionItem--selected"); + }); + addClass(itemEl, "queryHistoryVersionItem--selected"); + + // Reset selection state and show loading in preview + this.selectedVersionId = undefined; + this.selectedVersionRead = undefined; + this.openTabBtn.disabled = true; + this.previewEmptyEl.style.display = "none"; + this.previewContentEl.style.display = "block"; + this.previewContentEl.textContent = "Loading…"; + + if (!this.backend || !this.queryId) return; + + try { + const read = await this.backend.readVersion(this.queryId, versionId); + this.selectedVersionId = versionId; + this.selectedVersionRead = read; + this.previewContentEl.textContent = read.queryText; + this.openTabBtn.disabled = false; + } catch (err) { + this.previewContentEl.textContent = `Error: ${asWorkspaceBackendError(err).message}`; + } + } + + private async openSelectedVersionInTab() { + if ( + !this.selectedVersionId || + !this.selectedVersionRead || + !this.workspaceId || + !this.backendType || + !this.queryId + ) { + return; + } + + const { workspaceId, backendType, queryId, queryLabel } = this; + const read = this.selectedVersionRead; + + const tab = this.yasgui.addTab(true); + if (queryLabel) tab.setName(queryLabel); + + const endpoint = getEndpointToAutoSwitch(backendType, read); + if (endpoint) tab.setEndpoint(endpoint); + + tab.setQuery(read.queryText); + + const versionRef: VersionRef | undefined = this.versionRefFromVersionTag(backendType, read.versionTag); + + const managedMetadata: ManagedTabMetadata = { + workspaceId, + backendType, + queryRef: backendType === "git" ? { path: queryId } : { managedQueryIri: queryId }, + lastSavedVersionRef: versionRef, + lastSavedTextHash: hashQueryText(read.queryText), + }; + tab.setManagedQueryMetadata(managedMetadata); + + this.close(); + } + + private versionRefFromVersionTag(backendType: BackendType, versionTag: string | undefined): VersionRef | undefined { + if (!versionTag) return undefined; + if (backendType === "git") return { commitSha: versionTag }; + return { managedQueryVersionIri: versionTag }; + } + + private formatDate(isoDate: string): string { + try { + return new Date(isoDate).toLocaleString(); + } catch { + return isoDate; + } + } +} From 2a501e18c5db2e8fb6bd63e8623351f29278028b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 11 May 2026 11:56:03 +0000 Subject: [PATCH 3/5] fix: address code review feedback in QueryHistoryModal - Move document keydown listener to show()/close() to avoid memory leak - Fix invalid date detection using isNaN(d.getTime()) instead of try/catch Agent-Logs-Url: https://github.com/Matdata-eu/Yasgui/sessions/aec69354-f6b3-4b13-8808-88827fb8970c --- .../src/queryManagement/QueryHistoryModal.ts | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/yasgui/src/queryManagement/QueryHistoryModal.ts b/packages/yasgui/src/queryManagement/QueryHistoryModal.ts index 5e62bea3..41c48d0b 100644 --- a/packages/yasgui/src/queryManagement/QueryHistoryModal.ts +++ b/packages/yasgui/src/queryManagement/QueryHistoryModal.ts @@ -144,16 +144,16 @@ export default class QueryHistoryModal { if (e.target === this.overlayEl && this.mouseDownOnOverlay) this.close(); this.mouseDownOnOverlay = false; }); - - document.addEventListener("keydown", (e) => { - if (!this.overlayEl.classList.contains("open")) return; - if (e.key === "Escape") { - e.stopPropagation(); - this.close(); - } - }); } + /** Bound keydown handler stored so it can be removed on close. */ + private readonly onKeyDown = (e: KeyboardEvent) => { + if (e.key === "Escape") { + e.stopPropagation(); + this.close(); + } + }; + /** * Show the history modal for the given managed query. * Returns a promise that resolves when the modal is closed. @@ -187,6 +187,7 @@ export default class QueryHistoryModal { document.body.appendChild(this.overlayEl); addClass(this.overlayEl, "open"); + document.addEventListener("keydown", this.onKeyDown); void this.loadVersions(); @@ -196,6 +197,7 @@ export default class QueryHistoryModal { } private close() { + document.removeEventListener("keydown", this.onKeyDown); removeClass(this.overlayEl, "open"); this.overlayEl.remove(); const resolve = this.resolve; @@ -337,10 +339,8 @@ export default class QueryHistoryModal { } private formatDate(isoDate: string): string { - try { - return new Date(isoDate).toLocaleString(); - } catch { - return isoDate; - } + const d = new Date(isoDate); + if (isNaN(d.getTime())) return isoDate; + return d.toLocaleString(); } } From fb6eb5487f5adf61e4de1e0d6f47c1f34b542882 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 11 May 2026 11:57:44 +0000 Subject: [PATCH 4/5] fix: use 'Unknown date' fallback for unparseable version timestamps Agent-Logs-Url: https://github.com/Matdata-eu/Yasgui/sessions/aec69354-f6b3-4b13-8808-88827fb8970c --- packages/yasgui/src/queryManagement/QueryHistoryModal.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/yasgui/src/queryManagement/QueryHistoryModal.ts b/packages/yasgui/src/queryManagement/QueryHistoryModal.ts index 41c48d0b..eac4e0e3 100644 --- a/packages/yasgui/src/queryManagement/QueryHistoryModal.ts +++ b/packages/yasgui/src/queryManagement/QueryHistoryModal.ts @@ -340,7 +340,7 @@ export default class QueryHistoryModal { private formatDate(isoDate: string): string { const d = new Date(isoDate); - if (isNaN(d.getTime())) return isoDate; + if (isNaN(d.getTime())) return "Unknown date"; return d.toLocaleString(); } } From e4561b344f2afd95df05fc26d2b1b96ff1102f7d Mon Sep 17 00:00:00 2001 From: Mathias Vanden Auweele Date: Mon, 11 May 2026 13:58:46 +0200 Subject: [PATCH 5/5] fix: color --- packages/yasgui/src/themes.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/yasgui/src/themes.scss b/packages/yasgui/src/themes.scss index afd8555d..01cd8876 100644 --- a/packages/yasgui/src/themes.scss +++ b/packages/yasgui/src/themes.scss @@ -16,6 +16,7 @@ --yasgui-link-color: #337ab7; --yasgui-link-hover: #2a6496; --yasgui-accent-color: #337ab7; + --yasgui-accent-bg: #e8f0fe; --yasgui-button-text: #505050; --yasgui-button-hover: #000; --yasgui-chip-bg: #f5f5f5; @@ -66,6 +67,7 @@ --yasgui-link-color: #58a6ff; --yasgui-link-hover: #79c0ff; --yasgui-accent-color: #58a6ff; + --yasgui-accent-bg: #1c2d41; --yasgui-button-text: #c9d1d9; --yasgui-button-hover: #f0f6fc; --yasgui-chip-bg: #21262d;