Spaces:
Runtime error
Runtime error
Update index.html
Browse files- index.html +1 -164
index.html
CHANGED
|
@@ -1,164 +1 @@
|
|
| 1 |
-
<!DOCTYPE html>
|
| 2 |
-
<html lang="en">
|
| 3 |
-
<head>
|
| 4 |
-
<meta charset="UTF-8" />
|
| 5 |
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
-
<title>💛 Your Empathetic Friend</title>
|
| 7 |
-
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
-
<style>
|
| 9 |
-
body {
|
| 10 |
-
transition: background 1.5s ease, color 1s ease;
|
| 11 |
-
background: linear-gradient(135deg, #fef3c7, #fbbf24, #f59e0b);
|
| 12 |
-
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
| 13 |
-
}
|
| 14 |
-
.avatar {
|
| 15 |
-
animation: breathe 3s ease-in-out infinite;
|
| 16 |
-
filter: drop-shadow(0 0 15px rgba(251, 191, 36, 0.6));
|
| 17 |
-
}
|
| 18 |
-
@keyframes breathe {
|
| 19 |
-
0%, 100% { transform: scale(1); }
|
| 20 |
-
50% { transform: scale(1.05); }
|
| 21 |
-
}
|
| 22 |
-
.message {
|
| 23 |
-
animation: fadeIn 0.4s ease-out;
|
| 24 |
-
border-radius: 15px;
|
| 25 |
-
padding: 10px 15px;
|
| 26 |
-
margin-bottom: 10px;
|
| 27 |
-
max-width: 80%;
|
| 28 |
-
word-wrap: break-word;
|
| 29 |
-
}
|
| 30 |
-
@keyframes fadeIn {
|
| 31 |
-
from { opacity: 0; transform: translateY(20px); }
|
| 32 |
-
to { opacity: 1; transform: translateY(0); }
|
| 33 |
-
}
|
| 34 |
-
.loading {
|
| 35 |
-
display: inline-block;
|
| 36 |
-
width: 20px;
|
| 37 |
-
height: 20px;
|
| 38 |
-
border: 3px solid #fbbf24;
|
| 39 |
-
border-radius: 50%;
|
| 40 |
-
border-top-color: transparent;
|
| 41 |
-
animation: spin 1s linear infinite;
|
| 42 |
-
}
|
| 43 |
-
@keyframes spin {
|
| 44 |
-
to { transform: rotate(360deg); }
|
| 45 |
-
}
|
| 46 |
-
.user-message {
|
| 47 |
-
background: linear-gradient(135deg, #dbeafe, #bfdbfe, #93c5fd);
|
| 48 |
-
align-self: flex-end;
|
| 49 |
-
color: #1e40af;
|
| 50 |
-
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
| 51 |
-
}
|
| 52 |
-
.assistant-message {
|
| 53 |
-
background: linear-gradient(135deg, #fef3c7, #fde68a, #fcd34d);
|
| 54 |
-
align-self: flex-start;
|
| 55 |
-
color: #92400e;
|
| 56 |
-
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
| 57 |
-
}
|
| 58 |
-
.chat-box {
|
| 59 |
-
scrollbar-width: thin;
|
| 60 |
-
scrollbar-color: #fbbf24 #f3f4f6;
|
| 61 |
-
}
|
| 62 |
-
.chat-box::-webkit-scrollbar {
|
| 63 |
-
width: 6px;
|
| 64 |
-
}
|
| 65 |
-
.chat-box::-webkit-scrollbar-track {
|
| 66 |
-
background: #f3f4f6;
|
| 67 |
-
border-radius: 10px;
|
| 68 |
-
}
|
| 69 |
-
.chat-box::-webkit-scrollbar-thumb {
|
| 70 |
-
background: #fbbf24;
|
| 71 |
-
border-radius: 10px;
|
| 72 |
-
}
|
| 73 |
-
</style>
|
| 74 |
-
</head>
|
| 75 |
-
<body class="text-gray-800 flex flex-col items-center justify-center min-h-screen p-4 bg-gradient-to-br from-yellow-100 via-orange-100 to-pink-100">
|
| 76 |
-
<div class="avatar mb-6">
|
| 77 |
-
<svg width="120" height="120" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
| 78 |
-
<circle cx="50" cy="50" r="45" fill="#fbbf24" stroke="#f59e0b" stroke-width="5"/>
|
| 79 |
-
<circle cx="35" cy="40" r="5" fill="#374151"/>
|
| 80 |
-
<circle cx="65" cy="40" r="5" fill="#374151"/>
|
| 81 |
-
<path d="M 40 60 Q 50 70 60 60" stroke="#374151" stroke-width="3" fill="none"/>
|
| 82 |
-
</svg>
|
| 83 |
-
</div>
|
| 84 |
-
<h1 class="text-5xl font-extrabold mb-4 text-center text-gray-900 drop-shadow-lg bg-gradient-to-r from-yellow-600 to-orange-600 bg-clip-text text-transparent">💛 Your Empathetic Friend</h1>
|
| 85 |
-
<p class="text-center mb-6 text-gray-700 text-lg">I'm here to listen, support, and share knowledge with endless love. Let's connect! ❤️</p>
|
| 86 |
-
|
| 87 |
-
<div class="flex gap-4 mb-6 flex-wrap justify-center">
|
| 88 |
-
<button id="modeBtn" onclick="toggleMode()" class="bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white px-6 py-3 rounded-full shadow-xl transition transform hover:scale-105" aria-label="Toggle between Emotional Support and Knowledge modes">Mode: Emotional Support</button>
|
| 89 |
-
<button onclick="clearChat()" class="bg-gradient-to-r from-red-500 to-red-600 hover:from-red-600 hover:to-red-700 text-white px-6 py-3 rounded-full shadow-xl transition transform hover:scale-105" aria-label="Clear the chat history">Clear Chat</button>
|
| 90 |
-
<select id="voiceSelect" class="bg-gradient-to-r from-purple-500 to-purple-600 hover:from-purple-600 hover:to-purple-700 text-white px-6 py-3 rounded-full shadow-xl transition transform hover:scale-105" aria-label="Select voice for speech">
|
| 91 |
-
<option value="neutral">Neutral Voice</option>
|
| 92 |
-
<option value="calming_male">Calming Male</option>
|
| 93 |
-
<option value="calming_female">Calming Female</option>
|
| 94 |
-
<option value="soothing_supportive_male">Soothing Supportive Male</option>
|
| 95 |
-
<option value="soothing_supportive_female">Soothing Supportive Female</option>
|
| 96 |
-
<option value="motivating_gentle_male">Motivating Gentle Male</option>
|
| 97 |
-
<option value="loving_motivating_female">Loving Motivating Female</option>
|
| 98 |
-
<option value="deep_soar_female">Deep and Soar Female</option>
|
| 99 |
-
<option value="deep_soar_male">Deep and Soar Male</option>
|
| 100 |
-
</select>
|
| 101 |
-
</div>
|
| 102 |
-
|
| 103 |
-
<div id="chat-box" class="w-full max-w-2xl h-96 bg-white rounded-2xl shadow-2xl overflow-y-auto p-6 mb-6 flex flex-col chat-box" role="log" aria-live="polite" aria-label="Chat conversation"></div>
|
| 104 |
-
|
| 105 |
-
<div class="flex gap-3 w-full max-w-2xl">
|
| 106 |
-
<input id="message" type="text" class="flex-grow p-4 border-2 border-gray-300 rounded-full focus:outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-200 transition" placeholder="Share what's on your mind..." aria-label="Type your message" />
|
| 107 |
-
<button onclick="sendMessage()" class="bg-gradient-to-r from-green-500 to-green-600 hover:from-green-600 hover:to-green-700 text-white px-6 py-3 rounded-full shadow-xl transition transform hover:scale-105" aria-label="Send your message">Send</button>
|
| 108 |
-
</div>
|
| 109 |
-
|
| 110 |
-
<div class="flex gap-3 mt-6">
|
| 111 |
-
<button id="listenBtn" onclick="startListening()" class="bg-gradient-to-r from-purple-500 to-purple-600 hover:from-purple-600 hover:to-purple-700 text-white px-6 py-3 rounded-full shadow-xl transition transform hover:scale-105" aria-label="Start voice input" disabled>🎙️ Start Listening</button>
|
| 112 |
-
<button id="stopBtn" onclick="stopListening()" class="bg-gradient-to-r from-red-500 to-red-600 hover:from-red-600 hover:to-red-700 text-white px-6 py-3 rounded-full shadow-xl transition transform hover:scale-105" aria-label="Stop voice input" disabled>⏹️ Stop</button>
|
| 113 |
-
</div>
|
| 114 |
-
|
| 115 |
-
<script>
|
| 116 |
-
const chatBox = document.getElementById("chat-box");
|
| 117 |
-
const messageInput = document.getElementById("message");
|
| 118 |
-
const modeBtn = document.getElementById("modeBtn");
|
| 119 |
-
const listenBtn = document.getElementById("listenBtn");
|
| 120 |
-
const stopBtn = document.getElementById("stopBtn");
|
| 121 |
-
const voiceSelect = document.getElementById("voiceSelect");
|
| 122 |
-
let recognition;
|
| 123 |
-
let listening = false;
|
| 124 |
-
let currentMode = "emotional_support";
|
| 125 |
-
let selectedVoiceType = "neutral";
|
| 126 |
-
|
| 127 |
-
const emotionColors = {
|
| 128 |
-
joy: ["bg-gradient-to-br from-yellow-200 to-yellow-400", "bg-gradient-to-br from-yellow-400 to-orange-300", "bg-gradient-to-br from-orange-300 to-pink-200"],
|
| 129 |
-
sadness: ["bg-gradient-to-br from-blue-200 to-blue-400", "bg-gradient-to-br from-blue-400 to-indigo-300", "bg-gradient-to-br from-indigo-300 to-purple-200"],
|
| 130 |
-
anger: ["bg-gradient-to-br from-red-200 to-red-400", "bg-gradient-to-br from-red-400 to-pink-300", "bg-gradient-to-br from-pink-300 to-orange-200"],
|
| 131 |
-
calm: ["bg-gradient-to-br from-green-200 to-green-400", "bg-gradient-to-br from-green-400 to-teal-300", "bg-gradient-to-br from-teal-300 to-blue-200"],
|
| 132 |
-
optimism: ["bg-gradient-to-br from-orange-200 to-orange-400", "bg-gradient-to-br from-orange-400 to-yellow-300", "bg-gradient-to-br from-yellow-300 to-green-200"],
|
| 133 |
-
surprise: ["bg-gradient-to-br from-purple-200 to-purple-400", "bg-gradient-to-br from-purple-400 to-violet-300", "bg-gradient-to-br from-violet-300 to-pink-200"],
|
| 134 |
-
neutral: ["bg-gradient-to-br from-gray-200 to-gray-400", "bg-gradient-to-br from-gray-400 to-slate-300", "bg-gradient-to-br from-slate-300 to-blue-200"]
|
| 135 |
-
};
|
| 136 |
-
|
| 137 |
-
// Map voice types to browser voices
|
| 138 |
-
function getVoiceForType(type) {
|
| 139 |
-
const voices = speechSynthesis.getVoices();
|
| 140 |
-
let selectedVoice = voices.find(v => v.default) || voices[0];
|
| 141 |
-
switch (type) {
|
| 142 |
-
case "calming_male":
|
| 143 |
-
selectedVoice = voices.find(v => v.name.toLowerCase().includes("male") && v.lang.includes("en")) || voices.find(v => v.name.toLowerCase().includes("google") && v.lang.includes("en-US")) || selectedVoice;
|
| 144 |
-
break;
|
| 145 |
-
case "calming_female":
|
| 146 |
-
selectedVoice = voices.find(v => v.name.toLowerCase().includes("female") && v.lang.includes("en")) || voices.find(v => v.name.toLowerCase().includes("zira") || v.name.toLowerCase().includes("samantha")) || selectedVoice;
|
| 147 |
-
break;
|
| 148 |
-
case "soothing_supportive_male":
|
| 149 |
-
selectedVoice = voices.find(v => v.name.toLowerCase().includes("male") && v.lang.includes("en")) || selectedVoice;
|
| 150 |
-
break;
|
| 151 |
-
case "soothing_supportive_female":
|
| 152 |
-
selectedVoice = voices.find(v => v.name.toLowerCase().includes("female") && v.lang.includes("en")) || selectedVoice;
|
| 153 |
-
break;
|
| 154 |
-
case "motivating_gentle_male":
|
| 155 |
-
selectedVoice = voices.find(v => v.name.toLowerCase().includes("male") && v.lang.includes("en")) || selectedVoice;
|
| 156 |
-
break;
|
| 157 |
-
case "loving_motivating_female":
|
| 158 |
-
selectedVoice = voices.find(v => v.name.toLowerCase().includes("female") && v.lang.includes("en")) || selectedVoice;
|
| 159 |
-
break;
|
| 160 |
-
case "neutral":
|
| 161 |
-
selectedVoice = voices.find(v => v.default) || voices[0];
|
| 162 |
-
break;
|
| 163 |
-
case "deep_soar_female":
|
| 164 |
-
selectedVoice = voices.find(v => v.name.toLowerCase().includes("female") && v.lang.includes
|
|
|
|
| 1 |
+
<!DOCTYPE html> <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 2s ease, color 1s ease; background: linear-gradient(135deg, #fef3c7, #fbbf24); } .avatar { animation: breathe 3s ease-in-out infinite; } @keyframes breathe { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.05); } } .message { animation: fadeIn 0.5s ease-in; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .loading { display: inline-block; width: 20px; height: 20px; border: 3px solid #fbbf24; border-radius: 50%; border-top-color: transparent; animation: spin 1s ease-in-out infinite; } @keyframes spin { to { transform: rotate(360deg); } } </style> </head> <body class="text-gray-800 flex flex-col items-center justify-center min-h-screen p-4"> <div class="avatar mb-4"> <svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"> <circle cx="50" cy="50" r="45" fill="#fbbf24" stroke="#f59e0b" stroke-width="5"/> <circle cx="35" cy="40" r="5" fill="#374151"/> <circle cx="65" cy="40" r="5" fill="#374151"/> <path d="M 40 60 Q 50 70 60 60" stroke="#374151" stroke-width="3" fill="none"/> </svg> </div> <h1 class="text-4xl font-bold mb-6 text-center text-gray-900">💛 Your Empathetic Friend</h1> <p class="text-center mb-4 text-gray-700">I'm here to listen, support, and share knowledge. Let's connect!</p> <div class="flex gap-4 mb-4"> <button id="modeBtn" onclick="toggleMode()" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg shadow-lg transition" aria-label="Toggle between Emotional Support and Knowledge modes">Mode: Emotional Support</button> <button onclick="clearChat()" class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg shadow-lg transition" aria-label="Clear the chat history">Clear Chat</button> </div> <div id="chat-box" class="w-full max-w-lg h-96 bg-white rounded-lg shadow-xl overflow-y-auto p-4 mb-4" role="log" aria-live="polite" aria-label="Chat conversation"></div> <div class="flex gap-2 w-full max-w-lg"> <input id="message" type="text" class="flex-grow p-3 border-2 border-gray-300 rounded-lg focus:outline-none focus:border-blue-500" placeholder="Share what's on your mind..." aria-label="Type your message" /> <button onclick="sendMessage()" class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded-lg shadow-lg transition" aria-label="Send your message">Send</button> </div> <div class="flex gap-2 mt-4"> <button id="listenBtn" onclick="startListening()" class="bg-purple-500 hover:bg-purple-600 text-white px-4 py-2 rounded-lg shadow-lg transition" aria-label="Start voice input" disabled>🎙️ Start Listening</button> <button id="stopBtn" onclick="stopListening()" class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg shadow-lg transition" aria-label="Stop voice input" disabled>⏹️ Stop</button> </div> <script> const chatBox = document.getElementById("chat-box"); const messageInput = document.getElementById("message"); const modeBtn = document.getElementById("modeBtn"); const listenBtn = document.getElementById("listenBtn"); const stopBtn = document.getElementById("stopBtn"); let recognition; let listening = false; let currentMode = "emotional_support"; const emotionColors = { joy: ["bg-gradient-to-r from-yellow-200 to-yellow-300", "bg-gradient-to-r from-yellow-300 to-orange-200"], sadness: ["bg-gradient-to-r from-blue-200 to-blue-300", "bg-gradient-to-r from-blue-300 to-indigo-200"], anger: ["bg-gradient-to-r from-red-200 to-red-300", "bg-gradient-to-r from-red-300 to-pink-200"], calm: ["bg-gradient-to-r from-green-200 to-green-300", "bg-gradient-to-r from-green-300 to-teal-200"], optimism: ["bg-gradient-to-r from-orange-200 to-orange-300", "bg-gradient-to-r from-orange-300 to-yellow-200"], surprise: ["bg-gradient-to-r from-purple-200 to-purple-300", "bg-gradient-to-r from-purple-300 to-violet-200"], neutral: ["bg-gradient-to-r from-gray-200 to-gray-300", "bg-gradient-to-r from-gray-300 to-slate-200"] }; // Load chat history from localStorage function loadHistory() { const history = JSON.parse(localStorage.getItem("chatHistory") || "[]"); history.forEach(msg => appendMessage(msg.sender, msg.text, false)); } // Save chat history to localStorage function saveHistory() { const messages = Array.from(chatBox.children).map(div => ({ sender: div.querySelector("strong").textContent.replace(":", ""), text: div.textContent.split(": ")[1] })); localStorage.setItem("chatHistory", JSON.stringify(messages)); } async function sendMessage(voiceConfirmed = false) { const message = messageInput.value.trim(); if (!message) return; if (!voiceConfirmed && recognition) { if (!confirm("Send this voice message?")) return; } appendMessage("You", message); messageInput.value = ""; showLoading(true); try { const res = await fetch("/chat", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ message, mode: currentMode }), }); const data = await res.json(); appendMessage("Assistant", data.reply); updateBackground(data.emotion); speak(data.reply); saveHistory(); } catch (error) { appendMessage("Assistant", "Oops, something went wrong. Please try again."); } finally { showLoading(false); } } function appendMessage(sender, text, save = true) { const div = document.createElement("div"); div.className = `message ${sender === "You" ? "text-right mb-2" : "text-left mb-2"}`; div.innerHTML = `<strong>${sender}:</strong> ${text}`; chatBox.appendChild(div); chatBox.scrollTop = chatBox.scrollHeight; if (save) saveHistory(); } function updateBackground(emotion) { const colors = emotionColors[emotion.toLowerCase()] || emotionColors["neutral"]; const randomColor = colors[Math.floor(Math.random() * colors.length)]; document.body.className = `transition-all min-h-screen flex flex-col items-center justify-center p-4 ${randomColor}`; } function speak(text) { if ("speechSynthesis" in window) { const speech = new SpeechSynthesisUtterance(text); speech.rate = 1; speech.pitch = 1; speech.lang = "en-US"; window.speechSynthesis.speak(speech); } else { alert("Speech synthesis not supported in your browser."); } } function startListening() { if (!("webkitSpeechRecognition" in window)) { alert("Speech recognition not supported in your browser."); return; } recognition = new webkitSpeechRecognition(); recognition.continuous = false; recognition.interimResults = false; recognition.lang = "en-US"; recognition.onstart = () => { listening = true; listenBtn.disabled = true; stopBtn.disabled = false; appendMessage("System", "Listening..."); }; recognition.onresult = (event) => { const transcript = event.results[0][0].transcript; appendMessage("You (voice)", transcript); messageInput.value = transcript; sendMessage(true); // Auto-send with confirmation }; recognition.onend = () => { listening = false; listenBtn.disabled = false; stopBtn.disabled = true; }; recognition.start(); } function stopListening() { if (recognition && listening) { recognition.stop(); listening = false; listenBtn.disabled = false; stopBtn.disabled = true; appendMessage("System", "Stopped listening."); window.speechSynthesis.cancel(); } } function toggleMode() { currentMode = currentMode === "emotional_support" ? "knowledge" : "emotional_support"; modeBtn.textContent = `Mode: ${currentMode === "emotional_support" ? "Emotional Support" : "Knowledge"}`; } function clearChat() { chatBox.innerHTML = ""; localStorage.removeItem("chatHistory"); } function showLoading(show) { const btn = document.querySelector("button[onclick='sendMessage()']"); if (show) { btn.innerHTML = '<div class="loading"></div>'; btn.disabled = true; } else { btn.innerHTML = "Send"; btn.disabled = false; } } // Initialize loadHistory(); appendMessage("Assistant", "Hi there! I'm your empathetic friend. How are you feeling today? Let's talk."); </script> </body> </html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|