Spaces:
Sleeping
Sleeping
<html lang="en" class="light"> | |
<head> | |
<meta charset="UTF-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
<meta name="atheon-project-verification" content="bac75f10-2276-42b3-a839-16ae98baa57c" /> | |
<title>AI Chat Interface</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<script> | |
tailwind.config = { | |
darkMode: "class", | |
theme: { | |
extend: { | |
colors: { | |
primary: { | |
DEFAULT: "#3b82f6", | |
foreground: "#ffffff", | |
}, | |
secondary: { | |
DEFAULT: "#f3f4f6", | |
foreground: "#1f2937", | |
}, | |
}, | |
}, | |
}, | |
}; | |
</script> | |
<script data-atheon-publisher-key="arc-pub-f8066482a332b510a0dd998dc1c614d419582f2e7326f69236602562c9d419cf" | |
src="https://js.atheon.ad/atheon.js"></script> | |
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" | |
rel="stylesheet" /> | |
</head> | |
<body class="font-['Inter'] text-gray-800 bg-white transition-colors duration-200 dark:text-gray-200 dark:bg-gray-900"> | |
<main class="flex flex-col h-screen"> | |
<!-- Header --> | |
<header class="p-4 bg-white border-b border-gray-200 dark:bg-gray-800 dark:border-gray-700"> | |
<div class="flex justify-between items-center mx-auto max-w-screen-xl"> | |
<div class="flex items-center"> | |
<h1 class="font-medium text-xl"> | |
OpenGPT with | |
<svg style=" | |
display: inline-block; | |
vertical-align: -16.75%; | |
margin-right: -1.5%; | |
" stroke_linecap="round" stroke_linejoin="round" width="32" height="32" viewBox="0 0 24 24" | |
xmlns="http://www.w3.org/2000/svg"> | |
<defs> | |
<linearGradient id="logo-grad-arm" x1="0%" y1="0%" x2="100%" y2="0%"> | |
<stop offset="0%" style="stop-color: #f7cd1b; stop-opacity: 1" /> | |
<stop offset="100%" style="stop-color: #f9800c; stop-opacity: 1" /> | |
</linearGradient> | |
</defs> | |
<g class="tooltip" style="display: inline"> | |
<path fill="#067ff3" | |
d="M.227 20.705a5.444 5.444 0 0 0 4.745-2.858l4.48-8.13L7.67 6.613.03 20.368a.227.227 0 0 0 .198.337z" /> | |
<path fill="#07b682" | |
d="M16.003 13.074l-2.747 1.361 1.944 3.39a5.697 5.682-.012 0 0 4.935 2.869.19.19 0 0 0 .165-.286z" /> | |
<path fill="url(#logo-grad-arm)" | |
d="M7.99 14.555L6.2 17.872a.03.03 0 0 0 .04.042l17.744-8.798a.03.03 0 0 0-.022-.055l-11.67 3.765-3.851 1.344a.819.819 0 0 0-.451.385z" /> | |
<path class="logo-arm" | |
d="M10.011 3.3a.683.681-.012 0 0-.733.339L8.19 5.603l4.137 7.212 2.964-.956-4.825-8.234a.683.681-.012 0 0-.455-.324z" /> | |
</g> | |
<style> | |
@media (prefers-color-scheme: light) { | |
.logo-arm { | |
fill: black; | |
} | |
} | |
@media (prefers-color-scheme: dark) { | |
.logo-arm { | |
fill: white; | |
} | |
} | |
</style> | |
</svg><span class="font-semibold" style="color: #067ff3">th</span><span class="font-semibold" | |
style="color: #f9800c">e</span><span class="font-semibold" style="color: #07b682">on</span> | |
</h1> | |
</div> | |
<!-- --> | |
<nav class="flex flex-row items-center space-x-5"> | |
<a href="https://www.producthunt.com/posts/arcana-2?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-arcana-2" | |
target="_blank"><img | |
src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=946508&theme=light&t=1743029566204" | |
alt="Atheon - AI-Powered Monetization: Native GenAI Ads for GenAI apps | Product Hunt" | |
width="158" /> | |
</a> | |
<a href="https://atheon.ad/" | |
class="font-medium text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">Home</a> | |
<button id="logout-button" | |
class="font-medium text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white"> | |
Logout | |
</button> | |
</nav> | |
</div> | |
</header> | |
<!-- Model selector --> | |
<div class="px-4 py-3 bg-gray-50 border-b border-gray-200 dark:bg-gray-800 dark:border-gray-700"> | |
<div class="flex items-center mx-auto max-w-screen-xl"> | |
<div class="relative" id="model-selector-container"> | |
<button id="model-dropdown-button" | |
class="flex justify-between items-center px-4 py-2 w-64 bg-white rounded-md border border-gray-300 shadow-sm dark:bg-gray-700 dark:border-gray-600 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:hover:bg-gray-600"> | |
<span id="selected-model-text" class="text-gray-800 dark:text-gray-200">Select a model</span> | |
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" | |
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" | |
class="lucide lucide-chevron-down"> | |
<path d="m6 9 6 6 6-6" /> | |
</svg> | |
</button> | |
<div id="model-dropdown" | |
class="hidden absolute z-10 mt-1 w-full bg-white rounded-md border border-gray-300 shadow-lg dark:bg-gray-700 dark:border-gray-600"> | |
<ul class="py-1"> | |
<li class="px-4 py-2 text-gray-800 cursor-pointer model-option dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-600" | |
data-value="gemma2"> | |
Gemma2 | |
</li> | |
</ul> | |
<ul class="py-1"> | |
<li class="px-4 py-2 text-gray-800 cursor-pointer model-option dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-600" | |
data-value="llama3_1"> | |
Llama3.1 | |
</li> | |
</ul> | |
<ul class="py-1"> | |
<li class="px-4 py-2 text-gray-800 cursor-pointer model-option dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-600" | |
data-value="llama3_3"> | |
Llama3.3 | |
</li> | |
</ul> | |
<ul class="py-1"> | |
<li class="px-4 py-2 text-gray-800 cursor-pointer model-option dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-600" | |
data-value="qwen2_5"> | |
Qwen2.5 | |
</li> | |
</ul> | |
<ul class="py-1"> | |
<li class="px-4 py-2 text-gray-800 cursor-pointer model-option dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-600" | |
data-value="deepseek_r1"> | |
Deepseek_R1 | |
</li> | |
</ul> | |
<ul class="py-1"> | |
<li class="px-4 py-2 text-gray-800 cursor-pointer model-option dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-600" | |
data-value="gemma3"> | |
Gemma3 | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Chat container --> | |
<div id="chat-container" class="flex-1 overflow-y-auto px-4 py-4 bg-white md:px-0 dark:bg-gray-900"> | |
<div class="mx-auto max-w-screen-md"> | |
<!-- Initial welcome message --> | |
<div class="flex flex-col justify-center items-center mt-16 mb-8"> | |
<div | |
class="flex justify-center items-center mb-4 w-16 h-16 font-semibold text-2xl bg-gray-100 rounded-full dark:bg-gray-700"> | |
AR | |
</div> | |
<h2 class="font-semibold text-2xl text-center">Hello, User</h2> | |
<p class="text-2xl text-center">How can I help you today?</p> | |
</div> | |
<!-- Messages will be added here --> | |
</div> | |
</div> | |
<!-- Loading indicator --> | |
<div id="loading" class="hidden"> | |
<div class="mx-auto max-w-screen-md"> | |
<div class="flex items-start mb-4"> | |
<div | |
class="flex justify-center items-center mr-2 w-8 h-8 bg-gray-100 rounded-full dark:bg-gray-700"> | |
<span class="font-semibold text-sm">AR</span> | |
</div> | |
<div class="p-3 bg-gray-100 rounded-lg dark:bg-gray-700"> | |
<div class="typing-indicator"> | |
<span class="dot"></span> | |
<span class="dot"></span> | |
<span class="dot"></span> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Suggestion buttons --> | |
<div class="grid grid-cols-1 gap-3 px-4 mx-auto mb-6 max-w-screen-md md:grid-cols-2 md:px-0"> | |
<button | |
class="p-4 text-left bg-gray-50 rounded-xl transition-colors dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700"> | |
<div class="font-medium">Create a short 3 scene video script</div> | |
<div class="text-gray-500 text-sm"> | |
set in a cyberpunk world run by AI | |
</div> | |
</button> | |
<button | |
class="p-4 text-left bg-gray-50 rounded-xl transition-colors dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700"> | |
<div class="font-medium">Write a python code</div> | |
<div class="text-gray-500 text-sm"> | |
for a simple, functional web app | |
</div> | |
</button> | |
<button | |
class="p-4 text-left bg-gray-50 rounded-xl transition-colors dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700"> | |
<div class="font-medium">Help me study</div> | |
<div class="text-gray-500 text-sm"> | |
vocabulary for a college entrance exam | |
</div> | |
</button> | |
<button | |
class="p-4 text-left bg-gray-50 rounded-xl transition-colors dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700"> | |
<div class="font-medium">Give me ideas</div> | |
<div class="text-gray-500 text-sm"> | |
for a LinkedIn post for my recent promotion | |
</div> | |
</button> | |
</div> | |
<!-- Input area --> | |
<div class="px-4 pt-4 bg-white border-t border-gray-200 dark:bg-gray-800 dark:border-gray-700"> | |
<div class="mx-auto max-w-screen-md"> | |
<form id="chat-form" class="relative"> | |
<div | |
class="flex items-center bg-white rounded-full border border-gray-300 shadow-sm dark:bg-gray-700 dark:border-gray-600"> | |
<button type="button" | |
class="p-3 text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-300"> | |
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" | |
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" | |
stroke-linejoin="round" class="lucide lucide-plus"> | |
<path d="M5 12h14" /> | |
<path d="M12 5v14" /> | |
</svg> | |
</button> | |
<input type="text" id="user-input" | |
class="flex-1 px-2 py-3 text-gray-800 bg-transparent dark:text-gray-200 focus:outline-none" | |
placeholder="Send a Message" autocomplete="off" /> | |
<button type="button" | |
class="p-3 text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-300"> | |
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" | |
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" | |
stroke-linejoin="round" class="lucide lucide-mic"> | |
<path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" /> | |
<path d="M19 10v2a7 7 0 0 1-14 0v-2" /> | |
<path d="M12 19v3" /> | |
</svg> | |
</button> | |
<button type="submit" | |
class="p-3 text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-300"> | |
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" | |
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" | |
stroke-linejoin="round" class="lucide lucide-arrow-up"> | |
<path d="m12 19-7-7 7-7" /> | |
<path d="M5 12h14" /> | |
</svg> | |
</button> | |
</div> | |
</form> | |
</div> | |
</div> | |
<footer class="p-4 text-center text-gray-500 text-sm dark:text-gray-400 dark:bg-gray-800"> | |
<p class="p-1 text-gray-400 dark:text-gray-500"> | |
LLMs can make mistakes. Verify important information. | |
</p> | |
<p class="p-1">© 2025 Atheon Inc. All rights reserved.</p> | |
</footer> | |
</main> | |
<style> | |
.typing-indicator { | |
display: flex; | |
align-items: center; | |
} | |
.dot { | |
height: 8px; | |
width: 8px; | |
margin-right: 4px; | |
border-radius: 50%; | |
background-color: #6b7280; | |
display: inline-block; | |
animation: pulse 1.5s infinite ease-in-out; | |
} | |
.dark .dot { | |
background-color: #9ca3af; | |
} | |
.dot:nth-child(2) { | |
animation-delay: 0.2s; | |
} | |
.dot:nth-child(3) { | |
animation-delay: 0.4s; | |
margin-right: 0; | |
} | |
@keyframes pulse { | |
0%, | |
100% { | |
transform: scale(0.7); | |
opacity: 0.5; | |
} | |
50% { | |
transform: scale(1); | |
opacity: 1; | |
} | |
} | |
.message-content p { | |
margin-bottom: 0.5rem; | |
} | |
.message-content p:last-child { | |
margin-bottom: 0; | |
} | |
.message-content strong { | |
font-weight: 600; | |
} | |
.message-content em { | |
font-style: normal; | |
color: #4b5563; | |
} | |
.dark .message-content em { | |
color: #9ca3af; | |
} | |
.message-content code { | |
font-family: "Menlo", "Monaco", "Courier New", monospace; | |
background-color: #f1f5f9; | |
padding: 0.1em 0.3em; | |
border-radius: 0.25rem; | |
font-size: 0.9em; | |
border: 1px solid #e2e8f0; | |
} | |
.dark .message-content code { | |
background-color: #1e293b; | |
border-color: #334155; | |
} | |
.message-content pre { | |
background-color: #f8fafc; | |
border: 1px solid #e2e8f0; | |
border-radius: 0.5rem; | |
padding: 1rem; | |
margin: 0.75rem 0; | |
overflow-x: auto; | |
} | |
.dark .message-content pre { | |
background-color: #1e293b; | |
border-color: #334155; | |
} | |
.message-content pre code { | |
background-color: transparent; | |
padding: 0; | |
border: none; | |
font-size: 0.9em; | |
display: block; | |
white-space: pre; | |
} | |
.message-content ul, | |
.message-content ol { | |
margin-left: 1.5rem; | |
margin-bottom: 0.75rem; | |
} | |
.message-content ul li, | |
.message-content ol li { | |
margin-bottom: 0.25rem; | |
} | |
.message-content blockquote { | |
border-left: 3px solid #cbd5e1; | |
padding-left: 1rem; | |
margin: 0.75rem 0; | |
color: #64748b; | |
} | |
.dark .message-content blockquote { | |
border-left-color: #475569; | |
color: #94a3b8; | |
} | |
</style> | |
<script> | |
document.addEventListener("DOMContentLoaded", () => { | |
const html = document.documentElement; | |
// Function to set theme based on system preference | |
function setThemeBasedOnSystemPreference() { | |
const prefersDark = window.matchMedia( | |
"(prefers-color-scheme: dark)" | |
).matches; | |
if (prefersDark) { | |
html.classList.add("dark"); | |
} else { | |
html.classList.remove("dark"); | |
} | |
} | |
// Initial theme setup based on system preference | |
setThemeBasedOnSystemPreference(); | |
// Listen for system preference changes | |
window | |
.matchMedia("(prefers-color-scheme: dark)") | |
.addEventListener("change", () => { | |
setThemeBasedOnSystemPreference(); | |
}); | |
// Check if user is logged in | |
const verifiedId = sessionStorage.getItem("verifiedId"); | |
if (!verifiedId) { | |
window.location.href = "/login"; | |
return; | |
} | |
// Logout functionality | |
const logoutButton = document.getElementById("logout-button"); | |
logoutButton.addEventListener("click", () => { | |
sessionStorage.removeItem("verifiedId"); | |
window.location.href = "/login"; | |
}); | |
const chatForm = document.getElementById("chat-form"); | |
const userInput = document.getElementById("user-input"); | |
const chatContainer = document.getElementById("chat-container"); | |
const loadingIndicator = document.getElementById("loading"); | |
const suggestionButtons = document.querySelectorAll( | |
".max-w-screen-md.mx-auto.px-4.md\\:px-0.mb-6 button" | |
); | |
// Model selector elements | |
const modelDropdownButton = document.getElementById( | |
"model-dropdown-button" | |
); | |
const modelDropdown = document.getElementById("model-dropdown"); | |
const modelOptions = document.querySelectorAll(".model-option"); | |
const selectedModelText = document.getElementById( | |
"selected-model-text" | |
); | |
// Default model | |
let selectedModel = "gemma2"; | |
// Toggle dropdown | |
modelDropdownButton.addEventListener("click", () => { | |
modelDropdown.classList.toggle("hidden"); | |
}); | |
// Close dropdown when clicking outside | |
document.addEventListener("click", (e) => { | |
if ( | |
!modelDropdownButton.contains(e.target) && | |
!modelDropdown.contains(e.target) | |
) { | |
modelDropdown.classList.add("hidden"); | |
} | |
}); | |
// Handle model selection | |
modelOptions.forEach((option) => { | |
option.addEventListener("click", () => { | |
selectedModel = option.getAttribute("data-value"); | |
selectedModelText.textContent = option.textContent; | |
modelDropdown.classList.add("hidden"); | |
// Add visual indication of selected model | |
modelOptions.forEach((opt) => | |
opt.classList.remove( | |
"bg-blue-50", | |
"text-blue-700", | |
"dark:bg-blue-900/50", | |
"dark:text-blue-300" | |
) | |
); | |
option.classList.add( | |
"bg-blue-50", | |
"text-blue-700", | |
"dark:bg-blue-900/50", | |
"dark:text-blue-300" | |
); | |
}); | |
}); | |
// Set default selected model | |
modelOptions[0].click(); | |
// Function to add a message to the chat | |
function formatMessage(text) { | |
// First, preserve code blocks by replacing them with placeholders | |
const codeBlocks = []; | |
text = text.replace( | |
/^```(?:([\w-]+)[ \t]*)?\r?\n([\s\S]*?)\r?\n```/gm, | |
function (match, lang, code) { | |
const id = codeBlocks.length; | |
codeBlocks.push({ lang: lang || null, code: code.trim() }); | |
return `__CODE_BLOCK_${id}__`; | |
} | |
); | |
// Handle inline code | |
const inlineCodes = []; | |
text = text.replace(/`([^`]+)`/g, function (match, code) { | |
const id = inlineCodes.length; | |
inlineCodes.push(code); | |
return `__INLINE_CODE_${id}__`; | |
}); | |
// Replace ** with proper styling | |
text = text.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>"); | |
// Handle numbered lists | |
text = text.replace( | |
/(\d+\.\s[^*]*?)(?=\d+\.|$)/g, | |
'<div class="mb-3">$1</div>' | |
); | |
// Handle sections with asterisks | |
text = text.replace(/\*(.*?)\*/g, "<em>$1</em>"); | |
// Handle unordered lists | |
text = text.replace(/^- (.*?)$/gm, "<li>$1</li>"); | |
text = text.replace( | |
/<li>.*?<\/li>(?:\s*<li>.*?<\/li>)+/gs, | |
function (match) { | |
return "<ul>" + match + "</ul>"; | |
} | |
); | |
// Handle ordered lists | |
text = text.replace(/^(\d+)\. (.*?)$/gm, "<li>$2</li>"); | |
text = text.replace( | |
/<li>.*?<\/li>(?:\s*<li>.*?<\/li>)+/gs, | |
function (match) { | |
return "<ol>" + match + "</ol>"; | |
} | |
); | |
// Handle blockquotes | |
text = text.replace(/^> (.*?)$/gm, "<blockquote>$1</blockquote>"); | |
// Add proper spacing between sections | |
text = text | |
.split("\n") | |
.map((line) => line.trim()) | |
.filter((line) => line) | |
.join("<p></p>"); | |
// Restore code blocks | |
codeBlocks.forEach((codeBlock, i) => { | |
text = text.replace( | |
`__CODE_BLOCK_${i}__`, | |
`<pre><code class="${escapeHtml(codeBlock.lang) ?? 'plaintext' | |
}">${escapeHtml(codeBlock.code)}</code></pre>` | |
); | |
}); | |
// Restore inline code | |
inlineCodes.forEach((code, i) => { | |
text = text.replace( | |
`__INLINE_CODE_${i}__`, | |
`<code>${escapeHtml(code)}</code>` | |
); | |
}); | |
return `<p>${text}</p>`; | |
} | |
// Helper function to escape HTML in code blocks | |
function escapeHtml(text) { | |
if (!text) return ""; | |
return text | |
.replace(/&/g, "&") | |
.replace(/</g, "<") | |
.replace(/>/g, ">") | |
.replace(/"/g, """) | |
.replace(/'/g, "'"); | |
} | |
// Function to add a message to the chat | |
function addMessage(content, isUser = false) { | |
// Hide the welcome message when chat starts | |
const welcomeMessage = chatContainer.querySelector( | |
".flex.flex-col.items-center.justify-center" | |
); | |
if (welcomeMessage) { | |
welcomeMessage.remove(); | |
} | |
// Hide suggestion buttons when chat starts | |
const suggestionContainer = document.querySelector( | |
".max-w-screen-md.mx-auto.px-4.md\\:px-0.mb-6" | |
); | |
if ( | |
suggestionContainer && | |
!suggestionContainer.classList.contains("hidden") | |
) { | |
suggestionContainer.classList.add("hidden"); | |
} | |
const messageDiv = document.createElement("div"); | |
messageDiv.className = "max-w-screen-md mx-auto mb-6"; | |
const formattedContent = isUser ? content : formatMessage(content); | |
const html = ` | |
<div class="flex items-start ${isUser ? 'justify-end' : ''}"> | |
${!isUser | |
? ` | |
<div class="flex justify-center items-center mr-2 w-8 h-8 bg-gray-100 rounded-full dark:bg-gray-700"> | |
<span class="font-semibold text-sm">AR</span> | |
</div> | |
` | |
: "" | |
} | |
<div class="${isUser | |
? 'bg-blue-50 dark:bg-blue-900/30 text-blue-800 dark:text-blue-100' | |
: 'bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-gray-200' | |
} p-3 rounded-lg max-w-[80%] message-content"> | |
${formattedContent} | |
</div> | |
${isUser | |
? ` | |
<div class="flex justify-center items-center ml-2 w-8 h-8 text-blue-800 bg-blue-100 rounded-full dark:text-blue-200 dark:bg-blue-900/50"> | |
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-user"><circle cx="12" cy="8" r="5"/><path d="M20 21a8 8 0 0 0-16 0"/></svg> | |
</div> | |
` | |
: "" | |
} | |
</div> | |
`; | |
messageDiv.innerHTML = html; | |
chatContainer.appendChild(messageDiv); | |
// Scroll to bottom | |
chatContainer.scrollTop = chatContainer.scrollHeight; | |
} | |
// Function to show loading indicator | |
function showLoading() { | |
loadingIndicator.classList.remove("hidden"); | |
chatContainer.scrollTop = chatContainer.scrollHeight; | |
} | |
// Function to hide loading indicator | |
function hideLoading() { | |
loadingIndicator.classList.add("hidden"); | |
} | |
// Handle form submission | |
chatForm.addEventListener("submit", async (e) => { | |
e.preventDefault(); | |
const message = userInput.value.trim(); | |
if (!message) return; | |
// Add user message to chat | |
addMessage(message, true); | |
// Clear input | |
userInput.value = ""; | |
// Show loading indicator | |
showLoading(); | |
try { | |
// Send message to API | |
const verifiedId = sessionStorage.getItem("verifiedId"); | |
const response = await fetch("/chat", { | |
method: "POST", | |
headers: { | |
"Content-Type": "application/json", | |
Authorization: `Bearer ${verifiedId}`, | |
}, | |
body: JSON.stringify({ | |
message: message, | |
model: selectedModel, | |
}), | |
}); | |
if (!response.ok) { | |
// If unauthorized, redirect to login | |
if (response.status === 401) { | |
sessionStorage.removeItem("verifiedId"); | |
window.location.href = "/login"; | |
return; | |
} | |
throw new Error("API request failed"); | |
} | |
const data = await response.json(); | |
// Hide loading indicator | |
hideLoading(); | |
// Add AI response to chat | |
addMessage(data.response); | |
} catch (error) { | |
console.error("Error:", error); | |
// Hide loading indicator | |
hideLoading(); | |
// Add error message | |
addMessage( | |
"Sorry, there was an error processing your request. Please try again." | |
); | |
} | |
}); | |
// Handle suggestion button clicks | |
suggestionButtons.forEach((button) => { | |
button.addEventListener("click", () => { | |
const mainText = button.querySelector(".font-medium").textContent; | |
const subText = button.querySelector( | |
".text-gray-500, .text-gray-400" | |
).textContent; | |
const fullText = `${mainText} ${subText}`; | |
// Set the input value | |
userInput.value = fullText; | |
// Submit the form | |
chatForm.dispatchEvent(new Event("submit")); | |
}); | |
}); | |
// Focus input on page load | |
userInput.focus(); | |
}); | |
</script> | |
</body> | |
</html> |