|
from fastapi import FastAPI, HTTPException |
|
from fastapi.responses import HTMLResponse |
|
from transformers import AutoModelForCausalLM, AutoTokenizer |
|
|
|
app = FastAPI(title="Chatbot") |
|
|
|
|
|
model_name = "microsoft/DialoGPT-medium" |
|
tokenizer = AutoTokenizer.from_pretrained(model_name) |
|
model = AutoModelForCausalLM.from_pretrained(model_name) |
|
|
|
|
|
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_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) |