From b6a8a886656e51e63854a9be7dc0a5c425de4a3d Mon Sep 17 00:00:00 2001 From: ChuckBuilds Date: Thu, 16 Jul 2026 11:10:46 -0400 Subject: [PATCH] fix(web): size the live preview from the PNG's real dimensions, not config The initial SSE render sized the preview image (and both overlay canvases) from the server-reported config dimensions (cols x chain_length, rows x parallel), while the scale slider's re-render path sized from img.naturalWidth/naturalHeight. Whenever the snapshot PNG's actual size disagrees with the config (stale config, display service not restarted after a hardware change), the initial render stretched the image at a fractional ratio - blurry despite image-rendering: pixelated - and touching the scale slider "fixed" it. Reported live on the devpi test rig. Both paths now size from the loaded image's natural dimensions inside img.onload (which also removes a transient wrong-size flash between src assignment and load). The meta label now reports the true snapshot size. The preview card also gets overflow-x-auto so on narrow screens a wide preview scrolls at its exact pixel-perfect size instead of being squeezed into the viewport (fractional downscaling of pixel art also reads as blur). Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ --- web_interface/static/v3/js/app-shell.js | 42 +++++++++++-------- .../templates/v3/partials/overview.html | 5 ++- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/web_interface/static/v3/js/app-shell.js b/web_interface/static/v3/js/app-shell.js index f7049106..c366b16b 100644 --- a/web_interface/static/v3/js/app-shell.js +++ b/web_interface/static/v3/js/app-shell.js @@ -1870,29 +1870,35 @@ // Current scale from slider const scale = parseInt(document.getElementById('scaleRange')?.value || '8'); - // Update image and meta label + // Update image and meta label. Size everything from the PNG's + // OWN dimensions (naturalWidth/Height) once it has loaded — + // not from the server-reported config dimensions. If the + // config disagrees with what the display service actually + // renders (stale config, service not yet restarted), sizing + // from config stretches the image at a fractional ratio and + // the preview looks blurry until the scale slider forces a + // re-render. The slider path always used natural dimensions; + // now both paths do. img.style.imageRendering = 'pixelated'; img.onload = () => { + const nw = img.naturalWidth || data.width || 128; + const nh = img.naturalHeight || data.height || 64; + const width = nw * scale; + const height = nh * scale; + img.style.width = width + 'px'; + img.style.height = height + 'px'; + ledCanvas.width = width; + ledCanvas.height = height; + canvas.width = width; + canvas.height = height; + const meta = document.getElementById('previewMeta'); + if (meta) { + meta.textContent = `${nw} x ${nh} @ ${scale}x`; + } + drawGrid(canvas, nw, nh, scale); renderLedDots(); }; img.src = `data:image/png;base64,${data.image}`; - - const meta = document.getElementById('previewMeta'); - if (meta) { - meta.textContent = `${data.width || 128} x ${data.height || 64} @ ${scale}x`; - } - - // Size the canvases to match - const width = (data.width || 128) * scale; - const height = (data.height || 64) * scale; - img.style.width = width + 'px'; - img.style.height = height + 'px'; - ledCanvas.width = width; - ledCanvas.height = height; - canvas.width = width; - canvas.height = height; - drawGrid(canvas, data.width || 128, data.height || 64, scale); - renderLedDots(); } else { stage.style.display = 'none'; placeholder.style.display = 'block'; diff --git a/web_interface/templates/v3/partials/overview.html b/web_interface/templates/v3/partials/overview.html index daa8f990..11e3e398 100644 --- a/web_interface/templates/v3/partials/overview.html +++ b/web_interface/templates/v3/partials/overview.html @@ -321,7 +321,10 @@

Live Display Preview

-
+ +