Spaces:
No application file
No application file
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Responsive Chatbot Widget</title> | |
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet"> | |
<style> | |
/* Reset and Base Styles */ | |
* { | |
box-sizing: border-box; | |
margin: 0; | |
padding: 0; | |
} | |
body { | |
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; | |
line-height: 1.5; | |
color: #333; | |
background-color: #f5f5f5; | |
} | |
/* Floating Chatbot Icon */ | |
#chatbotIcon { | |
position: fixed; | |
bottom: 20px; | |
right: 20px; | |
width: 60px; | |
height: 60px; | |
/* background: linear-gradient(135deg, #6366F1, #A855F7); */ | |
/* color: white; */ | |
border-radius: 50%; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
font-size: 30px; | |
cursor: pointer; | |
/* transition: transform 0.3s, box-shadow 0.3s; */ | |
/* box-shadow: 0 4px 15px rgba(99, 102, 241, 0.4); */ | |
z-index: 1000; | |
user-select: none; | |
} | |
/* #chatbotIcon:hover { | |
transform: scale(1.05); | |
box-shadow: 0 6px 20px rgba(99, 102, 241, 0.5); | |
} */ | |
#chatimg{ | |
position: fixed; | |
bottom: 20px; | |
right: 20px; | |
width: 80px; | |
height: 80px; | |
border-radius: 50%; | |
box-shadow: 0 4px 15px rgba(99, 102, 241, 0.4); | |
transition: transform 0.3s, box-shadow 0.3s; | |
z-index: 1000; | |
user-select: none; | |
border-radius: 50%; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
font-size: 30px; | |
cursor: pointer; | |
} | |
#chatimg:hover { | |
transform: scale(1.05); | |
box-shadow: 0 6px 20px rgba(99, 102, 241, 0.5); | |
} | |
/* Chatbot Window */ | |
#chatbotContainer { | |
position: fixed; | |
bottom: 90px; | |
right: 20px; | |
width: 350px; | |
max-width: 90vw; | |
background: white; | |
border-radius: 16px; | |
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15); | |
display: none; | |
flex-direction: column; | |
overflow: hidden; | |
z-index: 999; | |
height: 500px; | |
max-height: 80vh; | |
} | |
/* Chatbot Header */ | |
#chatbotHeader { | |
background: linear-gradient(135deg, #6366F1, #A855F7); | |
color: white; | |
padding: 15px; | |
font-weight: 600; | |
position: relative; | |
font-size: 16px; | |
} | |
#closeChat { | |
position: absolute; | |
right: 12px; | |
top: 50%; | |
transform: translateY(-50%); | |
cursor: pointer; | |
font-size: 18px; | |
opacity: 0.8; | |
transition: opacity 0.2s; | |
height: 24px; | |
width: 24px; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
border-radius: 50%; | |
} | |
#closeChat:hover { | |
opacity: 1; | |
background-color: rgba(255, 255, 255, 0.2); | |
} | |
/* Chat Messages */ | |
#chatMessages { | |
flex: 1; | |
overflow-y: auto; | |
padding: 15px; | |
background: #f5f7fa; | |
display: flex; | |
flex-direction: column; | |
} | |
/* Chat Bubbles */ | |
.message { | |
padding: 12px 16px; | |
margin: 5px 0; | |
border-radius: 14px; | |
max-width: 80%; | |
word-wrap: break-word; | |
position: relative; | |
animation: fadeIn 0.3s ease; | |
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); | |
line-height: 1.4; | |
} | |
@keyframes fadeIn { | |
from { opacity: 0; transform: translateY(10px); } | |
to { opacity: 1; transform: translateY(0); } | |
} | |
/* User Message */ | |
.user { | |
background: linear-gradient(135deg, #6366F1, #A855F7); | |
color: white; | |
align-self: flex-end; | |
border-bottom-right-radius: 2px; | |
} | |
/* Bot Message */ | |
.bot { | |
background: white; | |
color: #333; | |
align-self: flex-start; | |
border-bottom-left-radius: 2px; | |
} | |
/* User Input */ | |
#chatInput { | |
display: flex; | |
padding: 12px; | |
border-top: 1px solid #eaeaea; | |
background: white; | |
align-items: center; | |
} | |
#userMessage { | |
flex: 1; | |
padding: 12px 16px; | |
border: none; | |
border-radius: 24px; | |
background-color: #f0f0f0; | |
outline: none; | |
font-size: 14px; | |
font-family: inherit; | |
} | |
#sendMessage, #micButton { | |
background: linear-gradient(135deg, #6366F1, #A855F7); | |
color: white; | |
border: none; | |
border-radius: 50%; | |
width: 40px; | |
height: 40px; | |
margin-left: 10px; | |
cursor: pointer; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
transition: transform 0.2s; | |
flex-shrink: 0; | |
} | |
#sendMessage:hover, #micButton:hover { | |
transform: scale(1.05); | |
} | |
/* Typing indicator */ | |
.typing-indicator { | |
display: flex; | |
padding: 12px 16px; | |
background-color: white; | |
border-radius: 14px; | |
width: fit-content; | |
margin: 5px 0; | |
align-self: flex-start; | |
border-bottom-left-radius: 2px; | |
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); | |
} | |
.typing-indicator span { | |
height: 8px; | |
width: 8px; | |
background-color: #ccc; | |
border-radius: 50%; | |
display: inline-block; | |
margin-right: 5px; | |
animation: bounce 1.3s linear infinite; | |
} | |
.typing-indicator span:nth-child(2) { | |
animation-delay: 0.15s; | |
} | |
.typing-indicator span:nth-child(3) { | |
animation-delay: 0.3s; | |
margin-right: 0; | |
} | |
@keyframes bounce { | |
0%, 60%, 100% { transform: translateY(0); } | |
30% { transform: translateY(-4px); } | |
} | |
/* Welcome Screen */ | |
#welcomeScreen { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
background: linear-gradient(135deg, #26288E, #5C4A6C); | |
height: 100%; | |
padding: 20px; | |
text-align: center; | |
} | |
#robotAnimationContainer { | |
position: relative; | |
width: 240px; | |
height: 200px; | |
margin-bottom: 20px; | |
overflow: hidden; | |
} | |
#robotAnimation { | |
width: 100%; | |
height: 100%; | |
object-fit: cover; | |
} | |
#botTitle { | |
color: white; | |
font-weight: 600; | |
margin-bottom: 5px; | |
font-size: 18px; | |
} | |
#botSubtitle { | |
color: rgba(255, 255, 255, 0.7); | |
font-size: 13px; | |
margin-bottom: 20px; | |
} | |
#getStartedBtn { | |
background: linear-gradient(135deg, #6366F1, #A855F7); | |
color: white; | |
border: none; | |
padding: 10px 20px; | |
border-radius: 30px; | |
font-weight: 600; | |
cursor: pointer; | |
transition: all 0.2s; | |
margin-top: 10px; | |
} | |
#getStartedBtn:hover { | |
transform: scale(1.05); | |
} | |
#versionTag { | |
color: rgba(255, 255, 255, 0.5); | |
font-size: 12px; | |
margin-top: 15px; | |
} | |
/* Name Input Screen */ | |
#nameInputScreen { | |
display: none; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
background: linear-gradient(135deg, #26288E, #5C4A6C); | |
height: 100%; | |
padding: 20px; | |
text-align: center; | |
} | |
#namePrompt { | |
color: white; | |
font-weight: 600; | |
margin-bottom: 15px; | |
font-size: 16px; | |
} | |
#nameInput { | |
padding: 10px 15px; | |
border: none; | |
border-radius: 20px; | |
width: 80%; | |
max-width: 250px; | |
font-size: 14px; | |
outline: none; | |
margin-bottom: 15px; | |
} | |
#submitNameBtn { | |
background: linear-gradient(135deg, #6366F1, #A855F7); | |
color: white; | |
border: none; | |
padding: 10px 20px; | |
border-radius: 30px; | |
font-weight: 600; | |
cursor: pointer; | |
transition: all 0.2s; | |
} | |
#submitNameBtn:hover { | |
transform: scale(1.05); | |
} | |
/* Mobile Styles (Small devices) */ | |
@media (max-width: 480px) { | |
#chatbotContainer { | |
bottom: 80px; | |
right: 10px; | |
width: calc(100vw - 20px); | |
max-width: none; | |
height: calc(100vh - 100px); | |
max-height: none; | |
} | |
#chatbotIcon { | |
width: 50px; | |
height: 50px; | |
font-size: 24px; | |
bottom: 15px; | |
right: 15px; | |
} | |
#userMessage { | |
padding: 10px 12px; | |
} | |
#sendMessage, #micButton { | |
width: 36px; | |
height: 36px; | |
} | |
.message { | |
max-width: 85%; | |
} | |
} | |
/* Tablet Styles (Medium devices) */ | |
@media (min-width: 481px) and (max-width: 768px) { | |
#chatbotContainer { | |
width: 340px; | |
height: 450px; | |
} | |
} | |
/* Large Screen Styles */ | |
@media (min-width: 1200px) { | |
#chatbotContainer { | |
width: 380px; | |
height: 550px; | |
} | |
} | |
/* Landscape Mode on Mobile */ | |
@media (max-height: 500px) { | |
#chatbotContainer { | |
height: calc(100vh - 80px); | |
bottom: 70px; | |
} | |
#chatbotHeader { | |
padding: 10px 15px; | |
} | |
#chatInput { | |
padding: 8px; | |
} | |
#welcomeScreen, #nameInputScreen { | |
padding: 10px; | |
} | |
#robotAnimationContainer { | |
width: 150px; | |
height: 100px; | |
margin-bottom: 10px; | |
} | |
} | |
/* Dark Mode Support */ | |
@media (prefers-color-scheme: dark) { | |
body { | |
background-color: #121212; | |
color: #f0f0f0; | |
} | |
#chatbotContainer { | |
background: #1e1e1e; | |
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3); | |
} | |
#chatMessages { | |
background: #2d2d2d; | |
} | |
.bot { | |
background: #3d3d3d; | |
color: #f0f0f0; | |
} | |
#userMessage { | |
background-color: #3d3d3d; | |
color: #f0f0f0; | |
} | |
#chatInput { | |
background: #1e1e1e; | |
border-top: 1px solid #333; | |
} | |
.typing-indicator { | |
background-color: #3d3d3d; | |
} | |
.typing-indicator span { | |
background-color: #888; | |
} | |
#nameInput { | |
background-color: #3d3d3d; | |
color: #f0f0f0; | |
} | |
} | |
</style> | |
</head> | |
<body> | |
<div id="chatbotIcon"><img id="chatimg" src="/Static\digibot.png"></div> | |
<div id="chatbotContainer"> | |
<div id="chatbotHeader"> | |
DigiBot | |
<span id="closeChat">β</span> | |
</div> | |
<!-- Welcome Screen --> | |
<div id="welcomeScreen"> | |
<div id="robotAnimationContainer"> | |
<img id="robotAnimation" src="/Static/vid2.gif" alt="Smart AI Chatbot"> | |
</div> | |
<div id="botTitle">Smart AI Chatbot Assistance</div> | |
<div id="botSubtitle">Your digital assistant for all your questions</div> | |
<div id="versionTag">v2.0</div> | |
<button id="getStartedBtn">Get Started</button> | |
</div> | |
<!-- Name Input Screen with Robot --> | |
<div id="nameInputScreen"> | |
<div id="robotAnimationContainer"> | |
<img id="robotAnimation" src="/Static/vid2.gif" alt="Smart AI Chatbot"> | |
</div> | |
<div id="namePrompt">Hello! I'm your AI assistant. What's your name?</div> | |
<input type="text" id="nameInput" placeholder="Enter your name..." /> | |
<button id="submitNameBtn">Continue</button> | |
</div> | |
<div id="chatMessages" style="display: none;"></div> | |
<div id="chatInput" style="display: none;"> | |
<input type="text" id="userMessage" placeholder="Type a message..." /> | |
<button id="micButton"> | |
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-mic-fill" viewBox="0 0 16 16"> | |
<path d="M5 3a3 3 0 0 1 6 0v5a3 3 0 0 1-6 0z"/> | |
<path d="M3.5 6.5A.5.5 0 0 1 4 7v1a4 4 0 0 0 8 0V7a.5.5 0 0 1 1 0v1a5 5 0 0 1-4.5 4.975V15h3a.5.5 0 0 1 0 1h-7a.5.5 0 0 1 0-1h3v-2.025A5 5 0 0 1 3 8V7a.5.5 0 0 1 .5-.5"/> | |
</svg> | |
</button> | |
<button id="sendMessage"> | |
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
<path d="M22 2L11 13"></path> | |
<path d="M22 2L15 22L11 13L2 9L22 2Z"></path> | |
</svg> | |
</button> | |
</div> | |
</div> | |
<script> | |
// API Endpoint - Using a mockup function for demonstration | |
// Replace this with your actual API endpoint when you connect to a backend | |
const API_URL = "https://chatbot-5dsh.onrender.com/chat"; | |
// Mockup function for demo purposes | |
function mockApiCall(message) { | |
return new Promise((resolve) => { | |
// Simulate network delay | |
setTimeout(() => { | |
// Simple responses based on message content | |
let response; | |
const lowerMsg = message.toLowerCase(); | |
if (lowerMsg.includes("hello") || lowerMsg.includes("hi")) { | |
response = "Hello there! How can I help you today?"; | |
} else if (lowerMsg.includes("help") || lowerMsg.includes("support")) { | |
response = "I'm here to help! What do you need assistance with?"; | |
} else if (lowerMsg.includes("time") || lowerMsg.includes("date")) { | |
response = "It's currently " + new Date().toLocaleString(); | |
} else if (lowerMsg.includes("thank")) { | |
response = "You're welcome! Is there anything else I can help with?"; | |
} else if (lowerMsg.includes("weather")) { | |
response = "I'm sorry, I don't have access to real-time weather data. You might want to check a weather service for that information."; | |
} else if (lowerMsg.includes("name")) { | |
response = "I'm your AI assistant. How can I assist you today?"; | |
} else { | |
response = "I understand you're asking about '" + message + "'. Could you provide more details so I can help you better?"; | |
} | |
resolve({ response: response }); | |
}, 1000); | |
}); | |
} | |
// Get Elements | |
const chatbotIcon = document.getElementById("chatbotIcon"); | |
const chatbotContainer = document.getElementById("chatbotContainer"); | |
const closeChat = document.getElementById("closeChat"); | |
const chatMessages = document.getElementById("chatMessages"); | |
const userMessageInput = document.getElementById("userMessage"); | |
const sendMessageButton = document.getElementById("sendMessage"); | |
const welcomeScreen = document.getElementById("welcomeScreen"); | |
const getStartedBtn = document.getElementById("getStartedBtn"); | |
const chatInput = document.getElementById("chatInput"); | |
const nameInputScreen = document.getElementById("nameInputScreen"); | |
const nameInput = document.getElementById("nameInput"); | |
const submitNameBtn = document.getElementById("submitNameBtn"); | |
// Store the username | |
let userName = ""; | |
// Flag to track if chat has been initialized | |
let chatInitialized = false; | |
// Toggle Chatbot Window | |
// chatbotIcon.addEventListener("click", () => { | |
// if (chatbotContainer.style.display === "none" || !chatbotContainer.style.display) { | |
// chatbotContainer.style.display = "flex"; | |
// // If chat hasn't been initialized yet, show welcome screen | |
// if (!chatInitialized) { | |
// welcomeScreen.style.display = "flex"; | |
// nameInputScreen.style.display = "none"; | |
// chatMessages.style.display = "none"; | |
// chatInput.style.display = "none"; | |
// } else { | |
// // If chat was initialized but closed, just show the chat interface | |
// welcomeScreen.style.display = "none"; | |
// nameInputScreen.style.display = "none"; | |
// chatMessages.style.display = "flex"; | |
// chatInput.style.display = "flex"; | |
// } | |
// } else { | |
// chatbotContainer.style.display = "none"; | |
// } | |
// }); | |
// Modify the existing chatbotIcon click event listener | |
chatbotIcon.addEventListener("click", () => { | |
if (chatbotContainer.style.display === "none" || !chatbotContainer.style.display) { | |
chatbotContainer.style.display = "flex"; | |
// If chat has been fully initialized before, reset to initial state | |
if (chatInitialized) { | |
// Clear previous chat messages | |
chatMessages.innerHTML = ''; | |
// Show welcome screen again | |
welcomeScreen.style.display = "flex"; | |
nameInputScreen.style.display = "none"; | |
chatMessages.style.display = "none"; | |
chatInput.style.display = "none"; | |
// Reset chat initialization flag | |
chatInitialized = false; | |
// Clear stored username | |
userName = ""; | |
// Clear name input | |
nameInput.value = ""; | |
} else { | |
// If chat hasn't been initialized, show welcome screen as before | |
welcomeScreen.style.display = "flex"; | |
nameInputScreen.style.display = "none"; | |
chatMessages.style.display = "none"; | |
chatInput.style.display = "none"; | |
} | |
} else { | |
chatbotContainer.style.display = "none"; | |
} | |
}); | |
// Get Started Button | |
getStartedBtn.addEventListener("click", () => { | |
welcomeScreen.style.display = "none"; | |
nameInputScreen.style.display = "flex"; | |
chatMessages.style.display = "none"; | |
chatInput.style.display = "none"; | |
}); | |
// Submit Name Button | |
submitNameBtn.addEventListener("click", () => { | |
submitUserName(); | |
}); | |
// Handle name input enter key press | |
nameInput.addEventListener("keypress", function(event) { | |
if (event.key === "Enter") { | |
submitUserName(); | |
} | |
}); | |
function submitUserName() { | |
userName = nameInput.value.trim(); | |
if (!userName) { | |
// userName = "Friend"; // Default name if user doesn't enter anything | |
alert("Please enter your name"); | |
return | |
} | |
// Hide name input, show chat interface | |
nameInputScreen.style.display = "none"; | |
chatMessages.style.display = "flex"; | |
chatInput.style.display = "flex"; | |
// Set chat as initialized | |
chatInitialized = true; | |
// Add personalized welcome message | |
displayMessage(`Hello ${userName}! How can I help you today?`, "bot"); | |
userMessageInput.focus(); | |
} | |
// Close Chatbot Window | |
closeChat.addEventListener("click", (e) => { | |
e.stopPropagation(); | |
chatbotContainer.style.display = "none"; | |
}); | |
// Send User Message | |
sendMessageButton.addEventListener("click", sendMessage); | |
userMessageInput.addEventListener("keypress", function(event) { | |
if (event.key === "Enter") { | |
sendMessage(); | |
} | |
}); | |
async function sendMessage() { | |
let userMessage = userMessageInput.value.trim(); | |
if (!userMessage) return; | |
// Display User Message | |
displayMessage(userMessage, "user"); | |
userMessageInput.value = ""; | |
// Show typing indicator | |
const typingIndicator = document.createElement("div"); | |
typingIndicator.className = "typing-indicator"; | |
typingIndicator.innerHTML = "<span></span><span></span><span></span>"; | |
chatMessages.appendChild(typingIndicator); | |
chatMessages.scrollTop = chatMessages.scrollHeight; | |
// Call Flask API | |
try { | |
let response = await fetch(API_URL, { | |
method: "POST", | |
headers: { "Content-Type": "application/json" }, | |
body: JSON.stringify({ | |
message: userMessage, | |
userName: userName // Send username with the message | |
}) | |
}); | |
// Remove typing indicator | |
chatMessages.removeChild(typingIndicator); | |
let data = await response.json(); | |
displayMessage(data.response, "bot"); | |
} catch (error) { | |
// Remove typing indicator | |
chatMessages.removeChild(typingIndicator); | |
displayMessage("β Error: Unable to connect to chatbot.", "bot"); | |
} | |
} | |
function displayMessage(text, sender) { | |
let messageDiv = document.createElement("div"); | |
messageDiv.textContent = text; | |
messageDiv.classList.add(sender, "message"); | |
text = text | |
.replace(/## (.*?)(\n|$)/g, "<h2>$1</h2>") // Convert ## Heading to <h2> | |
.replace(/### (.*?)(\n|$)/g, "<h3>$1</h3>") // Convert ### Heading to <h3> | |
.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>") // Bold (**text** β <strong>text</strong>) | |
.replace(/\*(.*?)\*/g, "<em>$1</em>") // Italic (*text* β <em>text</em>) | |
.replace(/__(.*?)__/g, "<u>$1</u>") // Underline (__text__ β <u>text</u>) | |
.replace(/- (.*?)(\n|$)/g, "<li>$1</li>") // Bullet points (- text β <li>text</li>) | |
.replace(/\n/g, "<br>"); // Preserve line breaks | |
// Wrap bullet points in <ul> if they exist | |
if (text.includes("<li>")) { | |
text = text.replace(/(<li>.*?<\/li>)/gs, "<ul>$1</ul>"); | |
} | |
messageDiv.innerHTML= text; | |
chatMessages.appendChild(messageDiv); | |
chatMessages.scrollTop = chatMessages.scrollHeight; // Keep the latest message in view | |
let words = text.split(" "); // Split text into words | |
messageDiv.innerHTML = ""; // Clear initial content | |
let index = 0; | |
function typeWords() { | |
if (index < words.length) { | |
messageDiv.innerHTML += words[index] + " "; // Append word | |
index++; | |
chatMessages.scrollTop = chatMessages.scrollHeight; // Auto-scroll | |
setTimeout(typeWords, 60); // Adjust speed (Lower = Faster) | |
} | |
} | |
typeWords(); | |
} | |
// Prevent clicks inside chatbot container from closing it | |
chatbotContainer.addEventListener("click", (e) => { | |
e.stopPropagation(); | |
}); | |
document.getElementById("micButton").addEventListener("click", function() { | |
fetch("/speech") // Calls Flask speech-to-text API | |
.then(response => response.json()) | |
.then(data => { | |
let recognizedText = data.recognized_text; | |
if (recognizedText) { | |
document.getElementById("userMessage").value = recognizedText; // Autofill input | |
document.getElementById("sendMessage").click(); // Send automatically | |
} else { | |
alert("Couldn't recognize speech. Try again."); | |
} | |
}) | |
.catch(error => console.error("Error:", error)); | |
}); | |
// Handle window resize | |
window.addEventListener("resize", () => { | |
// If chat is open, adjust scroll | |
if (chatbotContainer.style.display === "flex" && chatMessages.style.display === "flex") { | |
chatMessages.scrollTop = chatMessages.scrollHeight; | |
} | |
}); | |
</script> | |
</body> | |
</html> |