From df2272188fc3349269abdf69f7a7293f69c23d5e Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Sun, 30 Nov 2025 00:50:20 -0500 Subject: [PATCH] Add universal text-to-speech for keyboard-selected content MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added KJVSpeech global helper using Web Speech API - Space key reads aloud any element with green box selection - Works on all pages with keyboard navigation (resources, parables, etc.) - Chapter page has specialized handler for verse text - Prefers English voices, cleans up text (removes verse numbers) - Added Space key to keyboard shortcuts help modal 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- kjvstudy_org/templates/base.html | 106 ++++++++++++++++++++++++++++ kjvstudy_org/templates/chapter.html | 11 +++ 2 files changed, 117 insertions(+) diff --git a/kjvstudy_org/templates/base.html b/kjvstudy_org/templates/base.html index b1e7998..e79e15b 100644 --- a/kjvstudy_org/templates/base.html +++ b/kjvstudy_org/templates/base.html @@ -1633,6 +1633,103 @@ } }; + // Text-to-speech for any selected content + window.KJVSpeech = { + utterance: null, + speaking: false, + + speak: function(text) { + if (!('speechSynthesis' in window)) { + console.log('Speech synthesis not supported'); + return; + } + + // Stop any current speech + this.stop(); + + // Clean up the text - remove verse numbers at start, collapse whitespace + text = text.replace(/^\s*\d+\s*/, '').replace(/\s+/g, ' ').trim(); + + if (!text) return; + + this.utterance = new SpeechSynthesisUtterance(text); + this.utterance.rate = 0.9; + this.utterance.pitch = 1; + + // Try to use a good English voice + var voices = speechSynthesis.getVoices(); + var englishVoice = voices.find(function(v) { + return v.lang.startsWith('en') && v.name.includes('Daniel'); + }) || voices.find(function(v) { + return v.lang.startsWith('en-GB'); + }) || voices.find(function(v) { + return v.lang.startsWith('en'); + }); + + if (englishVoice) { + this.utterance.voice = englishVoice; + } + + this.speaking = true; + + this.utterance.onend = function() { + KJVSpeech.speaking = false; + }; + + this.utterance.onerror = function() { + KJVSpeech.speaking = false; + }; + + speechSynthesis.speak(this.utterance); + }, + + stop: function() { + if ('speechSynthesis' in window) { + speechSynthesis.cancel(); + } + this.speaking = false; + }, + + toggle: function(text) { + if (this.speaking) { + this.stop(); + } else { + this.speak(text); + } + }, + + // Get text from the currently selected element (green box) + getSelectedText: function() { + // Find element with our green selection outline + var selected = document.querySelector('[style*="outline: 2px solid"]') || + document.querySelector('[style*="outline:2px solid"]'); + + if (selected) { + return selected.textContent || selected.innerText; + } + + // Fallback: Try specific verse selectors + var verseEl = document.querySelector('.verse-text-content') || + document.querySelector('.verse-display .text') || + document.querySelector('.verse-text') || + document.querySelector('[data-verse-text]'); + + if (verseEl) { + return verseEl.textContent || verseEl.innerText; + } + + return null; + } + }; + + // Load voices (they may not be available immediately) + if ('speechSynthesis' in window) { + speechSynthesis.getVoices(); + speechSynthesis.onvoiceschanged = function() { + speechSynthesis.getVoices(); + }; + } + // Keyboard shortcuts document.addEventListener('keydown', function(e) { // Don't trigger if user is typing in an input field @@ -1728,6 +1825,14 @@ case '?': showKeyboardHelp(); break; + case ' ': + // Space: Read aloud selected text (green box selection or verse text) + var selectedText = KJVSpeech.getSelectedText(); + if (selectedText) { + e.preventDefault(); + KJVSpeech.toggle(selectedText); + } + break; } } }); @@ -1807,6 +1912,7 @@ '' + '
' + '

Other

' + + '
SpaceRead aloud
' + '
`Toggle sidebar
' + '
⌘+DToggle dark mode
' + '
/Search
' + diff --git a/kjvstudy_org/templates/chapter.html b/kjvstudy_org/templates/chapter.html index a947d73..0ae06ce 100644 --- a/kjvstudy_org/templates/chapter.html +++ b/kjvstudy_org/templates/chapter.html @@ -537,6 +537,17 @@ document.addEventListener('DOMContentLoaded', function() { var pdfBtn = document.querySelector('.pdf-btn'); if (pdfBtn) window.location.href = pdfBtn.href; } + + // Space: Read selected verse aloud + if (e.key === ' ' && selectedVerseIndex >= 0) { + e.preventDefault(); + var verseEl = verses[selectedVerseIndex]; + // Get text content, excluding the verse number link + var text = verseEl.textContent || verseEl.innerText; + // Remove verse number from the beginning + text = text.replace(/^\s*\d+\s*/, ''); + KJVSpeech.toggle(text); + } }); // Click to select verse