From f26cc60b5379b8603e042513a1b4779e30b4800c Mon Sep 17 00:00:00 2001 From: drjones Date: Thu, 12 Mar 2026 13:36:01 -0700 Subject: [PATCH] UI polish: CRT scanlines, glow effects, boot animation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pure CSS visual upgrades — zero JS overhead, no feature changes: - CRT scanline overlay via body::after repeating gradient - Boot-in animation (brightness flash + blur fade) - Title flicker on load, persistent text-shadow glow - Pulsing glow on active radio dots and connection indicator - Shimmer gradient on progress bar - Canvas sweep cursor and sparkline glow via shadowBlur - Gradient fills on sparkline charts - Hover states on cards, metric tiles, buttons (glow + scale) - Custom thin scrollbar on log panel - Tabular-nums for jitter-free metric updates - Version badge in header Made-with: Cursor --- src/main.cpp | 80 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 54 insertions(+), 26 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 18d8cda..018042f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -978,25 +978,30 @@ const char kHtml[] = R"HTML( CC1101 JAMMER

CC1101 JAMMER

ESP32-S3 • 300–320 MHz + 390–436 MHz • Dual-band FM noise sweep
-
-
+
+
+
+
v2.0 // LFSR+VCO
+
@@ -1239,13 +1261,17 @@ function drawSw(id,freq,ctr,span,active,trail,marks){ const g=ctx.createLinearGradient(cx-80,0,cx+80,0); g.addColorStop(0,'rgba(134,242,138,0)');g.addColorStop(.5,'rgba(134,242,138,0.22)');g.addColorStop(1,'rgba(134,242,138,0)'); ctx.fillStyle=g;ctx.fillRect(cx-80,0,160,H-14); - // Cursor line + // Cursor line with glow + ctx.save();ctx.shadowColor='rgba(134,242,138,.6)';ctx.shadowBlur=6; ctx.strokeStyle='#86f28a';ctx.lineWidth=2; ctx.beginPath();ctx.moveTo(cx,0);ctx.lineTo(cx,H-14);ctx.stroke(); - // Cursor dot + ctx.restore(); + // Cursor dot with glow + ctx.save();ctx.shadowColor='rgba(134,242,138,.8)';ctx.shadowBlur=10; ctx.fillStyle='#86f28a';ctx.beginPath();ctx.arc(cx,(H-14)/2,4,0,Math.PI*2);ctx.fill(); + ctx.restore(); // Top notch - ctx.fillRect(Math.max(0,cx-2),0,4,4); + ctx.fillStyle='#86f28a';ctx.fillRect(Math.max(0,cx-2),0,4,4); } else if(!active){ ctx.strokeStyle='#2a1216';ctx.lineWidth=1; ctx.beginPath();ctx.moveTo(W/2,0);ctx.lineTo(W/2,H-14);ctx.stroke(); @@ -1270,11 +1296,13 @@ function drawSp(id,data,color,minH,maxH){ ctx.beginPath();let fs=true; data.forEach((v,i)=>{if(v===null){fs=true;return;}const x=i/(HS-1)*W,y=toY(v);if(fs){ctx.moveTo(x,H-4);ctx.lineTo(x,y);fs=false;}else ctx.lineTo(x,y);}); ctx.lineTo(W,H-4);ctx.closePath(); - ctx.fillStyle=color+'28';ctx.fill(); - // Line + const gf=ctx.createLinearGradient(0,0,0,H);gf.addColorStop(0,color+'30');gf.addColorStop(1,color+'05'); + ctx.fillStyle=gf;ctx.fill(); + // Line with glow + ctx.save();ctx.shadowColor=color;ctx.shadowBlur=4; ctx.strokeStyle=color;ctx.lineWidth=1.5;ctx.beginPath();fs=true; data.forEach((v,i)=>{if(v===null){fs=true;return;}const x=i/(HS-1)*W,y=toY(v);if(fs){ctx.moveTo(x,y);fs=false;}else ctx.lineTo(x,y);}); - ctx.stroke(); + ctx.stroke();ctx.restore(); // Range labels ctx.fillStyle=color+'90';ctx.font='8px monospace'; ctx.textAlign='left';ctx.fillText(mn.toFixed(0),2,H-3); @@ -1349,7 +1377,7 @@ function applyTelemetry(t){ document.getElementById('hNow').className=ch(t.free_heap/1024); drawSp('cT',hT,'#86f28a',20,90); drawSp('cH',hH,'#4fbf59',0,320); - document.getElementById('connDot').style.background='#86f28a'; + const cd=document.getElementById('connDot');cd.style.background='#86f28a';cd.style.boxShadow='0 0 8px rgba(134,242,138,.7)'; } let logTick=0; @@ -1364,7 +1392,7 @@ async function poll(){ if(atBot)log.scrollTop=log.scrollHeight; } }catch(e){ - document.getElementById('connDot').style.background='#f28a86'; + const cd=document.getElementById('connDot');cd.style.background='#f28a86';cd.style.boxShadow='0 0 8px rgba(242,138,134,.6)'; document.getElementById('bt').textContent='OFFLINE'; } setTimeout(poll, 1000);