Skip to content

Commit 131618f

Browse files
committed
fix(admin): KeyViz heatmap honours devicePixelRatio
Phase 2-B follow-up to PR #680. Claude bot's round-1 review flagged that the canvas buffer was sized at CSS-pixel dimensions, leaving every cell edge blurry on 2x displays. Scale the buffer to physical pixels via window.devicePixelRatio, keep the CSS style at logical pixels, and reset the transform with setTransform on every render so repeated useEffect runs do not stack scales. The DPR is clamped at 4 so a browser reporting an absurd ratio (e.g. zoom-aware DPR > 8) cannot balloon the canvas buffer beyond the rendering budget; at the maximum matrix size 4x DPR is already 16384 x 16384 px.
1 parent df5ea5e commit 131618f

1 file changed

Lines changed: 14 additions & 2 deletions

File tree

web/admin/src/pages/KeyViz.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,22 @@ function Heatmap({ matrix }: HeatmapProps) {
111111
useEffect(() => {
112112
const canvas = canvasRef.current;
113113
if (!canvas) return;
114-
canvas.width = width;
115-
canvas.height = height;
114+
// Scale the backing buffer to physical pixels and keep CSS at
115+
// logical pixels: on a 2× display every cell edge is otherwise
116+
// rendered against a half-resolution buffer and reads as blurry.
117+
// We clamp the ratio so a future browser quirk reporting an
118+
// absurd value (e.g. Firefox's experimental zoom-aware DPR > 8)
119+
// does not balloon canvas memory beyond reason — at the maximum
120+
// matrix size 4 × dpr is already 16384 × 16384 px of buffer.
121+
const dpr = Math.min(window.devicePixelRatio || 1, 4);
122+
canvas.width = Math.max(1, Math.floor(width * dpr));
123+
canvas.height = Math.max(1, Math.floor(height * dpr));
116124
const ctx = canvas.getContext("2d");
117125
if (!ctx) return;
126+
// setTransform (not scale) so the matrix is reset cleanly on
127+
// every render — repeated useEffect runs would otherwise stack
128+
// scales on top of the previous one.
129+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
118130
ctx.clearRect(0, 0, width, height);
119131
if (matrix.rows.length === 0 || matrix.column_unix_ms.length === 0) return;
120132

0 commit comments

Comments
 (0)