diff --git a/public/demo.html b/public/demo.html index c5271ea..a315c44 100644 --- a/public/demo.html +++ b/public/demo.html @@ -145,7 +145,10 @@

Upload your pages

@@ -188,14 +191,37 @@

About

let sourceBase = 'document'; let currentUserCode = null; let currentBlobUrl = null; + // Tracks the last polled phase so step changes are announced to screen readers + // once, instead of re-announcing the same status on every poll (issue #18). + let lastPhase = null; + // Screen Wake Lock sentinel + whether a conversion is currently active (#16). + let wakeLock = null; + let converting = false; const $ = (id) => document.getElementById(id); const show = (id) => { $(id).hidden = false; }; const hide = (id) => { $(id).hidden = true; }; - const announce = (msg) => { $('status-live').textContent = msg; }; + const announce = (msg) => { $('status-text').textContent = msg; }; const live = (msg) => { $('live').textContent = msg; }; const setError = (msg) => { $('error').textContent = msg || ''; }; const focusHeading = (id) => { $(id).focus(); }; + // Best-effort Screen Wake Lock: keep the screen awake while a conversion is in + // progress (issue #16). The browser auto-releases it when the tab is hidden, so + // we re-acquire on visibilitychange. No-op where unsupported or not permitted. + async function keepAwake() { + if (!('wakeLock' in navigator) || wakeLock) return; + try { + wakeLock = await navigator.wakeLock.request('screen'); + wakeLock.addEventListener('release', () => { wakeLock = null; }); + } catch { wakeLock = null; } + } + function releaseAwake() { + try { if (wakeLock) wakeLock.release(); } catch {} + wakeLock = null; + } + document.addEventListener('visibilitychange', () => { + if (converting && document.visibilityState === 'visible') keepAwake(); + }); // Spell a code out so screen readers announce each character clearly, // e.g. "WXYZ-1234" -> "W X Y Z dash 1 2 3 4". const spellCode = (code) => code.split('').map((c) => (c === '-' ? 'dash' : c)).join(' '); @@ -333,8 +359,10 @@

About

sessionId = d.session_id; hide('upload-section'); show('status-section'); focusHeading('step3-h'); announce('Starting… ' + d.image_count + ' page(s) uploaded.'); + beginConverting('Converting your document.'); pollStatus(); } catch (e) { + converting = false; releaseAwake(); setError('Upload failed: ' + e.message); } finally { btn.removeAttribute('aria-busy'); @@ -348,6 +376,17 @@

About

reconciliation: 'stitching pages together', assembly: 'assembling the document', review: 'reviewing for accessibility', done: 'finishing up', }; + // One-time announcement when conversion starts: tells screen-reader users it + // runs in the background so they can leave and come back, without the repeated + // status chatter (issue #18). + function beginConverting(lead) { + lastPhase = null; + converting = true; + keepAwake(); + $('status-live').setAttribute('aria-busy', 'true'); + live((lead || 'Converting your document.') + + ' This can take a few minutes — you can leave this page and come back; the conversion keeps running in the background.'); + } async function pollStatus() { let d = null; try { @@ -358,7 +397,10 @@

About

} if (d && d.status === 'ready_for_review') { + converting = false; releaseAwake(); + $('status-live').setAttribute('aria-busy', 'false'); announce('Done! Your accessible HTML is ready.'); + live('Conversion complete — your accessible HTML is ready.'); try { await showResult(d); } catch (e) { @@ -367,6 +409,7 @@

About

return; // terminal — stop polling } if (d && d.status === 'failed') { + converting = false; releaseAwake(); setError('Conversion failed: ' + (d.error || 'unknown error') + '. You can try again.'); hide('status-section'); show('upload-section'); focusHeading('step2-h'); return; // terminal — stop polling @@ -375,7 +418,13 @@

About

// queued / running / a transient error / an unexpected shape: keep going // so a single hiccup never strands the user on the spinner. if (d && (d.phase || d.status)) { - announce('Working — ' + (PHASE_LABEL[d.phase] || d.phase || d.status) + '…'); + const phase = d.phase || d.status; + const label = PHASE_LABEL[phase] || phase; + announce('Working — ' + label + '…'); // visible only — not a live region, so it never re-announces + if (phase !== lastPhase) { // announce a step change to screen readers exactly once + lastPhase = phase; + live(label.charAt(0).toUpperCase() + label.slice(1) + '.'); + } } setTimeout(pollStatus, 2500); } @@ -437,8 +486,10 @@

About

$('feedback-text').value = ''; hide('result-section'); show('status-section'); focusHeading('step3-h'); announce('Re-running with your feedback…'); + beginConverting('Re-running the conversion with your feedback.'); pollStatus(); } catch (e) { + converting = false; releaseAwake(); setError('Could not submit feedback: ' + e.message); } });