| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>ChatVerse - Connect & Chat</title> |
| <link rel="icon" type="image/x-icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🌌</text></svg>"> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> |
| <script src="https://unpkg.com/feather-icons"></script> |
| <style> |
| @keyframes float { |
| 0%, 100% { transform: translateY(0px); } |
| 50% { transform: translateY(-20px); } |
| } |
| .float-animation { |
| animation: float 6s ease-in-out infinite; |
| } |
| .message-bubble { |
| animation: slideIn 0.3s ease-out; |
| } |
| @keyframes slideIn { |
| from { |
| opacity: 0; |
| transform: translateY(10px); |
| } |
| to { |
| opacity: 1; |
| transform: translateY(0); |
| } |
| } |
| .typing-indicator span { |
| animation: typing 1.4s infinite ease-in-out; |
| } |
| .typing-indicator span:nth-child(2) { |
| animation-delay: 0.2s; |
| } |
| .typing-indicator span:nth-child(3) { |
| animation-delay: 0.4s; |
| } |
| @keyframes typing { |
| 0%, 60%, 100% { |
| transform: translateY(0); |
| } |
| 30% { |
| transform: translateY(-10px); |
| } |
| } |
| </style> |
| </head> |
| <body class="bg-gradient-to-br from-purple-900 via-blue-900 to-indigo-900 min-h-screen"> |
| |
| <div id="loginScreen" class="min-h-screen flex items-center justify-center p-4"> |
| <div class="bg-white/10 backdrop-blur-lg rounded-3xl p-8 max-w-md w-full shadow-2xl border border-white/20"> |
| <div class="text-center mb-8"> |
| <div class="float-animation"> |
| <h1 class="text-5xl font-bold text-white mb-2">ChatVerse</h1> |
| <p class="text-purple-200">Connect with the universe 🌌</p> |
| </div> |
| </div> |
| <form id="loginForm" class="space-y-6"> |
| <div> |
| <label class="block text-purple-200 mb-2">Choose your username</label> |
| <input type="text" id="username" required |
| class="w-full px-4 py-3 rounded-xl bg-white/20 backdrop-blur text-white placeholder-purple-200 border border-purple-300/30 focus:outline-none focus:border-purple-400 transition" |
| placeholder="Enter username"> |
| </div> |
| <button type="submit" |
| class="w-full bg-gradient-to-r from-purple-500 to-pink-500 text-white py-3 rounded-xl font-semibold hover:from-purple-600 hover:to-pink-600 transform hover:scale-105 transition duration-300 shadow-lg"> |
| Join ChatVerse |
| </button> |
| </form> |
| </div> |
| </div> |
|
|
| |
| <div id="chatInterface" class="hidden h-screen flex flex-col"> |
| |
| <header class="bg-white/10 backdrop-blur-lg border-b border-white/20 p-4"> |
| <div class="flex items-center justify-between max-w-7xl mx-auto"> |
| <div class="flex items-center space-x-3"> |
| <h1 class="text-2xl font-bold text-white">ChatVerse</h1> |
| <span class="bg-green-400 w-3 h-3 rounded-full animate-pulse"></span> |
| </div> |
| <div class="flex items-center space-x-4"> |
| <span id="currentUser" class="text-purple-200"></span> |
| <button onclick="logout()" class="text-purple-200 hover:text-white transition"> |
| <i data-feather="log-out"></i> |
| </button> |
| </div> |
| </div> |
| </header> |
|
|
| <div class="flex-1 flex overflow-hidden"> |
| |
| <aside class="w-64 bg-white/5 backdrop-blur-lg border-r border-white/10 p-4 overflow-y-auto"> |
| <h2 class="text-purple-200 font-semibold mb-4 flex items-center"> |
| <i data-feather="users" class="w-4 h-4 mr-2"></i> |
| Online Users (<span id="onlineCount">0</span>) |
| </h2> |
| <div id="usersList" class="space-y-2"> |
| |
| </div> |
| </aside> |
|
|
| |
| <main class="flex-1 flex flex-col"> |
| |
| <div id="messagesContainer" class="flex-1 overflow-y-auto p-6 space-y-4"> |
| |
| </div> |
|
|
| |
| <div id="typingIndicator" class="px-6 py-2 hidden"> |
| <div class="typing-indicator flex items-center space-x-1 text-purple-300"> |
| <span class="w-2 h-2 bg-purple-300 rounded-full"></span> |
| <span class="w-2 h-2 bg-purple-300 rounded-full"></span> |
| <span class="w-2 h-2 bg-purple-300 rounded-full"></span> |
| <span class="text-sm ml-2" id="typingText"></span> |
| </div> |
| </div> |
|
|
| |
| <div class="bg-white/10 backdrop-blur-lg border-t border-white/20 p-4"> |
| <form id="messageForm" class="flex space-x-3"> |
| <input type="text" id="messageInput" |
| placeholder="Type your message..." |
| class="flex-1 px-4 py-3 rounded-xl bg-white/20 backdrop-blur text-white placeholder-purple-200 border border-purple-300/30 focus:outline-none focus:border-purple-400 transition" |
| autocomplete="off"> |
| <button type="submit" |
| class="bg-gradient-to-r from-purple-500 to-pink-500 text-white px-6 py-3 rounded-xl hover:from-purple-600 hover:to-pink-600 transform hover:scale-105 transition duration-300 shadow-lg"> |
| <i data-feather="send" class="w-5 h-5"></i> |
| </button> |
| </form> |
| </div> |
| </main> |
| </div> |
| </div> |
|
|
| <script> |
| |
| class ChatSystem { |
| constructor() { |
| this.currentUser = null; |
| this.users = new Set(); |
| this.messages = []; |
| this.broadcastChannel = new BroadcastChannel('chatverse'); |
| this.typingTimeout = null; |
| this.isTyping = false; |
| |
| this.broadcastChannel.onmessage = (event) => { |
| const { type, data } = event.data; |
| this.handleMessage(type, data); |
| }; |
| } |
| |
| handleMessage(type, data) { |
| switch(type) { |
| case 'userJoined': |
| this.addUser(data.username); |
| break; |
| case 'userLeft': |
| this.removeUser(data.username); |
| break; |
| case 'message': |
| this.addMessage(data); |
| break; |
| case 'typing': |
| this.showTyping(data.username); |
| break; |
| case 'stopTyping': |
| this.hideTyping(); |
| break; |
| } |
| } |
| |
| broadcast(type, data) { |
| this.broadcastChannel.postMessage({ type, data }); |
| } |
| |
| login(username) { |
| this.currentUser = username; |
| localStorage.setItem('currentUser', username); |
| this.broadcast('userJoined', { username }); |
| this.addUser(username); |
| this.loadMessages(); |
| } |
| |
| logout() { |
| if (this.currentUser) { |
| this.broadcast('userLeft', { username: this.currentUser }); |
| localStorage.removeItem('currentUser'); |
| this.removeUser(this.currentUser); |
| this.currentUser = null; |
| } |
| } |
| |
| addUser(username) { |
| this.users.add(username); |
| this.updateUsersList(); |
| } |
| |
| removeUser(username) { |
| this.users.delete(username); |
| this.updateUsersList(); |
| } |
| |
| updateUsersList() { |
| const usersList = document.getElementById('usersList'); |
| const onlineCount = document.getElementById('onlineCount'); |
| |
| usersList.innerHTML = ''; |
| onlineCount.textContent = this.users.size; |
| |
| this.users.forEach(username => { |
| const userDiv = document.createElement('div'); |
| userDiv.className = 'flex items-center space-x-2 p-2 rounded-lg hover:bg-white/10 transition cursor-pointer'; |
| userDiv.innerHTML = ` |
| <div class="w-2 h-2 bg-green-400 rounded-full"></div> |
| <span class="text-purple-200">${username}</span> |
| ${username === this.currentUser ? '<span class="text-xs text-purple-400">(You)</span>' : ''} |
| `; |
| usersList.appendChild(userDiv); |
| }); |
| } |
| |
| sendMessage(content) { |
| const message = { |
| id: Date.now(), |
| username: this.currentUser, |
| content: content, |
| timestamp: new Date().toISOString() |
| }; |
| |
| this.messages.push(message); |
| this.broadcast('message', message); |
| this.addMessage(message); |
| this.saveMessages(); |
| } |
| |
| addMessage(message) { |
| const messagesContainer = document.getElementById('messagesContainer'); |
| const messageDiv = document.createElement('div'); |
| messageDiv.className = `message-bubble flex ${message.username === this.currentUser ? 'justify-end' : 'justify-start'}`; |
| |
| const time = new Date(message.timestamp).toLocaleTimeString('en-US', { |
| hour: '2-digit', |
| minute: '2-digit' |
| }); |
| |
| messageDiv.innerHTML = ` |
| <div class="max-w-xs lg:max-w-md px-4 py-2 rounded-2xl ${ |
| message.username === this.currentUser |
| ? 'bg-gradient-to-r from-purple-500 to-pink-500 text-white' |
| : 'bg-white/20 backdrop-blur text-purple-100' |
| }"> |
| <div class="flex items-baseline space-x-2"> |
| <span class="font-semibold text-sm">${message.username}</span> |
| <span class="text-xs opacity-70">${time}</span> |
| </div> |
| <p class="mt-1">${message.content}</p> |
| </div> |
| `; |
| |
| messagesContainer.appendChild(messageDiv); |
| messagesContainer.scrollTop = messagesContainer.scrollHeight; |
| } |
| |
| showTyping(username) { |
| if (username === this.currentUser) return; |
| |
| const typingIndicator = document.getElementById('typingIndicator'); |
| const typingText = document.getElementById('typingText'); |
| |
| typingText.textContent = `${username} is typing...`; |
| typingIndicator.classList.remove('hidden'); |
| } |
| |
| hideTyping() { |
| document.getElementById('typingIndicator').classList.add('hidden'); |
| } |
| |
| handleTyping() { |
| if (!this.isTyping) { |
| this.isTyping = true; |
| this.broadcast('typing', { username: this.currentUser }); |
| } |
| |
| clearTimeout(this.typingTimeout); |
| this.typingTimeout = setTimeout(() => { |
| this.isTyping = false; |
| this.broadcast('stopTyping', {}); |
| }, 1000); |
| } |
| |
| saveMessages() { |
| localStorage.setItem('chatMessages', JSON.stringify(this.messages)); |
| } |
| |
| loadMessages() { |
| const saved = localStorage.getItem('chatMessages'); |
| if (saved) { |
| this.messages = JSON.parse(saved); |
| this.messages.forEach(msg => this.addMessage(msg)); |
| } |
| } |
| } |
| |
| |
| const chatSystem = new ChatSystem(); |
| |
| |
| window.addEventListener('DOMContentLoaded', () => { |
| const savedUser = localStorage.getItem('currentUser'); |
| if (savedUser) { |
| showChatInterface(savedUser); |
| } |
| }); |
| |
| |
| document.getElementById('loginForm').addEventListener('submit', (e) => { |
| e.preventDefault(); |
| const username = document.getElementById('username').value.trim(); |
| if (username) { |
| chatSystem.login(username); |
| showChatInterface(username); |
| } |
| }); |
| |
| |
| document.getElementById('messageForm').addEventListener('submit', (e) => { |
| e.preventDefault(); |
| const input = document.getElementById('messageInput'); |
| const message = input.value.trim(); |
| if (message) { |
| chatSystem.sendMessage(message); |
| input.value = ''; |
| chatSystem.hideTyping(); |
| } |
| }); |
| |
| |
| document.getElementById('messageInput').addEventListener('input', () => { |
| chatSystem.handleTyping(); |
| }); |
| |
| |
| function showChatInterface(username) { |
| document.getElementById('loginScreen').classList.add('hidden'); |
| document.getElementById('chatInterface').classList.remove('hidden'); |
| document.getElementById('currentUser').textContent = username; |
| feather.replace(); |
| } |
| |
| |
| function logout() { |
| chatSystem.logout(); |
| document.getElementById('chatInterface').classList.add('hidden'); |
| document.getElementById('loginScreen').classList.remove('hidden'); |
| document.getElementById('messagesContainer').innerHTML = ''; |
| document.getElementById('username').value = ''; |
| } |
| |
| |
| feather.replace(); |
| </script> |
| </body> |
| </html> |
|
|