carhop__dynamiques-theme__P.../resources/js/singles/reader.ts
Nonimart a9765891ff
Some checks failed
continuous-integration/drone/push Build is failing
FEATURE Updating speech reading article
2025-09-29 15:53:30 +02:00

127 lines
3.8 KiB
TypeScript

export default function handleArticleReader() {
const button = document.getElementById('listen-article');
const article = document.querySelector('.article-content');
// Paramètres fixes définis dans le code
const speechSettings = {
rate: 1.2, // Vitesse de lecture (0.1 à 2)
pitch: 1.1, // Tonalité (0 à 2)
volume: 1, // Volume (0 à 1)
};
let utterance: SpeechSynthesisUtterance;
let voices: SpeechSynthesisVoice[] = [];
let thomasVoice: SpeechSynthesisVoice | null = null;
// Charger les voix et trouver Thomas
function loadVoices() {
voices = speechSynthesis.getVoices();
// Chercher la voix Thomas (peut être "Thomas" ou contenir "Thomas")
thomasVoice =
voices.find(
(voice) => voice.name.toLowerCase().includes('thomas') || voice.name === 'Thomas'
) || null;
// Si Thomas n'est pas trouvé, prendre une voix française par défaut
if (!thomasVoice) {
thomasVoice = voices.find((voice) => voice.lang.startsWith('fr')) || null;
}
}
// Charger les voix au démarrage et quand elles changent
loadVoices();
speechSynthesis.onvoiceschanged = loadVoices;
// Variables pour le highlighting
let originalContent = '';
let words: string[] = [];
let currentWordIndex = 0;
function highlightWord(index: number) {
if (!article || index >= words.length) return;
// Restaurer le contenu original si c'est le premier mot
if (index === 0) {
article.innerHTML = originalContent;
}
// Créer le nouveau HTML avec le mot highlighted
const highlightedWords = words.map((word, i) => {
if (i === index) {
return `<span class="speech-highlight" style="background-color: yellow; padding: 2px 4px; border-radius: 3px;">${word}</span>`;
}
return word;
});
article.innerHTML = highlightedWords.join(' ');
}
function removeHighlight() {
if (article && originalContent) {
article.innerHTML = originalContent;
}
}
function createUtterance(text: string): SpeechSynthesisUtterance {
// Sauvegarder le contenu original et préparer les mots
originalContent = article ? article.innerHTML : '';
words = text.split(/\s+/).filter((word) => word.trim().length > 0);
currentWordIndex = 0;
utterance = new SpeechSynthesisUtterance(text);
utterance.lang = 'fr-FR';
// Appliquer les paramètres fixes
utterance.rate = speechSettings.rate;
utterance.pitch = speechSettings.pitch;
utterance.volume = speechSettings.volume;
// Utiliser Thomas par défaut
if (thomasVoice) {
utterance.voice = thomasVoice;
}
// Event pour highlighter les mots pendant la lecture
utterance.onboundary = (event) => {
if (event.name === 'word') {
highlightWord(currentWordIndex);
currentWordIndex++;
}
};
utterance.onend = () => {
button.textContent = '🔊 Lire en vocal';
removeHighlight();
currentWordIndex = 0;
};
utterance.onerror = (event) => {
console.error('Erreur lors de la lecture:', event);
button.textContent = '🔊 Lire en vocal';
removeHighlight();
currentWordIndex = 0;
};
return utterance;
}
button?.addEventListener('click', () => {
if (speechSynthesis.speaking) {
speechSynthesis.cancel();
button.classList.remove('is-active');
removeHighlight(); // Retirer le highlight quand on arrête
currentWordIndex = 0;
} else {
if (article) {
const text = (article as HTMLElement).innerText.trim();
if (text) {
createUtterance(text);
speechSynthesis.speak(utterance);
button.classList.add('is-active');
}
}
}
});
}