From dc87c639856ae8415222cf5224f37c0dae1421d3 Mon Sep 17 00:00:00 2001 From: oshrizak <63424207+oshrizak@users.noreply.github.com> Date: Tue, 26 May 2026 10:54:13 -0700 Subject: [PATCH 1/2] fix(demo): stop repetitive screen-reader status announcements during conversion --- public/demo.html | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/public/demo.html b/public/demo.html index c5271ea..796b4d5 100644 --- a/public/demo.html +++ b/public/demo.html @@ -145,7 +145,10 @@

Upload your pages

@@ -188,11 +191,14 @@

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; 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(); }; @@ -333,6 +339,7 @@

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) { setError('Upload failed: ' + e.message); @@ -348,6 +355,15 @@

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; + $('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 +374,9 @@

About

} if (d && d.status === 'ready_for_review') { + $('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) { @@ -375,7 +393,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,6 +461,7 @@

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) { setError('Could not submit feedback: ' + e.message); From 66b0502abec4cdc7abfa337b292fe18a7414380f Mon Sep 17 00:00:00 2001 From: oshrizak <63424207+oshrizak@users.noreply.github.com> Date: Tue, 26 May 2026 11:46:43 -0700 Subject: [PATCH 2/2] fix(demo): non-repetitive SR status + best-effort screen wake lock during conversion --- public/demo.html | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/public/demo.html b/public/demo.html index 796b4d5..a315c44 100644 --- a/public/demo.html +++ b/public/demo.html @@ -194,6 +194,9 @@

About

// 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; }; @@ -202,6 +205,23 @@

About

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(' '); @@ -342,6 +362,7 @@

About

beginConverting('Converting your document.'); pollStatus(); } catch (e) { + converting = false; releaseAwake(); setError('Upload failed: ' + e.message); } finally { btn.removeAttribute('aria-busy'); @@ -360,6 +381,8 @@

About

// 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.'); @@ -374,6 +397,7 @@

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.'); @@ -385,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 @@ -464,6 +489,7 @@

About

beginConverting('Re-running the conversion with your feedback.'); pollStatus(); } catch (e) { + converting = false; releaseAwake(); setError('Could not submit feedback: ' + e.message); } });