diff --git a/internal/service/static/main.js b/internal/service/static/main.js index 3d1c142..194bed6 100644 --- a/internal/service/static/main.js +++ b/internal/service/static/main.js @@ -32,6 +32,7 @@ class Odidere { this.isScrollPending = false; this.isStopPending = false; this.mediaRecorder = null; + this.wakeLock = null; // Auto-scroll: enabled by default, disabled when user scrolls up, // re-enabled when they scroll back to bottom or send a new message. @@ -88,6 +89,7 @@ class Odidere { } this.#stopCurrentAudio(); this.currentController?.abort(); + this.#releaseWakeLock(); } /** @@ -124,6 +126,7 @@ class Odidere { this.#bindEvents(); this.#fetchVoices(); this.#fetchModels(); + this.#initWakeLock(); } /** @@ -213,6 +216,9 @@ class Odidere { this.$saveSystemMessage.addEventListener('click', () => this.#saveSystemMessage(), ); + + // Wake lock: reacquire on visibility change + this.document.addEventListener('visibilitychange', () => this.#handleVisibilityChange()); } /** @@ -548,6 +554,7 @@ class Odidere { this.audioChunks = []; this.mediaRecorder.start(); this.#setRecordingState(true); + this.#acquireWakeLock(); } /** @@ -1069,6 +1076,12 @@ class Odidere { this.isStopPending = false; this.$send.classList.remove('pending'); } + + if (loading) { + this.#acquireWakeLock(); + } else { + this.#releaseWakeLock(); + } } /** @@ -1681,6 +1694,57 @@ class Odidere { console.error('failed to copy:', e); } } + + // ==================== + // WAKE LOCK + // ==================== + /** + * #initWakeLock performs feature detection for the Screen Wake Lock API. + */ + #initWakeLock() { + if (!('wakeLock' in navigator)) { + console.debug('Screen Wake Lock API not supported'); + } + } + + /** + * #acquireWakeLock requests a screen wake lock. Idempotent. + */ + async #acquireWakeLock() { + if (this.wakeLock) return; + if (!('wakeLock' in navigator)) return; + + try { + this.wakeLock = await navigator.wakeLock.request('screen'); + } catch (e) { + console.debug('wake lock acquire failed:', e); + } + } + + /** + * #releaseWakeLock releases the active wake lock. Idempotent. + */ + async #releaseWakeLock() { + if (!this.wakeLock) return; + + try { + await this.wakeLock.release(); + } catch (e) { + console.debug('wake lock release failed:', e); + } + + this.wakeLock = null; + } + + /** + * #handleVisibilityChange reacquires the wake lock when the tab becomes + * visible, if the app is currently active (recording or processing). + */ + #handleVisibilityChange() { + if (this.document.visibilityState !== 'visible') return; + if (!this.isRecording && !this.isProcessing) return; + this.#acquireWakeLock(); + } } // Initialize