Cahtpage
Cahtpage
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Chat Companion</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/
all.min.css" rel="stylesheet">
<style>
:root {
--primary-color: #6c5ce7;
--secondary-color: #a29bfe;
--accent-color: #fd79a8;
--bg-color: #f9f9f9;
--chat-bg: #ffffff;
--text-color: #333;
--message-user-bg: #e4f0fb;
--message-ai-bg: #f0f4fd;
--shadow: 0 8px 30px rgba(0, 0, 0, 0.06);
}
[data-theme="dark"] {
--primary-color: #9f86ff;
--secondary-color: #6c5ce7;
--accent-color: #ff7eb3;
--bg-color: #1a1a2e;
--chat-bg: #202040;
--text-color: #f2f2f2;
--message-user-bg: #4d4d6e;
--message-ai-bg: #3c3c60;
--shadow: 0 8px 30px rgba(0, 0, 0, 0.2);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
transition: all 0.3s ease;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: var(--bg-color);
color: var(--text-color);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
}
.chat-container {
width: 100%;
max-width: 900px;
background-color: var(--chat-bg);
border-radius: 16px;
overflow: hidden;
box-shadow: var(--shadow);
display: flex;
flex-direction: column;
height: 80vh;
}
.chat-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20px;
background: linear-gradient(135deg, var(--primary-color), var(--
secondary-color));
color: white;
}
.chat-title {
display: flex;
align-items: center;
gap: 12px;
}
.chat-title h1 {
font-size: 1.5rem;
font-weight: 600;
}
.avatar {
width: 40px;
height: 40px;
background-color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.avatar i {
font-size: 20px;
color: var(--primary-color);
}
.chat-controls {
display: flex;
gap: 15px;
}
.control-btn {
background: rgba(255, 255, 255, 0.2);
border: none;
width: 36px;
height: 36px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
color: white;
font-size: 16px;
}
.control-btn:hover {
background: rgba(255, 255, 255, 0.3);
}
.messages-container {
flex-grow: 1;
padding: 20px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 15px;
}
.message {
max-width: 80%;
padding: 14px 18px;
border-radius: 18px;
font-size: 0.95rem;
line-height: 1.5;
position: relative;
animation: fadeIn 0.3s ease;
}
.message-user {
align-self: flex-end;
background-color: var(--message-user-bg);
border-bottom-right-radius: 4px;
}
.message-ai {
align-self: flex-start;
background-color: var(--message-ai-bg);
border-bottom-left-radius: 4px;
}
.message-time {
font-size: 0.7rem;
opacity: 0.7;
margin-top: 5px;
display: block;
text-align: right;
}
.typing-indicator {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 10px 15px;
background-color: var(--message-ai-bg);
border-radius: 18px;
font-size: 0.9rem;
margin-top: 10px;
align-self: flex-start;
animation: fadeIn 0.3s ease;
}
.dot {
width: 7px;
height: 7px;
background-color: var(--text-color);
border-radius: 50%;
animation: bounce 1.5s infinite;
opacity: 0.7;
}
.dot:nth-child(2) {
animation-delay: 0.2s;
}
.dot:nth-child(3) {
animation-delay: 0.4s;
}
.chat-input-area {
display: flex;
padding: 15px 20px;
background-color: rgba(0, 0, 0, 0.02);
border-top: 1px solid rgba(0, 0, 0, 0.05);
}
.chat-input-area input {
flex-grow: 1;
padding: 14px 20px;
border: none;
border-radius: 10px;
background-color: var(--chat-bg);
color: var(--text-color);
font-size: 0.95rem;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
}
.chat-input-area input:focus {
outline: none;
box-shadow: 0 2px 15px rgba(0, 0, 0, 0.1);
}
.chat-input-area button {
margin-left: 10px;
width: 50px;
height: 50px;
border-radius: 50%;
background: linear-gradient(135deg, var(--primary-color), var(--
secondary-color));
color: white;
border: none;
font-size: 1.2rem;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.chat-input-area button:hover {
transform: scale(1.05);
}
#theme-toggle {
position: fixed;
top: 20px;
right: 20px;
background-color: var(--chat-bg);
color: var(--text-color);
border: none;
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 1.2rem;
box-shadow: var(--shadow);
}
.setting-panel {
position: absolute;
top: 80px;
right: 20px;
background-color: var(--chat-bg);
border-radius: 10px;
padding: 15px;
box-shadow: var(--shadow);
z-index: 10;
width: 250px;
display: none;
}
.setting-panel.show {
display: block;
animation: fadeIn 0.3s ease;
}
.setting-item {
margin-bottom: 15px;
}
.setting-item h3 {
font-size: 0.9rem;
margin-bottom: 8px;
font-weight: 500;
}
.theme-options {
display: flex;
gap: 10px;
}
.theme-option {
width: 25px;
height: 25px;
border-radius: 50%;
cursor: pointer;
border: 2px solid transparent;
}
.theme-option.active {
border-color: var(--accent-color);
}
.theme-light {
background: linear-gradient(135deg, #f5f7fa, #c3cfe2);
}
.theme-dark {
background: linear-gradient(135deg, #1a1a2e, #16213e);
}
.voice-toggle {
display: flex;
align-items: center;
gap: 8px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .4s;
border-radius: 34px;
}
.slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 4px;
bottom: 4px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
input:checked + .slider {
background-color: var(--primary-color);
}
input:focus + .slider {
box-shadow: 0 0 1px var(--primary-color);
}
input:checked + .slider:before {
transform: translateX(26px);
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-5px); }
}
.message {
max-width: 90%;
}
.chat-header {
padding: 15px;
}
.chat-title h1 {
font-size: 1.2rem;
}
}
</style>
</head>
<body>
<button id="theme-toggle"><i class="fas fa-moon"></i></button>
<div class="chat-container">
<div class="chat-header">
<div class="chat-title">
<div class="avatar">
<i class="fas fa-robot"></i>
</div>
<h1>AI Assistant</h1>
</div>
<div class="chat-controls">
<button class="control-btn" id="clear-chat">
<i class="fas fa-trash-alt"></i>
</button>
<button class="control-btn" id="settings-btn">
<i class="fas fa-cog"></i>
</button>
</div>
</div>
<div class="chat-input-area">
<input type="text" id="userInput" placeholder="Type your message
here..." autocomplete="off">
<button id="submitButton"><i class="fas fa-paper-plane"></i></button>
</div>
</div>
<script>
// DOM Elements
const messagesContainer = document.getElementById('messages');
const userInput = document.getElementById('userInput');
const submitButton = document.getElementById('submitButton');
const clearChat = document.getElementById('clear-chat');
const themeToggle = document.getElementById('theme-toggle');
const settingsBtn = document.getElementById('settings-btn');
const settingsPanel = document.getElementById('settings');
const themeOptions = document.querySelectorAll('.theme-option');
const voiceToggle = document.getElementById('voice-toggle');
const serverUrlInput = document.getElementById('server-url');
const connectServerBtn = document.getElementById('connect-server');
const statusLight = document.getElementById('status-light');
const connectionStatus = document.getElementById('connection-status');
// Theme handling
themeToggle.addEventListener('click', () => {
document.body.dataset.theme = document.body.dataset.theme === 'dark' ?
'' : 'dark';
themeToggle.innerHTML = document.body.dataset.theme === 'dark' ?
'<i class="fas fa-sun"></i>' : '<i class="fas fa-moon"></i>';
try {
// Using a simple GET request to check if server is reachable
const timeoutPromise = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), 5000)
);
if (response.ok) {
connectionStatus.textContent = 'Connected';
statusLight.className = 'status-indicator connected';
serverConnected = true;
addMessage('🔌 Connection to AI server established!');
} else {
throw new Error('Server responded with an error');
}
} catch (error) {
console.error('Connection error:', error);
connectionStatus.textContent = 'Disconnected - ' + (error.message
|| 'Unknown error');
statusLight.className = 'status-indicator disconnected';
serverConnected = false;
addMessage('❌ Failed to connect to AI server. Check the URL and try
again.');
}
}
messagesContainer.appendChild(messageDiv);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
if ('speechSynthesis' in window) {
const speech = new SpeechSynthesisUtterance(cleanText);
speech.lang = 'en-US';
window.speechSynthesis.speak(speech);
}
}
if (!prompt) {
return;
}
if (!serverConnected) {
addMessage('⚠️ Not connected to the AI server. Please connect
first.');
return;
}
// Send request to AI
try {
const response = await fetch(`${serverUrl}/chat`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ prompt: prompt })
});
if (response.ok) {
const data = await response.json();
addMessage(data.response || "I received your message but
couldn't generate a response.");
} else {
throw new Error(`Server returned status: ${response.status}`);
}
} catch (error) {
console.error('Error:', error);
removeTypingIndicator();
addMessage(`❌ Error: ${error.message || "Couldn't connect to the AI
server."}`, false);
}
}