Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
<title>Emma's Genie</title> | |
<style> | |
body { | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
min-height: 100vh; | |
margin: 0; | |
font-family: "Inter", system-ui, -apple-system, sans-serif; | |
background: linear-gradient(135deg, #f6f8fb 0%, #e9eef5 100%); | |
} | |
.chat-container { | |
width: 100%; | |
max-width: 400px; | |
border-radius: 16px; | |
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12), | |
0 2px 8px rgba(0, 0, 0, 0.08); | |
overflow: hidden; | |
display: flex; | |
flex-direction: column; | |
background-color: white; | |
height: 600px; | |
transition: transform 0.2s ease; | |
} | |
.chat-container:hover { | |
transform: translateY(-2px); | |
} | |
.messages { | |
flex-grow: 1; | |
padding: 20px; | |
overflow-y: auto; | |
background-color: #ffffff; | |
scroll-behavior: smooth; | |
} | |
.messages::-webkit-scrollbar { | |
width: 6px; | |
} | |
.messages::-webkit-scrollbar-thumb { | |
background-color: #cbd5e1; | |
border-radius: 3px; | |
} | |
.message { | |
margin-bottom: 16px; | |
max-width: 80%; | |
padding: 12px 16px; | |
border-radius: 12px; | |
font-size: 14px; | |
line-height: 1.5; | |
animation: fadeIn 0.3s ease; | |
} | |
@keyframes fadeIn { | |
from { | |
opacity: 0; | |
transform: translateY(10px); | |
} | |
to { | |
opacity: 1; | |
transform: translateY(0); | |
} | |
} | |
.message.user { | |
margin-left: auto; | |
background-color: #2563eb; | |
color: white; | |
border-bottom-right-radius: 4px; | |
} | |
.message.ai { | |
background-color: #f3f4f6; | |
color: #1f2937; | |
border-bottom-left-radius: 4px; | |
} | |
.typing-indicator { | |
margin-bottom: 16px; | |
padding: 12px 16px; | |
background-color: #f3f4f6; | |
border-radius: 12px; | |
border-bottom-left-radius: 4px; | |
width: fit-content; | |
} | |
.typing-indicator span { | |
display: inline-block; | |
width: 8px; | |
height: 8px; | |
background-color: #94a3b8; | |
border-radius: 50%; | |
margin-right: 4px; | |
animation: bounce 1.4s infinite ease-in-out; | |
} | |
.typing-indicator span:nth-child(1) { | |
animation-delay: -0.32s; | |
} | |
.typing-indicator span:nth-child(2) { | |
animation-delay: -0.16s; | |
} | |
.typing-indicator span:nth-child(3) { | |
animation-delay: 0s; | |
} | |
@keyframes subtleBounce { | |
0%, 100% { | |
transform: translateY(0); | |
} | |
50% { | |
transform: translateY(-1px); | |
} | |
} | |
.subtle-bounce { | |
animation: subtleBounce 0.1s ease; | |
} | |
#chat-form { | |
display: flex; | |
padding: 16px; | |
gap: 8px; | |
background-color: #fff; | |
border-top: 1px solid #eef2f6; | |
} | |
#user-input { | |
flex-grow: 1; | |
padding: 12px 16px; | |
border: 2px solid #e5e7eb; | |
border-radius: 8px; | |
font-size: 14px; | |
transition: border-color 0.2s ease; | |
outline: none; | |
} | |
#user-input:focus { | |
border-color: #2563eb; | |
} | |
button { | |
padding: 12px 24px; | |
background-color: #2563eb; | |
color: white; | |
border: none; | |
border-radius: 8px; | |
font-weight: 500; | |
cursor: pointer; | |
transition: all 0.2s ease; | |
} | |
button:hover { | |
background-color: #1d4ed8; | |
transform: translateY(-1px); | |
} | |
button:active { | |
transform: translateY(0); | |
} | |
</style> | |
</head> | |
<body> | |
<div id="app"> | |
<div class="chat-container" id="chat-container"> | |
<div id="messages" class="messages"></div> | |
<form id="chat-form"> | |
<input | |
type="text" | |
id="user-input" | |
placeholder="Ask anything.." | |
autocomplete="off" | |
required | |
/> | |
<button type="submit">Send</button> | |
</form> | |
</div> | |
</div> | |
<script type="module"> | |
import { Client } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js"; | |
document.addEventListener("DOMContentLoaded", async () => { | |
const chatContainer = document.getElementById("chat-container"); | |
const messagesDiv = document.getElementById("messages"); | |
const chatForm = document.getElementById("chat-form"); | |
const userInput = document.getElementById("user-input"); | |
const client = await Client.connect("nroggendorff/not-my-emma"); | |
chatForm.addEventListener("submit", async (e) => { | |
e.preventDefault(); | |
const userMessage = userInput.value; | |
if (!userMessage) return; | |
userInput.value = ""; | |
addMessage(userMessage, "user"); | |
const typingIndicator = document.createElement("div"); | |
typingIndicator.className = "typing-indicator"; | |
typingIndicator.innerHTML = ` | |
<span></span> | |
<span></span> | |
<span></span> | |
`; | |
messagesDiv.appendChild(typingIndicator); | |
messagesDiv.scrollTop = messagesDiv.scrollHeight; | |
try { | |
const result = await client.predict("/chat", { | |
message: userMessage, | |
}); | |
typingIndicator.remove(); | |
addMessage(result.data, "ai"); | |
} catch (error) { | |
typingIndicator.remove(); | |
addMessage( | |
"Sorry, I encountered an error. Please try again.", | |
"ai" | |
); | |
} | |
}); | |
function addMessage(message, sender) { | |
const messageElement = document.createElement("div"); | |
messageElement.classList.add("message", sender); | |
messageElement.textContent = message; | |
messagesDiv.appendChild(messageElement); | |
messagesDiv.scrollTop = messagesDiv.scrollHeight; | |
} | |
userInput.addEventListener("input", () => { | |
chatContainer.classList.add("subtle-bounce"); | |
setTimeout(() => chatContainer.classList.remove("subtle-bounce"), 100); | |
}); | |
}); | |
</script> | |
</body> | |
</html> |