diff --git a/public/analytics.js b/public/analytics.js index 355724dd1..7ef877b3a 100644 --- a/public/analytics.js +++ b/public/analytics.js @@ -216,7 +216,7 @@
- @@ -3194,10 +3194,10 @@

Units — Usefulness: the #672 composite (0.30 bridge + 0.25 coverage + 0.25 redundancy + 0.20 traffic). Traffic share: % of non-advert traffic relayed. Bridge/Coverage/Redundancy: 0–100% (network-normalized). Relays (1h/24h) and Adverts: packet counts. Axes auto-scale to the data.

${points.length} repeater${points.length === 1 ? '' : 's'}
diff --git a/public/perf.js b/public/perf.js index 26f1f2fd5..ee264265c 100644 --- a/public/perf.js +++ b/public/perf.js @@ -292,12 +292,26 @@ function renderVersionCard(health) { return String(s).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, '''); }; + // The server overview row (requests, latency, uptime, slow queries, version). + // It sits above the WebSocket history in its own container, so the history's + // Period select stays outside the five-second #perfContent rebuild. + function renderPerfOverview(server, health) { + return `
+
${escapeHtml(server.totalRequests)}
Total Requests
+
${escapeHtml(server.avgMs)}ms
Avg Response
+
${escapeHtml(health ? health.uptimeHuman : Math.round(server.uptime / 60) + 'm')}
Uptime
+
${server.slowQueries.length}
Slow (>100ms)
+ ${renderVersionCard(health)} +
`; + } + async function render(app) { // The selector stays outside perfContent's existing five-second DOM // rebuild, preserving keyboard focus and an open native select menu. - app.innerHTML = '

Performance Dashboard

' + + app.innerHTML = '

Performance Dashboard

' + '
' + - '

WebSocket clients

' + '
Loading history…
Loading...
'; const selector = document.getElementById('wsHistoryRange'); @@ -346,18 +360,11 @@ function renderVersionCard(health) { const historyEl = document.getElementById('wsHistoryContent'); if (historyEl) historyEl.innerHTML = renderWSClientHistory(wsHistory, health && health.websocket ? health.websocket.clients : null, range, false) || '

History is temporarily unavailable. Retrying on the next refresh.

'; + const overviewEl = document.getElementById('perfOverview'); + if (overviewEl) overviewEl.innerHTML = renderPerfOverview(server, health); let html = ''; - // Server overview - html += `
-
${escapeHtml(server.totalRequests)}
Total Requests
-
${escapeHtml(server.avgMs)}ms
Avg Response
-
${escapeHtml(health ? health.uptimeHuman : Math.round(server.uptime / 60) + 'm')}
Uptime
-
${server.slowQueries.length}
Slow (>100ms)
- ${renderVersionCard(health)} -
`; - // System health (memory, event loop / go runtime, WS) if (health) { const isGo = health.engine === 'go'; diff --git a/public/style.css b/public/style.css index e55500588..ad52dd119 100644 --- a/public/style.css +++ b/public/style.css @@ -3714,12 +3714,15 @@ button.region-pill-active:hover { opacity: 0.85; color: var(--text-on-accent); } margin-right: 2px; user-select: none; white-space: nowrap; flex-shrink: 0; } .analytics-filters { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 8px 0; } -.analytics-time-window-select { +/* The one time-window / metric ]*>/) || [''])[0]; + assert.ok(/class="time-window-select"/.test(select), 'select must use .time-window-select: ' + select); + assert.ok(!/style=/.test(select), 'select must not carry inline styles: ' + select); + assert.ok(/Period<\/span>/.test(appHtml), 'label text must use .region-filter-label'); + const heading = (appHtml.match(/

[\s\S]*?<\/h3>/) || [''])[0]; + assert.ok(/