initialisation asynchrone de la synthèse vaocale

This commit is contained in:
2026-01-18 14:50:37 +01:00
parent e2f57ebb65
commit 9782288596

View File

@@ -14,7 +14,7 @@
<meta property="og:locale" content="fr_FR" /> <meta property="og:locale" content="fr_FR" />
<style> <style>
body { body {
--vph: "100dvh"; --vph: "100vh";
display: flex; display: flex;
flex-direction: column; flex-direction: column;
min-height: var(--vph); min-height: var(--vph);
@@ -68,7 +68,7 @@ body > main {
<header> <header>
<nav class="container"> <nav class="container">
<h1>Chat<small><em>βeta</em><span style="color: #9B2318">*</span></small></h1> <h1>Chat<small><em>βeta</em><span style="color: #9B2318">*</span></small></h1>
<button id="bouton_synthese_vocale" type="button" class="outline" style="display: none;">🕨</button> <button id="bouton_synthese_vocale" type="button" class="secondary style="display: none;">🕨</button>
</nav> </nav>
</header> </header>
<main class="container overflow-auto" id="conversation"> <main class="container overflow-auto" id="conversation">
@@ -77,7 +77,7 @@ body > main {
<footer class="container"> <footer class="container">
<form id="formulaire" action="question.php" method="post" role="group"> <form id="formulaire" action="question.php" method="post" role="group">
<textarea id="question" name="question" placeholder="Ma question" required></textarea> <textarea id="question" name="question" placeholder="Ma question" required></textarea>
<button type="submit">Envoyer</button> <button id="bouton_envoyer" type="submit">Envoyer</button>
</form> </form>
</footer> </footer>
<dialog id="boite_synthese_vocale"> <dialog id="boite_synthese_vocale">
@@ -85,7 +85,7 @@ body > main {
<header> <header>
<button id="bouton_fermer" aria-label="Close" rel="prev"></button> <button id="bouton_fermer" aria-label="Close" rel="prev"></button>
<p> <p>
<strong>🕨 Synthèse vocale</strong> <strong>🕪 Synthèse vocale</strong>
</p> </p>
</header> </header>
<form> <form>
@@ -104,54 +104,68 @@ body > main {
</dialog> </dialog>
<script> <script>
const formulaire = document.getElementById('formulaire'); const formulaire = document.getElementById('formulaire');
const bouton = document.querySelector('button[type="submit"]'); const bouton_envoyer = document.getElementById('bouton_envoyer');
const conversation = document.getElementById('conversation'); const conversation = document.getElementById('conversation');
const question = document.getElementById('question'); const question = document.getElementById('question');
const bouton_synthese_vocale = document.getElementById('bouton_synthese_vocale'); const bouton_synthese_vocale = document.getElementById('bouton_synthese_vocale');
const boite_synthese_vocale = document.getElementById('boite_synthese_vocale'); const boite_synthese_vocale = document.getElementById('boite_synthese_vocale');
const select_voix = document.getElementById('select_voix'); const select_voix = document.getElementById('select_voix');
const aphone = select_voix.innerHTML;
const bouton_fermer = document.getElementById('bouton_fermer'); const bouton_fermer = document.getElementById('bouton_fermer');
const bouton_annuler = document.getElementById('bouton_annuler'); const bouton_annuler = document.getElementById('bouton_annuler');
const bouton_ok = document.getElementById('bouton_ok'); const bouton_ok = document.getElementById('bouton_ok');
const footer = document.querySelector('footer'); const footer = document.querySelector('footer');
const langue = "fr-FR"; const langue = document.documentElement.lang;
let voix = null; let voix = null;
if ('speechSynthesis' in window) { if ('speechSynthesis' in window) {
let liste_voix = speechSynthesis.getVoices().filter(voice => voice.lang === langue); speechSynthesis.onvoiceschanged = function() {
liste_voix.forEach((v, i) => { let liste_voix = speechSynthesis.getVoices().filter(voice => voice.lang.startsWith(langue));
const option = document.createElement('option'); select_voix.innerHTML = aphone;
option.value = i;
option.innerText = v.name; if (!liste_voix.length) {
select_voix.appendChild(option); bouton_synthese_vocale.style.display = 'none';
if (v.voiceURI === window.localStorage.getItem('voix')) { return;
select_voix.value = i;
voix = v;
option.selected = true;
} }
})
liste_voix.forEach((v, i) => {
const option = document.createElement('option');
option.value = i;
option.innerText = v.name;
select_voix.appendChild(option);
if (v.voiceURI === window.localStorage.getItem('voix')) {
select_voix.value = i;
voix = v;
option.selected = true;
bouton_synthese_vocale.innerHTML = "🕪";
}
})
bouton_synthese_vocale.style.display = 'block'; bouton_synthese_vocale.style.display = 'block';
bouton_synthese_vocale.addEventListener('click', () => { bouton_synthese_vocale.addEventListener('click', () => {
boite_synthese_vocale.showModal(); boite_synthese_vocale.showModal();
}); });
bouton_fermer.addEventListener('click', () => {
boite_synthese_vocale.close();
});
bouton_annuler.addEventListener('click', () => {
boite_synthese_vocale.close();
});
bouton_ok.addEventListener('click', () => {
boite_synthese_vocale.close();
if (select_voix.value) {
voix = liste_voix[select_voix.value];
window.localStorage.setItem('voix', voix.voiceURI);
bouton_synthese_vocale.innerHTML = "🕪";
} else {
voix = null;
window.localStorage.removeItem('voix');
bouton_synthese_vocale.innerHTML = "🕨";
}
});
}
bouton_fermer.addEventListener('click', () => { speechSynthesis.onvoiceschanged()
boite_synthese_vocale.close();
});
bouton_annuler.addEventListener('click', () => {
boite_synthese_vocale.close();
});
bouton_ok.addEventListener('click', () => {
boite_synthese_vocale.close();
if (select_voix.value) {
voix = liste_voix[select_voix.value];
window.localStorage.setItem('voix', voix.voiceURI);
} else {
voix = null;
window.localStorage.removeItem('voix');
}
});
} }
question.addEventListener('keydown', e => { question.addEventListener('keydown', e => {
@@ -167,10 +181,10 @@ body > main {
formulaire.addEventListener('submit', async (e) => { formulaire.addEventListener('submit', async (e) => {
e.preventDefault(); e.preventDefault();
if (bouton.disabled == true) return; if (bouton_envoyer.disabled == true) return;
bouton.disabled = true; bouton_envoyer.disabled = true;
bouton.setAttribute("aria-busy", true) bouton_envoyer.setAttribute("aria-busy", true)
const formulaireData = new FormData(formulaire); const formulaireData = new FormData(formulaire);
const citation = document.createElement('article'); const citation = document.createElement('article');
citation.innerText = formulaireData.get('question'); citation.innerText = formulaireData.get('question');
@@ -211,8 +225,8 @@ body > main {
setTimeout(() => { setTimeout(() => {
conversation.innerHTML += '<p class="reponse">Voulez-vous que je réponde à une autre question ?</p>'; conversation.innerHTML += '<p class="reponse">Voulez-vous que je réponde à une autre question ?</p>';
conversation.scrollTop = conversation.scrollHeight; conversation.scrollTop = conversation.scrollHeight;
bouton.disabled = false; bouton_envoyer.disabled = false;
bouton.setAttribute("aria-busy", false); bouton_envoyer.setAttribute("aria-busy", false);
question.focus() question.focus()
}, t); }, t);
}) })