diff --git a/hbd/server/templates/plugins.html b/hbd/server/templates/plugins.html index bca788b..7244d02 100644 --- a/hbd/server/templates/plugins.html +++ b/hbd/server/templates/plugins.html @@ -827,13 +827,10 @@ if (!el || pts.length < 2) { if (el) el.style.display = 'none'; return; } const unitSuffix = opts.unitSuffix || ''; - const W = 600, H = 80, PAD = { top: 6, right: 8, bottom: 18, left: 28 }; - const cW = W - PAD.left - PAD.right; - const cH = H - PAD.top - PAD.bottom; + const W = 690, H = 92, PAD = { top: 6, right: 8, bottom: 18, left: 28 }; const tMin = pts[0].t, tMax = pts[pts.length - 1].t; const tRange = tMax - tMin || 1; - const x = t => PAD.left + ((t - tMin) / tRange) * cW; // Auto-scale Y axis with 10% padding, optionally clamped to opts.yDomain const vMin = Math.min(...pts.map(p => p.v)); @@ -845,6 +842,23 @@ const yLow = Math.max(domainLow, vMin - vPad); const yHigh = Math.min(domainHigh, vMax + vPad); const yRange = yHigh - yLow || 1; + + // Compute nice tick step for ~3-5 grid lines, then size the left + // margin to fit the widest label (values/units can run to 3+ digits, + // e.g. RTT samples above 99ms) instead of a fixed guess that clips them. + const rawStep = yRange / 4; + const mag = Math.pow(10, Math.floor(Math.log10(rawStep || 1))); + const niceStep = [1, 2, 5, 10].map(f => f * mag).find(s => yRange / s <= 5) || mag * 10; + const tickStart = Math.ceil(yLow / niceStep) * niceStep; + const yTicks = []; + for (let v = tickStart; v <= yHigh + 0.001; v += niceStep) yTicks.push(v); + const yTickLabels = yTicks.map(v => (Number.isInteger(v) ? v : v.toFixed(1)) + unitSuffix); + const maxLabelLen = yTickLabels.reduce((m, s) => Math.max(m, s.length), 0); + PAD.left = Math.max(28, Math.ceil(maxLabelLen * 5.5) + 10); + + const cW = W - PAD.left - PAD.right; + const cH = H - PAD.top - PAD.bottom; + const x = t => PAD.left + ((t - tMin) / tRange) * cW; const y = v => PAD.top + cH - ((v - yLow) / yRange) * cH; // Color based on latest value @@ -879,27 +893,29 @@ areaPaths += ``; } - // Compute nice tick step for ~3-5 grid lines - const rawStep = yRange / 4; - const mag = Math.pow(10, Math.floor(Math.log10(rawStep || 1))); - const niceStep = [1, 2, 5, 10].map(f => f * mag).find(s => yRange / s <= 5) || mag * 10; - const tickStart = Math.ceil(yLow / niceStep) * niceStep; let gridLines = ''; - for (let v = tickStart; v <= yHigh + 0.001; v += niceStep) { + yTicks.forEach((v, i) => { const yy = y(v).toFixed(1); - const label = (Number.isInteger(v) ? v : v.toFixed(1)) + unitSuffix; gridLines += ``; - gridLines += `${label}`; - } + gridLines += `${yTickLabels[i]}`; + }); - // X-axis time labels + // X-axis: start/end plus evenly spaced intermediate tickmarks + labels const fmt = ts => { const d = new Date(ts * 1000); return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); }; - const xLabels = ` - ${fmt(pts[0].t)} - ${fmt(pts[pts.length-1].t)}`; + const xTickCount = 5; + const xAxisY = (PAD.top + cH).toFixed(1); + let xAxisMarks = ''; + let xLabels = ''; + for (let i = 0; i < xTickCount; i++) { + const t = tMin + (tRange * i) / (xTickCount - 1); + const xx = x(t).toFixed(1); + const anchor = i === 0 ? 'start' : (i === xTickCount - 1 ? 'end' : 'middle'); + xAxisMarks += ``; + xLabels += `${fmt(t)}`; + } el.innerHTML = ` @@ -915,6 +931,7 @@ ${areaPaths} ${linePolylines} + ${xAxisMarks} ${xLabels} `; }