diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_webpage_generator.py b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_webpage_generator.py index 0802302..4c59eff 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_webpage_generator.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_webpage_generator.py @@ -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

Audio warnings

@@ -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){{