| <!DOCTYPE html> |
| <html lang="fr"> |
| <head> |
| <meta charset="utf-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Rosalinda — Espace Codage</title> |
| <link rel="stylesheet" href="style.css"> |
| <script src="script.js"></script> |
| <style> |
| .rosalinda-container { |
| max-width: 980px; |
| margin: 0 auto; |
| padding: 18px; |
| height: calc(100vh - 80px); |
| display: flex; |
| flex-direction: column; |
| } |
| .rosalinda-top { |
| display: flex; |
| gap: 10px; |
| align-items: center; |
| justify-content: space-between; |
| margin-bottom: 14px; |
| } |
| .rosalinda-badge { |
| padding: 8px 12px; |
| border: 1px solid var(--border); |
| border-radius: 999px; |
| background: var(--bg-panel); |
| font-size: 14px; |
| } |
| .rosalinda-chat { |
| flex: 1; |
| border: 1px solid var(--border); |
| border-radius: 16px; |
| overflow: hidden; |
| background: var(--bg-panel); |
| display: flex; |
| flex-direction: column; |
| } |
| .rosalinda-msgs { |
| flex: 1; |
| overflow: auto; |
| padding: 14px; |
| display: flex; |
| flex-direction: column; |
| gap: 10px; |
| } |
| .rosalinda-msg { |
| max-width: 85%; |
| padding: 10px 12px; |
| border-radius: 14px; |
| border: 1px solid rgba(255,255,255,.10); |
| font-size: 14px; |
| line-height: 1.5; |
| } |
| .rosalinda-me { |
| margin-left: auto; |
| background: rgba(93,173,255,.10); |
| } |
| .rosalinda-ai { |
| background: rgba(255,255,255,.04); |
| } |
| .rosalinda-row { |
| display: flex; |
| gap: 10px; |
| align-items: center; |
| padding: 12px; |
| border-top: 1px solid var(--border); |
| background: rgba(0,0,0,.2); |
| } |
| .rosalinda-input { |
| flex: 1; |
| background: transparent; |
| border: 1px solid var(--border); |
| border-radius: 14px; |
| padding: 12px; |
| color: var(--text-light); |
| outline: none; |
| } |
| .rosalinda-btn { |
| width: 42px; |
| height: 42px; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| background: rgba(255,255,255,.05); |
| border: 1px solid var(--border); |
| color: var(--text-light); |
| border-radius: 14px; |
| cursor: pointer; |
| } |
| .rosalinda-btn:hover { |
| background: rgba(255,255,255,.08); |
| } |
| .rosalinda-btn:disabled { |
| opacity: .5; |
| cursor: not-allowed; |
| } |
| .rosalinda-send { |
| background: var(--primary); |
| } |
| .rosalinda-send:hover:not(:disabled) { |
| background: var(--primary-hover); |
| } |
| .rosalinda-info { |
| color: var(--text-muted); |
| font-size: 12px; |
| margin-top: 10px; |
| line-height: 1.5; |
| } |
| </style> |
| </head> |
| <body class="bg-gray-900 text-gray-100"> |
| <custom-navbar></custom-navbar> |
| <div class="rosalinda-container"> |
| <div class="rosalinda-top"> |
| <div class="rosalinda-badge"><b>Rosalinda</b> — Espace Codage</div> |
| <div class="rosalinda-badge" id="rosalindaStatus">Micro: prêt</div> |
| </div> |
|
|
| <div class="rosalinda-chat"> |
| <div class="rosalinda-msgs" id="rosalindaMsgs"></div> |
| <div class="rosalinda-row"> |
| <button class="rosalinda-btn" id="rosalindaMicBtn">🎤</button> |
| <input class="rosalinda-input" id="rosalindaInput" placeholder="Écris à Rosalinda…"> |
| <button class="rosalinda-btn rosalinda-send" id="rosalindaSendBtn">➤</button> |
| <button class="rosalinda-btn" id="rosalindaSpeakBtn" title="Lire la dernière réponse">🔊</button> |
| </div> |
| </div> |
|
|
| <div class="rosalinda-info"> |
| ✅ Micro & voix = API du navigateur (Chrome/Edge).<br> |
| ⚠️ Cette version répond avec une "IA locale simple" (règles). Étape suivante : brancher un vrai modèle IA (local ou API). |
| </div> |
| </div> |
|
|
| <script> |
| const rosalindaMsgs = document.getElementById("rosalindaMsgs"); |
| const rosalindaInput = document.getElementById("rosalindaInput"); |
| const rosalindaSendBtn = document.getElementById("rosalindaSendBtn"); |
| const rosalindaMicBtn = document.getElementById("rosalindaMicBtn"); |
| const rosalindaSpeakBtn = document.getElementById("rosalindaSpeakBtn"); |
| const rosalindaStatus = document.getElementById("rosalindaStatus"); |
| |
| let lastAIText = ""; |
| |
| function addRosalindaMsg(text, who) { |
| const div = document.createElement("div"); |
| div.className = `rosalinda-msg ${who === "me" ? "rosalinda-me" : "rosalinda-ai"}`; |
| div.textContent = text; |
| rosalindaMsgs.appendChild(div); |
| rosalindaMsgs.scrollTop = rosalindaMsgs.scrollHeight; |
| } |
| |
| function rosalindaBrain(userText) { |
| const t = userText.toLowerCase(); |
| if (t.includes("bonjour") || t.includes("salut")) return "Bonjour 😄 Je suis Rosalinda. Dis-moi ce que tu veux créer : site, thème, image, vidéo, plugin…"; |
| if (t.includes("theme") || t.includes("thème")) return "Ok ✅ Dis-moi : (1) style (moderne, luxe, minimal, flashy), (2) couleurs, (3) 3 colonnes ou non, (4) Shopify/WooCommerce/autre."; |
| if (t.includes("image")) return "Je peux préparer une demande d'image. Dis-moi : sujet + style + format (1:1, 16:9, 9:16) + texte à afficher."; |
| if (t.includes("vidéo") || t.includes("video")) return "Je peux préparer une demande vidéo. Dis-moi : durée, style (réaliste/3D), texte à l'écran, musique oui/non."; |
| if (t.includes("micro")) return "Pour le micro : clique 🎤, autorise le micro dans ton navigateur, puis parle. Je transcris et je réponds."; |
| return "Compris ✅ Donne-moi plus de détails (objectif + plateforme + style), et je te génère une réponse claire."; |
| } |
| |
| function rosalindaSpeak(text) { |
| if (!("speechSynthesis" in window)) { |
| alert("Synthèse vocale non supportée sur ce navigateur."); |
| return; |
| } |
| const utterance = new SpeechSynthesisUtterance(text); |
| utterance.lang = "fr-FR"; |
| window.speechSynthesis.cancel(); |
| window.speechSynthesis.speak(utterance); |
| } |
| |
| function handleRosalindaSend(text) { |
| const value = (text ?? rosalindaInput.value).trim(); |
| if (!value) return; |
| addRosalindaMsg(value, "me"); |
| rosalindaInput.value = ""; |
| const reply = rosalindaBrain(value); |
| lastAIText = reply; |
| addRosalindaMsg(reply, "ai"); |
| rosalindaSpeak(reply); |
| } |
| |
| rosalindaSendBtn.onclick = () => handleRosalindaSend(); |
| rosalindaInput.addEventListener("keydown", (e) => { |
| if (e.key === "Enter") handleRosalindaSend(); |
| }); |
| |
| rosalindaSpeakBtn.onclick = () => lastAIText && rosalindaSpeak(lastAIText); |
| |
| |
| let rosalindaRec = null; |
| let rosalindaListening = false; |
| |
| function setupRosalindaSTT() { |
| const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; |
| if (!SpeechRecognition) return null; |
| const recognition = new SpeechRecognition(); |
| recognition.lang = "fr-FR"; |
| recognition.interimResults = true; |
| recognition.continuous = false; |
| return recognition; |
| } |
| |
| rosalindaRec = setupRosalindaSTT(); |
| |
| rosalindaMicBtn.onclick = async () => { |
| if (!rosalindaRec) { |
| alert("Reconnaissance vocale non disponible ici. Essaie Chrome/Edge."); |
| return; |
| } |
| if (rosalindaListening) return; |
| |
| rosalindaListening = true; |
| rosalindaStatus.textContent = "Micro: écoute…"; |
| let finalText = ""; |
| |
| rosalindaRec.onresult = (e) => { |
| let transcript = ""; |
| for (let i = e.resultIndex; i < e.results.length; i++) { |
| transcript += e.results[i][0].transcript; |
| if (e.results[i].isFinal) finalText += e.results[i][0].transcript + " "; |
| } |
| rosalindaInput.value = (finalText || transcript).trim(); |
| }; |
| |
| rosalindaRec.onerror = () => { |
| rosalindaListening = false; |
| rosalindaStatus.textContent = "Micro: erreur"; |
| }; |
| |
| rosalindaRec.onend = () => { |
| rosalindaListening = false; |
| rosalindaStatus.textContent = "Micro: prêt"; |
| if (rosalindaInput.value.trim()) handleRosalindaSend(rosalindaInput.value); |
| }; |
| |
| try { rosalindaRec.start(); } catch (e) { |
| rosalindaListening = false; |
| rosalindaStatus.textContent = "Micro: prêt"; |
| } |
| }; |
| |
| |
| addRosalindaMsg("Bonjour 👋 Je suis Rosalinda. Clique 🎤 pour parler ou écris-moi.", "ai"); |
| </script> |
| <script src="components/navbar.js"></script> |
| <script src="script.js"></script> |
| </body> |
| </html> |