0% found this document useful (0 votes)
15 views13 pages

Cahtpage

The document outlines the structure and styling of an AI Chat Companion web application using HTML and CSS. It includes features such as a chat interface, theme toggle, voice assistant settings, and server connection options. The design is responsive and supports both light and dark themes, enhancing user interaction with animated elements.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
15 views13 pages

Cahtpage

The document outlines the structure and styling of an AI Chat Companion web application using HTML and CSS. It includes features such as a chat interface, theme toggle, voice assistant settings, and server connection options. The design is responsive and supports both light and dark themes, enhancing user interaction with animated elements.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 13

<!

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;
}

/* Toggle switch styling */


.switch {
position: relative;
display: inline-block;
width: 50px;
height: 24px;
}

.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); }
}

@media (max-width: 768px) {


.chat-container {
height: 90vh;
width: 100%;
border-radius: 12px;
}

.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="setting-panel" id="settings">


<div class="setting-item">
<h3>Server URL</h3>
<input type="text" id="server-url" placeholder="Enter server URL...">
<button id="connect-server">Connect</button>
</div>
<div class="setting-item">
<h3>Voice Assistant</h3>
<div class="voice-toggle">
<span>Off</span>
<label class="switch">
<input type="checkbox" id="voice-toggle">
<span class="slider"></span>
</label>
<span>On</span>
</div>
</div>
</div>

<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="messages-container" id="messages">


<div class="message message-ai">
Hello! I'm your AI assistant. How can I help you today?
<span class="message-time">Just now</span>
</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');

// Server connection state


document.addEventListener('DOMContentLoaded', function () {
// Ensure the DOM is fully loaded before accessing elements
const serverUrlInput = document.getElementById('server-url'); // Now this
element exists
const connectServerBtn = document.getElementById('connect-server'); // Ensure this
button exists
// Set default server URL if not found in localStorage
let serverUrl = localStorage.getItem('serverUrl') || 'http://127.0.0.1:5000';
let serverConnected = false;

// Initialize server URL from localStorage if available


if (serverUrlInput) {
serverUrlInput.value = serverUrl;
} else {
console.error('serverUrlInput element not found in the DOM.');
}
});

// 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>';

// Update active theme in settings


themeOptions.forEach(option => {
option.classList.remove('active');
if ((option.dataset.theme === 'dark' && document.body.dataset.theme
=== 'dark') ||
(option.dataset.theme === 'light' && !
document.body.dataset.theme)) {
option.classList.add('active');
}
});
});

// Settings panel toggle


settingsBtn.addEventListener('click', () => {
settingsPanel.classList.toggle('show');
});

// Click outside settings to close


document.addEventListener('click', (e) => {
if (!settingsPanel.contains(e.target) && e.target !== settingsBtn) {
settingsPanel.classList.remove('show');
}
});

// Theme options in settings


themeOptions.forEach(option => {
option.addEventListener('click', () => {
themeOptions.forEach(opt => opt.classList.remove('active'));
option.classList.add('active');

if (option.dataset.theme === 'dark') {


document.body.dataset.theme = 'dark';
themeToggle.innerHTML = '<i class="fas fa-sun"></i>';
} else {
document.body.dataset.theme = '';
themeToggle.innerHTML = '<i class="fas fa-moon"></i>';
}
});
});
// Connect to server button
connectServerBtn.addEventListener('click', () => {
serverUrl = serverUrlInput.value.trim();
if (serverUrl) {
localStorage.setItem('serverUrl', serverUrl);
checkServerConnection();
}
});

// Check server connection


async function checkServerConnection() {
connectionStatus.textContent = 'Connecting...';
statusLight.className = 'status-indicator';

try {
// Using a simple GET request to check if server is reachable
const timeoutPromise = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), 5000)
);

const fetchPromise = fetch(`${serverUrl}/chat`, {


method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ prompt: 'test connection' })
});

const response = await Promise.race([fetchPromise,


timeoutPromise]);

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.');
}
}

// Add message to chat


function addMessage(text, isUser = false) {
const messageDiv = document.createElement('div');
messageDiv.classList.add('message');
messageDiv.classList.add(isUser ? 'message-user' : 'message-ai');

// Get current time


const now = new Date();
const timeString = now.getHours() + ':' + (now.getMinutes() < 10 ?
'0' : '') + now.getMinutes();
messageDiv.innerHTML = `
${text}
<span class="message-time">${timeString}</span>
`;

messagesContainer.appendChild(messageDiv);
messagesContainer.scrollTop = messagesContainer.scrollHeight;

// Text-to-speech for AI responses


if (!isUser && voiceToggle.checked) {
speak(text);
}
}

// Show typing indicator


function showTypingIndicator() {
const indicator = document.createElement('div');
indicator.classList.add('typing-indicator');
indicator.innerHTML = `
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
`;
indicator.id = 'typing-indicator';
messagesContainer.appendChild(indicator);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}

// Remove typing indicator


function removeTypingIndicator() {
const indicator = document.getElementById('typing-indicator');
if (indicator) {
indicator.remove();
}
}

// Text to speech function


function speak(text) {
// Remove any HTML tags for speaking
const cleanText = text.replace(/<[^>]*>?/gm, '');

if ('speechSynthesis' in window) {
const speech = new SpeechSynthesisUtterance(cleanText);
speech.lang = 'en-US';
window.speechSynthesis.speak(speech);
}
}

// Handle user input submission


async function handleSubmit() {
const prompt = userInput.value.trim();

if (!prompt) {
return;
}

// Add user message to chat


addMessage(prompt, true);
userInput.value = '';

if (!serverConnected) {
addMessage('⚠️ Not connected to the AI server. Please connect
first.');
return;
}

// Show typing indicator


showTypingIndicator();

// Send request to AI
try {
const response = await fetch(`${serverUrl}/chat`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ prompt: prompt })
});

// Remove typing indicator


removeTypingIndicator();

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);
}
}

// Submit on button click


submitButton.addEventListener('click', handleSubmit);

// Submit on Enter key


userInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
handleSubmit();
}
});

// Clear chat history


clearChat.addEventListener('click', () => {
// Keep only the first welcome message
messagesContainer.innerHTML = '';
addMessage('Chat cleared. Ready for new conversation!');
});

// Check server on page load


document.addEventListener('DOMContentLoaded', () => {
checkServerConnection();
userInput.focus();
});
</script>
</body>
</html>

You might also like