// Thin wrapper around the Screen Wake Lock API. Held by the diashow page so a phone // driving a projector doesn't sleep. No-op on unsupported browsers (Firefox, older // Safari). // // Wake locks die when the document goes hidden; the page re-acquires on visible to // keep the screen on across short interruptions. interface SentinelLike { release: () => Promise; // WakeLockSentinel is an EventTarget; it fires `release` when the lock ends — including // when the OS drops it because the tab was hidden. addEventListener?: (type: 'release', listener: () => void) => void; } type WakeLock = { request: (t: string) => Promise }; let sentinel: SentinelLike | null = null; let visibilityHandler: (() => void) | null = null; async function request(wakeLock: WakeLock): Promise { try { sentinel = await wakeLock.request('screen'); // The OS auto-releases the lock when the tab is hidden. Without clearing our handle // on that event, the visibility handler's `sentinel === null` guard would never be // true and the lock would never be re-acquired — the screen could then sleep after // the first time the tab lost focus. Null it on release so re-acquire can fire. sentinel.addEventListener?.('release', () => { sentinel = null; }); } catch { // User denied, or already released — nothing useful to do. sentinel = null; } } export async function acquireWakeLock(): Promise { const wakeLock = (navigator as Navigator & { wakeLock?: WakeLock }).wakeLock; if (!wakeLock) return; await request(wakeLock); // Re-acquire when the page becomes visible again (the OS releases the lock // while the tab is hidden). if (!visibilityHandler) { visibilityHandler = () => { if (document.visibilityState === 'visible' && sentinel === null) { void request(wakeLock); } }; document.addEventListener('visibilitychange', visibilityHandler); } } export async function releaseWakeLock(): Promise { if (sentinel) { try { await sentinel.release(); } catch { // ignore — release after release is fine } sentinel = null; } if (visibilityHandler) { document.removeEventListener('visibilitychange', visibilityHandler); visibilityHandler = null; } }