webpage_generator: success chime on finish + trim mobile empty scroll space
- Audio: a normal finish (scanning -> idle) now plays a rising success arpeggio (C5-E5-G5) instead of the falling warning chime, so a completed measurement is audibly distinct from a stop to any other state (which keeps the falling 660->440 warning). startWarning(finished) selects the chime for both the initial tone and the 30s repeats; the Watch LED text reads "Measurement finished" vs "Measurement stopped" accordingly. Help text updated. - Layout: body uses min-height:100dvh (with 100vh fallback) so the page no longer extends below the visible viewport on mobile, where iOS 100vh counts the retracted-toolbar height and leaves empty scroll room at the bottom.
This commit is contained in:
@@ -1633,7 +1633,11 @@ def _render_html(phone_numbers: list, has_tomo_queue: bool = True, tomo_types: d
|
||||
body {{
|
||||
background: var(--bg); color: var(--text);
|
||||
font-family: var(--sans); font-weight: 300;
|
||||
min-height: 100vh; padding: 1.5rem;
|
||||
min-height: 100vh; /* fallback for browsers without dvh */
|
||||
min-height: 100dvh; /* dynamic viewport: avoids extra scroll room
|
||||
below content on mobile (iOS 100vh counts the
|
||||
retracted-toolbar height and overshoots) */
|
||||
padding: 1.5rem;
|
||||
transition: background 0.4s, color 0.4s;
|
||||
}}
|
||||
body::before {{
|
||||
@@ -2157,7 +2161,7 @@ def _render_html(phone_numbers: list, has_tomo_queue: bool = True, tomo_types: d
|
||||
<h3>Audio warnings</h3>
|
||||
<ul>
|
||||
<li><strong>System LED</strong> — on when audio is enabled on this device.</li>
|
||||
<li><strong>Watch LED</strong> — armed (green) while a scan is running; pulses orange if a scan stops unexpectedly, with a chime every 30s until confirmed.</li>
|
||||
<li><strong>Watch LED</strong> — armed (green) while a scan is running; pulses orange when a scan stops, with a chime every 30s until confirmed. A normal finish (scan → idle) plays a rising success tone; a stop to any other state plays the falling warning tone, so you can tell them apart by ear.</li>
|
||||
<li><strong>Blocked LED</strong> — pulses orange while the queue is locked. A chime fires automatically after 60 continuous minutes blocked, then repeats hourly until cleared. No confirmation needed — it clears itself once the block resolves.</li>
|
||||
<li><strong>Live LED</strong> — green while this page's data feed is fresh; pulses orange if the feed goes stale or a fetch fails.</li>
|
||||
</ul>
|
||||
@@ -2353,6 +2357,7 @@ initDrag();
|
||||
|
||||
let audioCtx=null, audioEnabled=false;
|
||||
let audioArmed=false, warningActive=false, warningTimer=null, lastStatus=null;
|
||||
let warningWasSuccess=false; // true when the active warning is a normal finish (scanning -> idle)
|
||||
let staleActive=false, staleTimer=null, staleConfirmed=false;
|
||||
let blockedSince=null, blockedWarningActive=false, blockedChimeTimer=null;
|
||||
const BLOCKED_WARNING_DELAY_MS=60*60*1000; // first chime after 60 min continuously blocked
|
||||
@@ -2387,6 +2392,13 @@ function beep(freq,dur,vol){{
|
||||
function warningChime(){{
|
||||
beep(660,0.3,0.4); setTimeout(()=>beep(440,0.5,0.4),350);
|
||||
}}
|
||||
function successChime(){{
|
||||
// Reverse of warningChime: a rising major arpeggio (C5-E5-G5) so a normal
|
||||
// finish (scanning -> idle) is audibly a happy "done" rather than an alert.
|
||||
beep(523,0.22,0.4);
|
||||
setTimeout(()=>beep(659,0.22,0.4),240);
|
||||
setTimeout(()=>beep(784,0.45,0.4),480);
|
||||
}}
|
||||
function staleChime(){{
|
||||
beep(1200,0.12,0.35);
|
||||
setTimeout(()=>beep(1200,0.12,0.35),180);
|
||||
@@ -2432,12 +2444,14 @@ function confirmWarning(){{
|
||||
updateAudioUI();
|
||||
}}
|
||||
|
||||
function startWarning(){{
|
||||
function startWarning(finished){{
|
||||
// Not a gesture handler — context already unlocked by Enable button click.
|
||||
if(warningActive) return;
|
||||
warningActive=true;
|
||||
if(audioEnabled) warningChime();
|
||||
warningTimer=setInterval(()=>{{ if(audioEnabled) warningChime(); }},30000);
|
||||
warningWasSuccess=!!finished; // scanning -> idle == successful finish
|
||||
const chime = warningWasSuccess ? successChime : warningChime;
|
||||
if(audioEnabled) chime();
|
||||
warningTimer=setInterval(()=>{{ if(audioEnabled) chime(); }},30000);
|
||||
document.getElementById('btn-confirm').style.display='inline-block';
|
||||
updateAudioUI();
|
||||
}}
|
||||
@@ -2537,10 +2551,10 @@ function updateAudioUI(){{
|
||||
txt.textContent='Audio disabled \u2014 enable to receive warnings';
|
||||
}}else if(warningActive && staleActive){{
|
||||
ledWatch.className='led led-warning';
|
||||
txt.textContent='Measurement stopped & live feed lost \u2014 confirm each';
|
||||
txt.textContent=(warningWasSuccess?'Measurement finished':'Measurement stopped')+' & live feed lost \u2014 confirm each';
|
||||
}}else if(warningActive){{
|
||||
ledWatch.className='led led-warning';
|
||||
txt.textContent='Measurement stopped \u2014 confirm to silence';
|
||||
txt.textContent=(warningWasSuccess?'Measurement finished':'Measurement stopped')+' \u2014 confirm to silence';
|
||||
}}else if(staleActive){{
|
||||
ledWatch.className='led';
|
||||
txt.textContent='Live feed lost \u2014 confirm to silence';
|
||||
@@ -2573,7 +2587,7 @@ function handleAudioForStatus(status, prevStatus){{
|
||||
}}
|
||||
|
||||
if(audioArmed && wasScanning && !isScanning){{
|
||||
startWarning();
|
||||
startWarning(status==='idle'); // idle == finished successfully -> success chime
|
||||
}}
|
||||
|
||||
if(isScanning && warningActive){{
|
||||
|
||||
Reference in New Issue
Block a user