gemma / app.py
luck210's picture
Update app.py
f2ea123 verified
raw
history blame
32.9 kB
from fastapi import FastAPI, HTTPException
from fastapi.responses import HTMLResponse
from transformers import AutoModelForCausalLM, AutoTokenizer
app = FastAPI(title="Chatbot")
# Load the chatbot model (DialoGPT-medium) at startup
model_name = "microsoft/DialoGPT-medium"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name)
# Function to generate chatbot response
def get_chatbot_response(user_input: str, max_length=1000):
if not user_input:
return "Please say something!"
input_ids = tokenizer.encode(user_input + tokenizer.eos_token, return_tensors="pt")
chat_history_ids = model.generate(
input_ids,
max_length=max_length,
pad_token_id=tokenizer.eos_token_id,
no_repeat_ngram_size=3,
do_sample=True,
top_k=50,
top_p=0.95,
temperature=0.8
)
response = tokenizer.decode(chat_history_ids[:, input_ids.shape[-1]:][0], skip_special_tokens=True)
return response.strip()
# HTML, CSS, and JS with two modes (dark and soft)
HTML_CONTENT = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Vion IA: Your friendly AI chatbot powered by xAI">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
<link href='https://unpkg.com/boxicons@2.1.4/css/boxicons.min.css' rel='stylesheet'>
<title>Vion IA | Powered by xAI</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;600;800&display=swap');
* { margin: 0; padding: 0; outline: none; box-sizing: border-box; font-family: "Open Sans", sans-serif; }
:root {
--primary-color: #0A0A0B; --secondary-color: #1A1B1E; --secondary-hover-color: #2A2B2E;
--focus-color: #242528; --focus-hover-color: #343538; --button-hover-color: #252627;
--text-color: #FFFFFF; --text-secondary-color: #A0A1A3; --heading-secondary-color: #606162;
--placeholder-color: #78797A; --accent-color: #00A3FF; --star-color: #FFFFFF;
--background-color: #000; --error-color: #FF4D4D; --success-color: #4CAF50;
}
.soft_mode {
--primary-color: rgb(220, 230, 240); /* Soft pastel blue */
--secondary-color: rgb(235, 240, 245); /* Lighter pastel */
--secondary-hover-color: rgb(200, 210, 225);
--focus-color: rgb(245, 245, 250); --focus-hover-color: rgb(230, 230, 235);
--button-hover-color: rgb(210, 220, 230);
--text-color: rgb(40, 50, 60); --text-secondary-color: rgb(90, 100, 110);
--heading-secondary-color: rgb(150, 160, 170);
--placeholder-color: rgb(120, 130, 140); --accent-color: rgb(100, 150, 255); /* Softer blue */
--star-color: rgb(180, 190, 200); --background-color: rgb(240, 245, 250); /* Very light pastel */
--error-color: rgb(255, 100, 100); --success-color: rgb(100, 200, 100);
}
body {
min-height: 100vh; display: flex; flex-direction: row; justify-content: space-between;
background: var(--background-color); position: relative; overflow-y: hidden; transition: background 0.3s ease;
}
.stars {
position: fixed; width: 100%; height: 100%;
background: url('https://www.transparenttextures.com/patterns/stardust.png');
animation: starsAnim 100s linear infinite; z-index: -1; transition: opacity 0.3s ease; opacity: 0.8;
}
.soft_mode .stars {
background: url('https://www.transparenttextures.com/patterns/stardust.png') rgba(240, 245, 250, 0.4);
opacity: 0.3;
}
@keyframes starsAnim { from { background-position: 0 0; } to { background-position: 10000px 10000px; } }
@keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.1); } 100% { transform: scale(1); } }
@keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
.sidebar {
width: 70px; height: 100vh; background: rgba(26, 27, 30, 0.95); display: flex; flex-direction: column;
align-items: center; padding: 1rem 0; position: fixed; left: 0; top: 0; transition: width 0.3s ease; z-index: 1001;
}
.sidebar:hover { width: 200px; }
.soft_mode .sidebar { background: rgba(235, 240, 245, 0.95); }
.sidebar__item {
width: 100%; padding: 1rem; color: var(--text-secondary-color); text-decoration: none;
display: flex; align-items: center; gap: 1rem; transition: all 0.3s ease; position: relative;
}
.sidebar__item:hover, .sidebar__item:focus {
background: var(--secondary-hover-color); color: var(--accent-color); padding-left: 1.5rem; transform: scale(1.05);
}
.sidebar__item i { font-size: 1.5rem; }
.sidebar__item span { display: none; font-size: 1rem; }
.sidebar:hover .sidebar__item span { display: inline; }
.tooltip {
visibility: hidden; background: var(--secondary-color); color: var(--text-color); font-size: 0.8rem;
padding: 0.5rem; border-radius: 0.3rem; position: absolute; top: -30px; left: 50%; transform: translateX(-50%);
white-space: nowrap; z-index: 1002; transition: visibility 0.2s, opacity 0.2s; opacity: 0;
}
.sidebar__item:hover .tooltip, .sidebar__item:focus .tooltip { visibility: visible; opacity: 1; }
.main-content {
flex: 1; display: flex; flex-direction: column; padding-bottom: 100px; padding-top: 2rem; margin-left: 70px;
height: 50vh; overflow: hidden;
}
.header { max-width: 900px; text-align: center; padding: 0 2rem; margin: 0 auto; }
.header__title h1 {
color: var(--text-color); font-size: 3.5rem; font-weight: 800; margin-bottom: 1rem;
text-shadow: 0 0 10px rgba(0, 163, 255, 0.5); animation: fadeIn 1s ease-in;
}
.header__title h2 {
color: var(--text-secondary-color); font-size: 1.5rem; font-weight: 400;
max-width: 600px; margin: 0 auto; text-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
transition: opacity 0.3s ease, height 0.3s ease;
}
.suggests {
display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
margin: 2rem auto; max-width: 900px; gap: 1rem; padding: 0 2rem; transition: opacity 0.3s ease, height 0.3s ease;
animation: fadeIn 0.5s ease-in;
}
.suggests.hidden, .header__title h2.hidden {
opacity: 0; height: 0; margin: 0; overflow: hidden;
}
.suggests__item {
background: rgba(26, 27, 30, 0.9); color: var(--text-secondary-color); padding: 1.5rem;
border-radius: 0.5rem; cursor: pointer; transition: all 0.3s ease; border: 1px solid var(--focus-color);
position: relative;
}
.soft_mode .suggests__item { background: rgba(235, 240, 245, 0.9); }
.suggests__item:hover, .suggests__item:focus {
background: var(--secondary-hover-color); border-color: var(--accent-color); color: var(--text-color);
transform: translateY(-3px);
}
.suggests__item-icon { margin-top: 1rem; color: var(--accent-color); transition: transform 0.2s ease; }
.suggests__item:hover .suggests__item-icon, .suggests__item:focus .suggests__item-icon { transform: scale(1.2); }
.suggests__item .tooltip { top: -40px; left: 50%; transform: translateX(-50%); }
.suggests__item:hover .tooltip, .suggests__item:focus .tooltip { visibility: visible; opacity: 1; }
.prompt {
position: fixed; background: rgba(10, 10, 11, 0.9); z-index: 1000; width: calc(100% - 70px);
left: 70px; bottom: 0; padding: 1rem; border-top: 1px solid var(--secondary-color); transition: all 0.3s ease;
}
.soft_mode .prompt { background: rgba(235, 240, 245, 0.9); border-top: 1px solid var(--focus-color); }
.prompt__input-wrapper {
max-width: 900px; margin: 0 auto; position: relative; display: flex; align-items: center;
background: var(--secondary-color); border: 1px solid var(--focus-color); border-radius: 0.5rem;
padding: 0.2rem; transition: all 0.3s ease; animation: fadeIn 0.5s ease-in;
}
.prompt__input-wrapper:focus-within {
border-color: var(--accent-color); background: var(--focus-color); transform: scale(1.02);
}
.prompt__input-wrapper.dragover {
border: 2px dashed var(--accent-color); background: var(--focus-hover-color);
}
.prompt__form-input {
flex-grow: 1; border: none; resize: none; font-size: 1.1rem; color: var(--text-color);
padding: 0.3rem 0.5rem; background: transparent; outline: none; transition: all 0.3s ease;
}
.prompt__form-input::placeholder { color: var(--placeholder-color); transition: opacity 0.3s ease; }
.prompt__form-input:focus::placeholder { opacity: 0.5; }
.prompt__action-buttons {
display: flex; align-items: center; gap: 0.3rem; padding-right: 0.3rem; position: relative;
}
.prompt__action-buttons.advanced { display: none; }
.prompt__action-buttons.advanced.active { display: flex; }
.prompt__form-button {
background: none; border: none; color: var(--text-secondary-color); font-size: 1.3rem;
cursor: pointer; padding: 0.3rem; transition: all 0.3s ease; position: relative;
}
.prompt__form-button:hover, .prompt__form-button:focus { color: var(--accent-color); transform: scale(1.1); }
.prompt__form-button.send { font-size: 1.5rem; }
.prompt__form-button .tooltip { top: -35px; left: 50%; transform: translateX(-50%); }
.prompt__form-button:hover .tooltip, .prompt__form-button:focus .tooltip { visibility: visible; opacity: 1; }
.prompt__disclaim {
text-align: center; color: var(--placeholder-color); font-size: 0.8rem; margin-top: 1rem;
max-width: 900px; margin-left: auto; margin-right: auto; transition: opacity 0.3s ease;
}
.chat-bar {
max-width: 900px; margin: 2rem auto; padding: 0 2rem; display: flex; flex-direction: column;
overflow-y: auto; max-height: calc(100vh - 180px); -ms-overflow-style: none; scrollbar-width: none;
}
.chat-bar::-webkit-scrollbar { display: none; }
.chat-message {
margin-bottom: 1rem; padding: 1rem; border-radius: 0.5rem; background: rgba(26, 27, 30, 0.9);
color: var(--text-color); word-wrap: break-word; animation: fadeIn 0.3s ease-in; position: relative;
}
.soft_mode .chat-message { background: rgba(235, 240, 245, 0.9); }
.chat-message.user {
background: rgba(122, 92, 250, 0.2); border: 1px solid var(--accent-color); border-radius: 0.5rem;
}
.chat-message.bot { background: rgba(36, 37, 40, 0.9); }
.soft_mode .chat-message.bot { background: rgba(245, 245, 250, 0.9); }
.chat-message.user.bubble-rounded { border-radius: 1rem; }
.chat-message.user.bubble-sharp { border-radius: 0; border: 2px solid var(--accent-color); }
.chat-message.user.bubble-starry {
border-radius: 0.5rem; border: 1px dashed var(--accent-color);
background: rgba(122, 92, 250, 0.2) url('https://www.transparenttextures.com/patterns/stardust.png') repeat;
background-size: 100px 100px;
}
.chat-message.feedback::after {
content: 'Was this helpful?'; color: var(--text-secondary-color); font-size: 0.8rem; display: block;
margin-top: 0.5rem; cursor: pointer; text-decoration: underline;
}
.chat-message.feedback .feedback-options {
display: none; position: absolute; bottom: -30px; left: 1rem; gap: 0.5rem;
}
.chat-message.feedback:hover .feedback-options { display: flex; }
.feedback-options button {
background: none; border: none; color: var(--text-secondary-color); font-size: 1rem; cursor: pointer;
transition: color 0.2s ease;
}
.feedback-options button:hover, .feedback-options button:focus { color: var(--accent-color); }
.error-message {
background: rgba(255, 77, 77, 0.2); border: 1px solid var(--error-color); color: var(--text-color);
padding: 1rem; border-radius: 0.5rem; margin-bottom: 1rem; animation: fadeIn 0.3s ease-in;
display: flex; justify-content: space-between; align-items: center;
}
.error-message button {
background: var(--error-color); color: var(--text-color); border: none; padding: 0.3rem 0.6rem;
border-radius: 0.3rem; cursor: pointer; transition: background 0.2s ease;
}
.error-message button:hover, .error-message button:focus { background: var(--button-hover-color); }
.back-to-latest {
display: none; position: fixed; bottom: 100px; right: 2rem; background: var(--secondary-color);
color: var(--text-color); padding: 0.5rem 1rem; border-radius: 0.5rem; cursor: pointer;
border: 1px solid var(--accent-color); transition: all 0.3s ease; z-index: 1000;
}
.back-to-latest.visible { display: block; }
.back-to-latest:hover, .back-to-latest:focus { background: var(--secondary-hover-color); transform: scale(1.05); }
.processing-dots {
display: none; position: absolute; right: 60px; color: var(--accent-color); font-size: 1.2rem;
}
.processing-dots.active { display: inline; animation: pulse 1.5s infinite; }
@keyframes blink {
0% { opacity: 1; } 50% { opacity: 0.3; } 100% { opacity: 1; }
}
.processing-dots span {
animation: blink 1s infinite; animation-delay: calc(0.2s * var(--i));
}
</style>
</head>
<body>
<div class="stars"></div>
<nav class="sidebar" aria-label="Main navigation">
<a href="#" class="sidebar__item" tabindex="0" aria-label="Home"><i class='bx bx-home'></i><span>Home</span><div class="tooltip">Go to Home</div></a>
<a href="#" class="sidebar__item" tabindex="0" aria-label="Profile"><i class='bx bx-user'></i><span>Profile</span><div class="tooltip">View Profile</div></a>
<a href="#" class="sidebar__item" tabindex="0" aria-label="Settings"><i class='bx bx-cog'></i><span>Settings</span><div class="tooltip">Adjust Settings</div></a>
<a href="#" class="sidebar__item" tabindex="0" aria-label="Help"><i class='bx bx-help-circle'></i><span>Help</span><div class="tooltip">Get Help</div></a>
</nav>
<div class="main-content">
<header class="header">
<div class="header__title">
<h1>Vion IA</h1>
<h2 id="welcome-text">Ask me anything and I'll provide helpful and truthful answers from an outside perspective on humanity.</h2>
</div>
</header>
<div class="suggests">
<div class="suggests__item" tabindex="0"><p class="suggests__item-text">What is the meaning of life?</p><div class="suggests__item-icon"><i class='bx bx-bulb'></i></div><div class="tooltip">Explore life's purpose</div></div>
<div class="suggests__item" tabindex="0"><p class="suggests__item-text">Explain quantum physics simply</p><div class="suggests__item-icon"><i class='bx bx-atom'></i></div><div class="tooltip">Learn about quantum physics</div></div>
<div class="suggests__item" tabindex="0"><p class="suggests__item-text">How does the universe work?</p><div class="suggests__item-icon"><i class='bx bx-planet'></i></div><div class="tooltip">Discover the universe</div></div>
</div>
<div class="chat-bar" id="chatBar" aria-live="polite"></div>
<button class="back-to-latest" id="backToLatest" tabindex="0">Back to Latest</button>
</div>
<section class="prompt">
<form action="#" class="prompt__form" novalidate>
<div class="prompt__input-wrapper">
<input type="text" placeholder="Ask me anything..." class="prompt__form-input" id="chatInput" required aria-label="Chat input">
<div class="prompt__action-buttons basic">
<button type="button" class="prompt__form-button send" id="sendButton" tabindex="0" aria-label="Send message"><i class='bx bx-send'></i><div class="tooltip">Send Message (Ctrl+S)</div></button>
<button type="button" class="prompt__form-button" id="moreOptions" tabindex="0" aria-label="Show more options"><i class='bx bx-dots-horizontal-rounded'></i><div class="tooltip">More Options</div></button>
</div>
<div class="prompt__action-buttons advanced" id="advancedOptions">
<label for="fileInput" class="prompt__form-button" tabindex="0" aria-label="Upload file"><i class='bx bx-upload'></i><div class="tooltip">Upload File</div></label>
<input type="file" id="fileInput" style="display: none;" accept=".txt,.pdf,.jpg,.png">
<button type="button" class="prompt__form-button" id="deepSearchButton" tabindex="0" aria-label="Deep search"><i class='bx bx-search'></i><div class="tooltip">Deep Search</div></button>
<button type="button" class="prompt__form-button" id="thinkButton" tabindex="0" aria-label="Think mode"><i class='bx bx-brain'></i><div class="tooltip">Think Mode</div></button>
<button type="button" class="prompt__form-button" id="bubbleToggle" tabindex="0" aria-label="Toggle bubble style"><i class='bx bx-chat'></i><div class="tooltip">Change Bubble Style</div></button>
<button type="button" class="prompt__form-button" id="soundToggle" tabindex="0" aria-label="Toggle sound"><i class='bx bx-volume-full'></i><div class="tooltip">Toggle Sound</div></button>
<button type="button" class="prompt__form-button" id="themeToggler" tabindex="0" aria-label="Toggle theme"><i class='bx bx-adjust'></i><div class="tooltip">Toggle Dark/Soft (Ctrl+T)</div></button>
<span class="processing-dots" id="processingDots"><span style="--i:1">.</span><span style="--i:2">.</span><span style="--i:3">.</span></span>
</div>
</div>
</form>
<p class="prompt__disclaim">Vion IA provides answers based on its training and design. It may make mistakes.</p>
</section>
<audio id="sendSound" src="https://www.soundjay.com/buttons/beep-01a.mp3"></audio>
<audio id="responseSound" src="https://www.soundjay.com/buttons/beep-02.mp3"></audio>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script>
// Initialize state
let soundEnabled = localStorage.getItem('soundEnabled') !== 'false';
let messageHistory = [];
let currentBubbleIndex = 0;
const bubbleStyles = ['bubble-rounded', 'bubble-sharp', 'bubble-starry'];
const chatBar = document.getElementById('chatBar');
const inputField = document.getElementById('chatInput');
const suggests = document.querySelector('.suggests');
const welcomeText = document.getElementById('welcome-text');
const processingDots = document.getElementById('processingDots');
const sendButton = document.getElementById('sendButton');
const backToLatest = document.getElementById('backToLatest');
const themeToggler = document.getElementById('themeToggler');
const soundToggle = document.getElementById('soundToggle');
const advancedOptions = document.getElementById('advancedOptions');
const moreOptions = document.getElementById('moreOptions');
// Personalized greeting
function setGreeting() {
const hour = new Date().getHours();
const greeting = hour < 12 ? 'Good morning!' : hour < 18 ? 'Good afternoon!' : 'Good evening!';
const returning = localStorage.getItem('visited');
welcomeText.textContent = returning ? `Welcome back! ${greeting}` : `${greeting} Ask me anything!`;
localStorage.setItem('visited', 'true');
}
setGreeting();
// Theme management (dark or soft)
function setTheme(theme) {
document.body.classList.remove('soft_mode');
if (theme === 'soft') document.body.classList.add('soft_mode');
themeToggler.innerHTML = theme === 'soft' ? "<i class='bx bx-moon'></i>" : "<i class='bx bx-adjust'></i>";
localStorage.setItem('theme', theme);
}
const savedTheme = localStorage.getItem('theme') || 'dark';
setTheme(savedTheme);
// Toggle theme with button or Ctrl+T
themeToggler.addEventListener('click', () => {
const newTheme = document.body.classList.contains('soft_mode') ? 'dark' : 'soft';
setTheme(newTheme);
});
document.addEventListener('keydown', (e) => {
if (e.ctrlKey && e.key === 't') {
e.preventDefault();
const newTheme = document.body.classList.contains('soft_mode') ? 'dark' : 'soft';
setTheme(newTheme);
}
});
// Sound toggle
soundToggle.innerHTML = soundEnabled ? "<i class='bx bx-volume-full'></i>" : "<i class='bx bx-volume-mute'></i>";
soundToggle.addEventListener('click', () => {
soundEnabled = !soundEnabled;
localStorage.setItem('soundEnabled', soundEnabled);
soundToggle.innerHTML = soundEnabled ? "<i class='bx bx-volume-full'></i>" : "<i class='bx bx-volume-mute'></i>";
});
// Progressive disclosure
moreOptions.addEventListener('click', () => {
advancedOptions.classList.toggle('active');
moreOptions.style.transform = advancedOptions.classList.contains('active') ? 'rotate(90deg)' : 'rotate(0)';
});
// Bubble customization
document.getElementById('bubbleToggle').addEventListener('click', () => {
currentBubbleIndex = (currentBubbleIndex + 1) % bubbleStyles.length;
applyBubbleStyle();
});
function applyBubbleStyle() {
const userMessages = document.querySelectorAll('.chat-message.user');
userMessages.forEach(msg => {
bubbleStyles.forEach(style => msg.classList.remove(style));
msg.classList.add(bubbleStyles[currentBubbleIndex]);
});
}
// Add message with feedback option
function addMessage(content, isUser = false, isError = false) {
if (isError) {
const errorDiv = document.createElement('div');
errorDiv.classList.add('error-message');
errorDiv.innerHTML = `${content} <button onclick="retryLastMessage()" tabindex="0">Retry</button>`;
chatBar.appendChild(errorDiv);
} else {
const messageDiv = document.createElement('div');
messageDiv.classList.add('chat-message', isUser ? 'user' : 'bot');
if (isUser) messageDiv.classList.add(bubbleStyles[currentBubbleIndex]);
if (!isUser) messageDiv.classList.add('feedback');
messageDiv.textContent = content;
if (!isUser) {
const feedbackDiv = document.createElement('div');
feedbackDiv.classList.add('feedback-options');
feedbackDiv.innerHTML = `
<button onclick="handleFeedback('up')" tabindex="0" aria-label="Thumbs up"><i class='bx bx-thumbs-up'></i></button>
<button onclick="handleFeedback('down')" tabindex="0" aria-label="Thumbs down"><i class='bx bx-thumbs-down'></i></button>
`;
messageDiv.appendChild(feedbackDiv);
}
chatBar.appendChild(messageDiv);
messageHistory.push({ content, isUser });
}
if (chatBar.scrollTop + chatBar.clientHeight >= chatBar.scrollHeight - 100) {
chatBar.scrollTop = chatBar.scrollHeight;
}
}
// Handle feedback (placeholder)
function handleFeedback(type) {
console.log(`Feedback: ${type}`);
alert(`Thanks for your feedback! (${type === 'up' ? 'Thumbs up' : 'Thumbs down'})`);
}
// Retry last message
function retryLastMessage() {
const lastUserMessage = messageHistory.filter(m => m.isUser).slice(-1)[0];
if (lastUserMessage) sendMessage(lastUserMessage.content);
}
// Input events
inputField.addEventListener('input', () => {
const input = inputField.value.trim();
suggests.classList.toggle('hidden', input.length > 0);
welcomeText.classList.toggle('hidden', input.length > 0);
inputField.setCustomValidity(input.length < 3 && input.length > 0 ? 'Please enter at least 3 characters.' : '');
});
inputField.addEventListener('focus', () => {
inputField.parentElement.style.boxShadow = `0 0 5px var(--accent-color)`;
});
inputField.addEventListener('blur', () => {
inputField.parentElement.style.boxShadow = 'none';
});
inputField.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
} else if (e.ctrlKey && e.key === 's') {
e.preventDefault();
sendMessage();
} else if (e.key === 'Escape') {
e.preventDefault();
inputField.value = '';
suggests.classList.remove('hidden');
welcomeText.classList.remove('hidden');
}
});
// Drag and drop
const inputWrapper = document.querySelector('.prompt__input-wrapper');
inputWrapper.addEventListener('dragover', (e) => {
e.preventDefault();
inputWrapper.classList.add('dragover');
});
inputWrapper.addEventListener('dragleave', () => {
inputWrapper.classList.remove('dragover');
});
inputWrapper.addEventListener('drop', (e) => {
e.preventDefault();
inputWrapper.classList.remove('dragover');
const file = e.dataTransfer.files[0];
if (file) alert(`File dropped: ${file.name} (Processing to be implemented)`);
});
// Suggestion interactions
document.querySelectorAll('.suggests__item').forEach(item => {
item.addEventListener('click', () => {
inputField.value = item.querySelector('.suggests__item-text').textContent;
suggests.classList.add('hidden');
welcomeText.classList.add('hidden');
inputField.focus();
});
item.addEventListener('dblclick', () => {
alert(`Pinned suggestion: ${item.querySelector('.suggests__item-text').textContent} (Placeholder)`);
});
item.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
item.click();
}
});
});
// Scroll handling
chatBar.addEventListener('scroll', () => {
const isScrolledUp = chatBar.scrollTop < chatBar.scrollHeight - chatBar.clientHeight - 100;
backToLatest.classList.toggle('visible', isScrolledUp);
if (chatBar.scrollTop < 100 && messageHistory.length > 10) {
loadMoreMessages();
}
});
backToLatest.addEventListener('click', () => {
chatBar.scrollTop = chatBar.scrollHeight;
});
backToLatest.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
chatBar.scrollTop = chatBar.scrollHeight;
}
});
// Placeholder for loading more messages
function loadMoreMessages() {
console.log('Loading more messages (placeholder)');
}
// Send message
function sendMessage(input = inputField.value.trim()) {
if (!input) {
addMessage('Oops, please type something to ask!', false, true);
return;
}
if (input.length < 3) {
addMessage('Your query is too short—try adding more details!', false, true);
return;
}
sendButton.disabled = true;
processingDots.classList.add('active');
addMessage(input, true);
inputField.value = '';
if (soundEnabled) document.getElementById('sendSound').play();
fetch('/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: input })
})
.then(res => res.json())
.then(data => {
if (data.response) {
addMessage(data.response);
suggests.classList.add('hidden');
if (soundEnabled) document.getElementById('responseSound').play();
} else {
addMessage(data.detail || 'Something went wrong! Please try again.', false, true);
}
})
.catch(error => {
addMessage('Failed to process the query! Please check your connection.', false, true);
})
.finally(() => {
sendButton.disabled = false;
processingDots.classList.remove('active');
});
}
sendButton.addEventListener('click', () => sendMessage());
sendButton.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
sendMessage();
}
});
// Placeholder button actions
document.getElementById('deepSearchButton').addEventListener('click', () => {
alert('Initiating DeepSearch... (Functionality to be implemented)');
});
document.getElementById('thinkButton').addEventListener('click', () => {
alert('Activating Think mode... (Functionality to be implemented)');
});
document.getElementById('fileInput').addEventListener('change', (e) => {
const file = e.target.files[0];
if (file) alert(`File selected: ${file.name} (Processing to be implemented)`);
});
// Responsive adjustments
window.addEventListener('resize', () => {
const width = window.innerWidth;
chatBar.style.maxHeight = width < 768 ? 'calc(100vh - 200px)' : 'calc(100vh - 180px)';
});
// Starry background effects
document.addEventListener('mousemove', (e) => {
let x = e.clientX / window.innerWidth;
let y = e.clientY / window.innerHeight;
document.querySelector('.stars').style.transform = `translate(${x * 50}px, ${y * 50}px)`;
});
// Enhanced keyboard navigation
document.querySelectorAll('.sidebar__item, .suggests__item, .prompt__form-button, .back-to-latest, .feedback-options button').forEach(el => {
el.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
el.click();
}
});
});
</script>
</body>
</html>
"""
@app.get("/", response_class=HTMLResponse)
async def read_root():
return HTML_CONTENT
@app.post("/chat")
async def chat_endpoint(data: dict):
message = data.get("message", "")
if not message:
raise HTTPException(status_code=400, detail="No message provided")
try:
response = get_chatbot_response(message)
return {"response": response}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Chat error: {str(e)}")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=7860)