ascenseur

This commit is contained in:
2026-01-19 16:52:54 +01:00
parent 4821285a00
commit 8dc22a80ba

View File

@@ -63,7 +63,14 @@ body > main {
} }
#bouton_reconnaissance_vocale { #bouton_reconnaissance_vocale {
padding: 0.7rem; padding: 0;
width: 3rem;
margin-left: -3rem;
border: none;
}
#bouton_envoyer {
padding: 1.5rem;
} }
@media print { @media print {
@@ -82,7 +89,7 @@ body > main {
<button id="bouton_synthese_vocale" type="button" class="secondary" style="display: none;" disabled>🔈</button> <button id="bouton_synthese_vocale" type="button" class="secondary" style="display: none;" disabled>🔈</button>
</nav> </nav>
</header> </header>
<main class="container overflow-auto" id="conversation"> <main class="container" id="conversation">
<p class="reponse">Posez-moi toutes vos questions !</p> <p class="reponse">Posez-moi toutes vos questions !</p>
</main> </main>
<footer class="container"> <footer class="container">
@@ -102,7 +109,7 @@ body > main {
</header> </header>
<form> <form>
<fieldset> <fieldset>
<label>Voix disponibles :</label> <label for="select_voix">Voix disponibles :</label>
<select id="select_voix"> <select id="select_voix">
<option value="">Pas de synthèse vocale</option> <option value="">Pas de synthèse vocale</option>
</select> </select>
@@ -115,23 +122,93 @@ body > main {
</article> </article>
</dialog> </dialog>
<script> <script>
const formulaire = document.getElementById('formulaire'); const formulaire = document.getElementById('formulaire');
const bouton_envoyer = document.getElementById('bouton_envoyer'); 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 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 = document.documentElement.lang; const langue = document.documentElement.lang;
let voix = null; question.addEventListener('keydown', e => {
function charger_voix() { if (e.key === 'Enter' && !e.ctrlKey && !e.shiftKey) {
let liste_voix = speechSynthesis.getVoices().filter(voice => voice.lang.startsWith(langue)); e.preventDefault();
e.target.form.requestSubmit();
}
});
question.addEventListener('focus', () => {
question.scrollIntoView({ block: 'nearest' });
});
formulaire.addEventListener('submit', async (e) => {
e.preventDefault();
if (bouton_envoyer.disabled == true) return;
bouton_envoyer.disabled = true;
let bouton_envoyer_innerHTML = bouton_envoyer.innerHTML;
bouton_envoyer.innerHTML = "";
bouton_envoyer.setAttribute("aria-busy", true)
const formulaireData = new FormData(formulaire);
const citation = document.createElement('article');
citation.innerText = formulaireData.get('question');
conversation.appendChild(citation);
formulaire.reset();
const paragraphe = document.createElement('p');
paragraphe.setAttribute("aria-busy", "true");
conversation.appendChild(paragraphe);
conversation.scrollTop = conversation.scrollHeight;
const requete = await fetch(formulaire.action, {
method: formulaire.method,
body: formulaireData
});
paragraphe.setAttribute('aria-busy', 'true');
const reponse = await requete.text();
paragraphe.setAttribute('aria-busy', 'false');
if (voix) {
const utterance = new SpeechSynthesisUtterance(reponse);
utterance.lang = langue;
utterance.voice = voix;
utterance.rate = 1;
speechSynthesis.speak(utterance);
}
let t = 0;
Array.from(reponse).forEach((lettre, i) => {
setTimeout(() => {
if (lettre == "\n") {
paragraphe.innerHTML += "<br>";
} else {
paragraphe.innerHTML += lettre;
}
paragraphe.scrollIntoView({ block: 'start', behavior: 'auto' });
}, t += 100 * Math.random());
});
setTimeout(() => {
const paragraphe = document.createElement('p');
paragraphe.classList.add('reponse');
paragraphe.innerHTML = 'Voulez-vous que je réponde à une autre question ?';
conversation.appendChild(paragraphe);
paragraphe.scrollIntoView({ block: 'start', behavior: 'auto' });
bouton_envoyer.disabled = false;
bouton_envoyer.setAttribute("aria-busy", false);
bouton_envoyer.innerHTML = bouton_envoyer_innerHTML;
question.focus()
}, t);
})
// Synthèse vocale
let voix = null;
function charger_voix() {
let liste_voix = speechSynthesis.getVoices(); // .filter(voice => voice.lang.startsWith(langue));
select_voix.innerHTML = aphone; select_voix.innerHTML = aphone;
if (!liste_voix.length) { if (!liste_voix.length) {
@@ -176,113 +253,51 @@ body > main {
bouton_synthese_vocale.innerHTML = "🔈"; bouton_synthese_vocale.innerHTML = "🔈";
} }
}); });
} }
if ('speechSynthesis' in window) { if ('speechSynthesis' in window) {
bouton_synthese_vocale.style.display = 'block'; bouton_synthese_vocale.style.display = 'block';
charger_voix(); charger_voix();
} }
var SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; // Reconnaissance vocale
if (SpeechRecognition) { var SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
if (SpeechRecognition) {
bouton_reconnaissance_vocale.addEventListener('click', () => { bouton_reconnaissance_vocale.addEventListener('click', () => {
const reconnaissance= new SpeechRecognition(); const reconnaissance= new SpeechRecognition();
reconnaissance.lang = langue; reconnaissance.lang = langue;
reconnaissance.continuous = false;
reconnaissance.interimResults = false;
reconnaissance.maxAlternatives = 1;
bouton_reconnaissance_vocale_inner_HTML = bouton_reconnaissance_vocale.innerHTML bouton_reconnaissance_vocale_inner_HTML = bouton_reconnaissance_vocale.innerHTML
bouton_reconnaissance_vocale.innerHTML = "" bouton_reconnaissance_vocale.innerHTML = ""
bouton_reconnaissance_vocale.setAttribute("aria-busy", true) bouton_reconnaissance_vocale.setAttribute("aria-busy", true)
reconnaissance.addEventListener('result', (event) => { reconnaissance.addEventListener('result', (event) => {
const transcript = event.results[0][0].transcript; const transcript = event.results[0][0].transcript;
question.value = transcript; question.value = transcript;
formulaire.requestSubmit() if (!bouton_envoyer.disabled) formulaire.requestSubmit()
}); });
reconnaissance.addEventListener('speechend', () => { reconnaissance.onspeechend =
reconnaissance.onerror =
reconnaissance.onnomatch = function () {
reconnaissance.stop(); reconnaissance.stop();
bouton_reconnaissance_vocale.setAttribute("aria-busy", false) bouton_reconnaissance_vocale.setAttribute("aria-busy", false)
bouton_reconnaissance_vocale.innerHTML = bouton_reconnaissance_vocale_inner_HTML bouton_reconnaissance_vocale.innerHTML = bouton_reconnaissance_vocale_inner_HTML
}); };
reconnaissance.addEventListener('error', (event) => {
bouton_reconnaissance_vocale.setAttribute("aria-busy", false)
bouton_reconnaissance_vocale.innerHTML = bouton_reconnaissance_vocale_inner_HTML
});
reconnaissance.start(); reconnaissance.start();
}); });
bouton_reconnaissance_vocale.style.display = 'block'; bouton_reconnaissance_vocale.style.display = 'block';
} }
question.addEventListener('keydown', e => { // Adaptation au clavier virtuel du téléphone
if (e.key === 'Enter' && !e.ctrlKey && !e.shiftKey) { function onResize() {
e.preventDefault();
e.target.form.requestSubmit();
}
});
question.addEventListener('focus', () => {
question.scrollIntoView({ block: 'nearest' });
});
formulaire.addEventListener('submit', async (e) => {
e.preventDefault();
if (bouton_envoyer.disabled == true) return;
bouton_envoyer.disabled = true;
let bouton_envoyer_innerHTML = bouton_envoyer.innerHTML;
bouton_envoyer.innerHTML = "";
bouton_envoyer.setAttribute("aria-busy", true)
const formulaireData = new FormData(formulaire);
const citation = document.createElement('article');
citation.innerText = formulaireData.get('question');
conversation.appendChild(citation);
formulaire.reset();
const paragraphe = document.createElement('p');
paragraphe.setAttribute("aria-busy", "true");
conversation.appendChild(paragraphe);
conversation.scrollTop = conversation.scrollHeight;
const requete = await fetch(formulaire.action, {
method: formulaire.method,
body: formulaireData
});
paragraphe.setAttribute('aria-busy', 'true');
const reponse = await requete.text();
paragraphe.setAttribute('aria-busy', 'false');
if (voix) {
const utterance = new SpeechSynthesisUtterance(reponse);
utterance.lang = langue;
utterance.voice = voix;
utterance.rate = 1;
speechSynthesis.speak(utterance);
}
let t = 0;
Array.from(reponse).forEach((lettre, i) => {
setTimeout(() => {
if (lettre == "\n") {
paragraphe.innerHTML += "<br>";
} else {
paragraphe.innerHTML += lettre;
}
conversation.scrollTop = conversation.scrollHeight;
}, t += 100 * Math.random());
});
setTimeout(() => {
conversation.innerHTML += '<p class="reponse">Voulez-vous que je réponde à une autre question ?</p>';
conversation.scrollTop = conversation.scrollHeight;
bouton_envoyer.disabled = false;
bouton_envoyer.setAttribute("aria-busy", false);
bouton_envoyer.innerHTML = bouton_envoyer_innerHTML;
question.focus()
}, t);
})
function onResize() {
document.body.style.setProperty("--vph", `${window.visualViewport.height}px`); document.body.style.setProperty("--vph", `${window.visualViewport.height}px`);
conversation.scrollTop = conversation.scrollHeight; conversation.scrollTop = conversation.scrollHeight;
} }
window.visualViewport?.addEventListener('resize', onResize); window.visualViewport?.addEventListener('resize', onResize);
window.addEventListener('resize', onResize); window.addEventListener('resize', onResize);
onResize(); onResize();
</script> </script>
</body> </body>