Spaces:
Runtime error
Runtime error
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| <title>π Emotional Support Assistant</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <style> | |
| body { transition: background 1s ease; } | |
| #mic-indicator { | |
| width: 20px; height: 20px; border-radius: 50%; | |
| margin-left: 10px; display: inline-block; | |
| background-color: gray; | |
| box-shadow: 0 0 5px rgba(0,0,0,0.2); | |
| transition: background-color 0.3s ease, box-shadow 0.3s ease; | |
| } | |
| .listening { | |
| background-color: #22c55e; | |
| box-shadow: 0 0 15px #22c55e; | |
| } | |
| .speaking { | |
| animation: pulse 1s infinite; | |
| } | |
| @keyframes pulse { | |
| 0% { opacity: 0.3; } | |
| 50% { opacity: 1; } | |
| 100% { opacity: 0.3; } | |
| } | |
| </style> | |
| </head> | |
| <body class="min-h-screen flex flex-col items-center justify-center bg-gradient-to-br from-yellow-100 to-yellow-300 p-4"> | |
| <h1 class="text-2xl font-bold mb-4 text-center">π Emotional Support Assistant</h1> | |
| <div id="chat-box" class="w-full max-w-2xl h-96 overflow-y-auto bg-white p-4 rounded shadow mb-4 border"></div> | |
| <div class="flex gap-2 w-full max-w-2xl"> | |
| <input id="user-input" type="text" placeholder="Type your message..." | |
| class="flex-1 border p-2 rounded" /> | |
| <button id="send-btn" class="bg-blue-500 text-white px-4 py-2 rounded">Send</button> | |
| </div> | |
| <div class="mt-4 flex items-center gap-3"> | |
| <button id="start-btn" class="bg-green-500 text-white px-4 py-2 rounded">ποΈ Start Listening</button> | |
| <button id="stop-btn" class="bg-red-500 text-white px-4 py-2 rounded">βΉοΈ Stop</button> | |
| <div id="mic-indicator" title="Mic status"></div> | |
| </div> | |
| <div id="speaking-status" class="text-gray-600 mt-3 hidden"> | |
| π€ Bot is speaking... | |
| </div> | |
| <script> | |
| const chatBox = document.getElementById("chat-box"); | |
| const userInput = document.getElementById("user-input"); | |
| const sendBtn = document.getElementById("send-btn"); | |
| const startBtn = document.getElementById("start-btn"); | |
| const stopBtn = document.getElementById("stop-btn"); | |
| const micIndicator = document.getElementById("mic-indicator"); | |
| const speakingStatus = document.getElementById("speaking-status"); | |
| let recognition; | |
| let isListening = false; | |
| let synth = window.speechSynthesis; | |
| let currentUtterance = null; | |
| // π€ Initialize SpeechRecognition | |
| if ("webkitSpeechRecognition" in window || "SpeechRecognition" in window) { | |
| const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; | |
| recognition = new SpeechRecognition(); | |
| recognition.continuous = true; | |
| recognition.interimResults = false; | |
| recognition.lang = "en-US"; | |
| recognition.onstart = () => { | |
| micIndicator.classList.add("listening"); | |
| isListening = true; | |
| startBtn.disabled = true; | |
| }; | |
| recognition.onend = () => { | |
| micIndicator.classList.remove("listening"); | |
| isListening = false; | |
| startBtn.disabled = false; | |
| }; | |
| recognition.onresult = (event) => { | |
| const text = event.results[event.results.length - 1][0].transcript.trim(); | |
| userInput.value = text; | |
| sendMessage(); | |
| }; | |
| recognition.onerror = (event) => { | |
| console.error("Speech recognition error:", event.error); | |
| micIndicator.classList.remove("listening"); | |
| isListening = false; | |
| startBtn.disabled = false; | |
| }; | |
| } else { | |
| alert("Speech recognition not supported in this browser. Try using Chrome."); | |
| } | |
| // π¬ Send user message to Flask backend | |
| async function sendMessage() { | |
| const text = userInput.value.trim(); | |
| if (!text) return; | |
| addMessage("π§ You", text); | |
| userInput.value = ""; | |
| try { | |
| const res = await fetch("/chat", { | |
| method: "POST", | |
| headers: { "Content-Type": "application/json" }, | |
| body: JSON.stringify({ message: text }), | |
| }); | |
| const data = await res.json(); | |
| addMessage("π€ Bot", data.text); | |
| speakText(data.text); | |
| updateBackground(data.emotion); | |
| } catch (err) { | |
| console.error("Error:", err); | |
| } | |
| } | |
| // π£οΈ Speak text aloud and show animation | |
| function speakText(text) { | |
| if (synth.speaking) synth.cancel(); | |
| currentUtterance = new SpeechSynthesisUtterance(text); | |
| currentUtterance.lang = "en-US"; | |
| speakingStatus.classList.remove("hidden"); | |
| speakingStatus.classList.add("speaking"); | |
| currentUtterance.onend = () => { | |
| speakingStatus.classList.add("hidden"); | |
| speakingStatus.classList.remove("speaking"); | |
| }; | |
| synth.speak(currentUtterance); | |
| } | |
| // π§ Button handlers | |
| startBtn.onclick = () => { | |
| if (recognition && !isListening) recognition.start(); | |
| }; | |
| stopBtn.onclick = () => { | |
| if (isListening && recognition) recognition.stop(); | |
| if (synth.speaking) synth.cancel(); | |
| isListening = false; | |
| micIndicator.classList.remove("listening"); | |
| speakingStatus.classList.add("hidden"); | |
| startBtn.disabled = false; | |
| }; | |
| sendBtn.onclick = sendMessage; | |
| // π¬ Display messages in chat | |
| function addMessage(sender, text) { | |
| const bubble = document.createElement("div"); | |
| bubble.className = sender.includes("Bot") | |
| ? "text-left mb-2 bg-yellow-100 p-2 rounded" | |
| : "text-right mb-2 bg-blue-100 p-2 rounded"; | |
| bubble.innerHTML = `<strong>${sender}:</strong> ${text}`; | |
| chatBox.appendChild(bubble); | |
| chatBox.scrollTop = chatBox.scrollHeight; | |
| } | |
| // π¨ Change background smoothly by emotion | |
| function updateBackground(emotion) { | |
| let color; | |
| switch (emotion?.toLowerCase()) { | |
| case "happy": color = "linear-gradient(135deg, #fff176, #ffd54f)"; break; | |
| case "sad": color = "linear-gradient(135deg, #64b5f6, #1976d2)"; break; | |
| case "angry": color = "linear-gradient(135deg, #ff7043, #f44336)"; break; | |
| case "calm": color = "linear-gradient(135deg, #a5d6a7, #66bb6a)"; break; | |
| case "motivated": color = "linear-gradient(135deg, #ffb74d, #fb8c00)"; break; | |
| default: color = "linear-gradient(135deg, #e0e0e0, #bdbdbd)"; | |
| } | |
| document.body.style.background = color; | |
| } | |
| </script> | |
| </body> | |
| </html> |