perf(charts): draw the gauges without Recharts (#1975)
* perf(charts): draw the gauges without Recharts The doughnut gauges were the last always-on 1Hz surface still on Recharts. Each tick re-rendered a RadialBarChart per gauge and restarted a JS tween that re-rendered the chart again on every animation frame — the mechanism behind the macOS fan noise that shortening the tween to 300ms only reduced. Draw the ring as a stroked circle instead: the value is the visible run of the stroke, so a tick sets `stroke-dashoffset` once and the transition to the new value runs on the compositor rather than through React. Geometry, colours and the readout keep the sizes the Recharts gauge used at each breakpoint, and the readout stays inside the view box so it scales with the container. Out-of-range readings now pin the ring instead of wrapping past the start, which would have read as a much lower value. Per gauge: 42 DOM elements -> 12. Dashboard Recharts wrappers: 6 -> 1. vendor-chart: 440.45 kB -> 413.32 kB (gzip 123.97 -> 118.00). Refs #1581 * fix(charts): size the gauge view box to its container The compact gauge drew its Recharts-era radii into the 200-unit view box the xl gauge uses, so every coordinate was halved: the backing disc covered 50px of the 100px surface and the readout shrank with it. Recharts sized its canvas to the container, which is what those radii were written against. Give each breakpoint the view box that matches its container, and size the loading placeholder per breakpoint too — a fixed 128px circle overflowed the 100px compact surface. * docs(charts): state why the gauge readout lives in the view box * perf(charts): lengthen the gauge tween to 500ms Half a second reads more smoothly than 300ms while still settling well inside the 1Hz tick, leaving the drawing pipeline idle for the rest of each second. The guard that matters is unchanged: a tween at or above the tick would restart before settling, so the gauge would never reach the reading it displays. Also corrects the note beside the transition. `stroke-dashoffset` repaints as it animates rather than being composited, so what the change removed is the per-frame React render, not the frames themselves — which is exactly why the duration is bounded by the tick.
S
shm committed
643261c30efbef8c7bedff48edc528ebd82ad405
Parent: 4311d60
Committed by GitHub <noreply@github.com>
on 8/22/2026, 9:00:12 AM