From 0005731c4558e2045c00b45ac98a2502375eaee5 Mon Sep 17 00:00:00 2001 From: "Valentin V. Bartenev" Date: Sat, 16 May 2026 16:58:16 +0300 Subject: [PATCH] Web: fix bar chart hover index for edge columns The hover index in syncHoverByX was computed as: Math.round(x / width * (n - 1)) which linearly maps the full canvas width [0, width] to indices [0, n-1]. For bar charts (packets, gps_satellites) the bars are drawn in a plot area with plotLeft=4 and plotRight=width-4 margins, so bar centers are at plotLeft + slotWidth*i + slotWidth/2. This caused the first and last bars to require the cursor to be at the very edge of the canvas to highlight correctly, while center bars appeared fine. For bar chart keys, use a slot-based formula instead: Math.floor((x - plotLeft) / slotWidth) which maps the cursor to whichever slot it falls in, matching the actual bar layout. Line chart keys retain the original formula. --- src/helpers/web/WebPanelServer.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/helpers/web/WebPanelServer.cpp b/src/helpers/web/WebPanelServer.cpp index 12111332..aa73b5fb 100644 --- a/src/helpers/web/WebPanelServer.cpp +++ b/src/helpers/web/WebPanelServer.cpp @@ -2001,7 +2001,14 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML( function syncHoverByX(srcCanvas, clientX) { const rect = srcCanvas.getBoundingClientRect(); const x = Math.max(0, Math.min(rect.width || 1, clientX - rect.left)); - const srcIdx = Math.max(0, Math.min(points.length - 1, Math.round(x / (rect.width || 1) * (points.length - 1)))); + let srcIdx; + if (key === "packets" || key === "gps_satellites") { + const plotLeft = 4, plotRight = (rect.width || 1) - 4; + const slotWidth = (plotRight - plotLeft) / points.length; + srcIdx = Math.max(0, Math.min(points.length - 1, Math.floor((x - plotLeft) / slotWidth))); + } else { + srcIdx = Math.max(0, Math.min(points.length - 1, Math.round(x / (rect.width || 1) * (points.length - 1)))); + } const hoveredTs = points[srcIdx][0]; document.querySelectorAll('#statsTrends canvas').forEach(c => { if (!c._updateHover || !c._points) return;