From 9c20253fa42d6b0d62ea5811c18542ec88c330bb Mon Sep 17 00:00:00 2001 From: marvin Date: Wed, 16 Sep 2026 15:21:54 -0400 Subject: [PATCH] Replace inline error-breakdown expand with a themed popup (v0.11.114) --- package.json | 2 +- src/public/app.js | 8 ++++++-- src/public/index.html | 4 ++-- src/public/styles.css | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index a44aad0..3c71f54 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "site-gateway", - "version": "0.11.113", + "version": "0.11.114", "private": true, "description": "Site Gateway: simple self-hosted website publishing, reverse proxying, and automatic HTTPS.", "type": "module", diff --git a/src/public/app.js b/src/public/app.js index 571d000..cbe1246 100644 --- a/src/public/app.js +++ b/src/public/app.js @@ -338,8 +338,8 @@ function renderPerformance() { $("#performance-sparkline").innerHTML = points.length ? `${gridLines}` : ""; if (!points.length) $("#performance-sparkline-labels").innerHTML = 'No request data for this window yet.'; const routes = data.routes || []; - const countCell = (count, errors, breakdown) => { if (!errors) return `${count.toLocaleString()}`; if (!breakdown?.length) return `${count.toLocaleString()} · ${errors.toLocaleString()}`; const rows = breakdown.map(item => `
${escapeHtml(item.status)}${item.count.toLocaleString()}
`).join(""); return `${count.toLocaleString()} ·
${errors.toLocaleString()}
${rows}
`; }; - $("#performance-rows").innerHTML = routes.length ? routes.map(route => `${escapeHtml(route.host)}${countCell(route.hourRequests, route.hourErrors)}${countCell(route.dayRequests, route.dayErrors, route.errorBreakdown)}${route.dayAvgMs == null ? "—" : `${route.dayAvgMs} ms`}`).join("") : 'No requests have been logged yet.'; + const countCell = (count, errors, breakdown, host) => { if (!errors) return `${count.toLocaleString()}`; if (!breakdown?.length) return `${count.toLocaleString()} · ${errors.toLocaleString()}`; return `${count.toLocaleString()} · `; }; + $("#performance-rows").innerHTML = routes.length ? routes.map(route => `${escapeHtml(route.host)}${countCell(route.hourRequests, route.hourErrors)}${countCell(route.dayRequests, route.dayErrors, route.errorBreakdown, route.host)}${route.dayAvgMs == null ? "—" : `${route.dayAvgMs} ms`}`).join("") : 'No requests have been logged yet.'; if (selected) $(`#performance-rows tr.row-highlight`)?.scrollIntoView({ block: "nearest" }); } @@ -513,6 +513,10 @@ $("#refresh-logs").addEventListener("click", () => loadFeatureView().catch(error $("#log-host").addEventListener("change", () => loadFeatureView().catch(error => toast(error.message))); $("#performance-host").addEventListener("change", () => loadFeatureView().catch(error => toast(error.message))); $("#performance-range").addEventListener("change", () => loadFeatureView().catch(error => toast(error.message))); + +// --- Performance: themed error-breakdown popup, replacing the old hover tooltip ------------ +function showErrorBreakdown(host, total, breakdown) { let dialog = document.querySelector("#error-breakdown-dialog"); if (!dialog) { dialog = document.createElement("dialog"); dialog.id = "error-breakdown-dialog"; document.body.append(dialog); } const rows = breakdown.map(item => `
${escapeHtml(item.status)}${item.count.toLocaleString()}
`).join(""); dialog.innerHTML = `

Performance · Last 24h

${escapeHtml(host)}

${total.toLocaleString()} error response${total === 1 ? "" : "s"} in the last 24 hours, by status code.

${rows}
`; dialog.showModal(); } +$("#performance-rows").addEventListener("click", event => { const button = event.target.closest("[data-error-breakdown]"); if (!button) return; let breakdown = []; try { breakdown = JSON.parse(button.dataset.errorBreakdown || "[]"); } catch { breakdown = []; } showErrorBreakdown(button.dataset.errorHost || "", Number(button.dataset.errorTotal) || 0, breakdown); }); $("#log-status").addEventListener("change", renderLogs); $("#event-severity").addEventListener("change", renderLogs); $("#event-category").addEventListener("change", renderLogs); diff --git a/src/public/index.html b/src/public/index.html index b623e9a..e31c6d0 100644 --- a/src/public/index.html +++ b/src/public/index.html @@ -7,7 +7,7 @@ Site Gateway - + - + diff --git a/src/public/styles.css b/src/public/styles.css index d205495..8f27586 100644 --- a/src/public/styles.css +++ b/src/public/styles.css @@ -277,7 +277,7 @@ select{appearance:none!important;-webkit-appearance:none!important;background-re .performance-table th:nth-child(3),.performance-table td:nth-child(3){width:23%;text-align:center} .performance-table th:nth-child(4),.performance-table td:nth-child(4){width:23%;text-align:center} .performance-table .count-divider{color:var(--muted);margin:0 2px} -.performance-table td:has(.error-breakdown[open]){white-space:normal;overflow:visible}.performance-table .error-breakdown{display:inline-block;margin:0;border:0;border-radius:0;background:transparent;text-align:left;vertical-align:middle}.performance-table .error-breakdown summary{display:inline;padding:0;color:inherit;font-weight:inherit;cursor:pointer;list-style:none}.performance-table .error-breakdown summary::-webkit-details-marker{display:none}.performance-table .error-breakdown summary::marker{content:""}.performance-table .error-breakdown[open] summary{text-decoration:underline dotted;text-underline-offset:3px}.performance-table .error-breakdown-body{margin-top:6px;padding:8px 10px;border:1px solid var(--line);border-radius:8px;background:rgba(8,16,29,.28);font-size:.76rem;white-space:normal}.performance-table .error-breakdown-row{display:flex;justify-content:space-between;gap:14px;padding:2px 0} +.performance-table .count-link-button{border:0;background:none;padding:0;font:inherit;color:var(--danger);cursor:pointer;text-decoration:underline dotted;text-underline-offset:3px}.performance-table .count-link-button:hover{text-decoration-style:solid}.error-breakdown-list{margin-top:16px;border:1px solid var(--line);border-radius:12px;background:rgba(8,16,29,.28);padding:0 16px}.error-breakdown-row{display:flex;justify-content:space-between;gap:14px;padding:10px 0;border-top:1px solid var(--line);font-size:.85rem}.error-breakdown-row:first-child{border-top:0} @media(max-width:760px){.performance-table th:nth-child(2),.performance-table td:nth-child(2){display:none}.performance-table th:nth-child(1),.performance-table td:nth-child(1){width:40%}.performance-table th:nth-child(3),.performance-table td:nth-child(3){width:30%}.performance-table th:nth-child(4),.performance-table td:nth-child(4){width:30%}} .mfa-qr{display:flex;justify-content:center;padding:16px;background:#fff;border-radius:12px;margin:16px 0} .mfa-qr svg{width:200px;height:200px}