diff --git a/resources/js/singles/reader.ts b/resources/js/singles/reader.ts new file mode 100644 index 0000000..867f0aa --- /dev/null +++ b/resources/js/singles/reader.ts @@ -0,0 +1,89 @@ +export default function handleArticleReader() { + const button = document.getElementById('speak-btn'); + const article = document.querySelector('.article-content'); + + // Contrôles de paramètres (optionnels) + const rateControl = document.getElementById('speech-rate') as HTMLInputElement; + const pitchControl = document.getElementById('speech-pitch') as HTMLInputElement; + const volumeControl = document.getElementById('speech-volume') as HTMLInputElement; + const voiceSelect = document.getElementById('speech-voice') as HTMLSelectElement; + + let utterance: SpeechSynthesisUtterance; + let voices: SpeechSynthesisVoice[] = []; + + // Charger les voix disponibles + function loadVoices() { + voices = speechSynthesis.getVoices(); + if (voiceSelect && voices.length > 0) { + voiceSelect.innerHTML = ''; + voices + .filter((voice) => voice.lang.startsWith('fr')) // Filtrer les voix françaises + .forEach((voice, index) => { + const option = document.createElement('option'); + option.value = index.toString(); + option.textContent = `${voice.name} (${voice.lang})`; + voiceSelect.appendChild(option); + }); + } + } + + // Charger les voix au démarrage et quand elles changent + loadVoices(); + speechSynthesis.onvoiceschanged = loadVoices; + + // Configuration par défaut + const defaultSettings = { + rate: 0.9, // Vitesse (0.1 à 10) + pitch: 1, // Tonalité (0 à 2) + volume: 1, // Volume (0 à 1) + voice: null, // Voix (null = voix par défaut) + }; + + function createUtterance(text: string): SpeechSynthesisUtterance { + utterance = new SpeechSynthesisUtterance(text); + utterance.lang = 'fr-FR'; + + // Appliquer les paramètres depuis les contrôles ou les valeurs par défaut + utterance.rate = rateControl ? parseFloat(rateControl.value) : defaultSettings.rate; + utterance.pitch = pitchControl ? parseFloat(pitchControl.value) : defaultSettings.pitch; + utterance.volume = volumeControl ? parseFloat(volumeControl.value) : defaultSettings.volume; + + // Sélectionner la voix + if (voiceSelect && voices.length > 0) { + const selectedVoiceIndex = parseInt(voiceSelect.value); + const frenchVoices = voices.filter((voice) => voice.lang.startsWith('fr')); + if (frenchVoices[selectedVoiceIndex]) { + utterance.voice = frenchVoices[selectedVoiceIndex]; + } + } + + utterance.onend = () => { + button.textContent = '🔊 Lire en vocal'; + }; + + utterance.onerror = (event) => { + console.error('Erreur lors de la lecture:', event); + button.textContent = '🔊 Lire en vocal'; + }; + + return utterance; + } + + if (button) { + button.addEventListener('click', () => { + if (speechSynthesis.speaking) { + speechSynthesis.cancel(); + button.textContent = '🔊 Lire en vocal'; + } else { + if (article) { + const text = article.innerText.trim(); + if (text) { + createUtterance(text); + speechSynthesis.speak(utterance); + button.textContent = '⏹️ Arrêter la lecture'; + } + } + } + }); + } +} diff --git a/resources/js/singles/singles.ts b/resources/js/singles/singles.ts index 540ad42..15623d2 100644 --- a/resources/js/singles/singles.ts +++ b/resources/js/singles/singles.ts @@ -3,6 +3,7 @@ import handleFootnoteFormat from './footnote-format'; import handleCiteButton from './cite-button'; import handleLikeButton from './like-button.ts'; import handleShareButton from './share-button.ts'; +import handleArticleReader from './reader.ts'; import { handleArticleToolbar } from './article-toolbar.ts'; import { handleRevueToolbar } from './revue-toolbar.ts'; @@ -19,4 +20,5 @@ export default function singles(): void { handleArticleToolbar(); handleRevueToolbar(); handleShareButton(); + handleArticleReader(); } diff --git a/single-articles.php b/single-articles.php index aa5428b..6b42793 100644 --- a/single-articles.php +++ b/single-articles.php @@ -22,6 +22,18 @@ $revueID = get_field('related_revue', get_the_ID());