anycoder-c0ad34ef / index.html
samirerty's picture
Upload folder using huggingface_hub
67666c0 verified
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>چت روم پیشرفته | ارتباط واقعی</title>
<!-- Persian Font: Vazirmatn -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@100;300;400;500;700;900&display=swap"
rel="stylesheet">
<!-- Icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--primary: #6366f1;
--primary-dark: #4f46e5;
--secondary: #ec4899;
--background: #0f172a;
--surface: rgba(30, 41, 59, 0.7);
--surface-solid: #1e293b;
--text: #f8fafc;
--text-muted: #94a3b8;
--border: rgba(148, 163, 184, 0.1);
--success: #10b981;
--warning: #f59e0b;
--glass: rgba(255, 255, 255, 0.05);
--shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37);
--message-sent: linear-gradient(135deg, var(--primary), var(--primary-dark));
--message-received: #334155;
}
[data-theme="light"] {
--background: #f1f5f9;
--surface: rgba(255, 255, 255, 0.8);
--surface-solid: #ffffff;
--text: #1e293b;
--text-muted: #64748b;
--border: rgba(148, 163, 184, 0.2);
--glass: rgba(255, 255, 255, 0.6);
--message-received: #e2e8f0;
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
outline: none;
}
html {
scroll-behavior: smooth;
}
body {
font-family: 'Vazirmatn', sans-serif;
background: var(--background);
color: var(--text);
min-height: 100vh;
overflow: hidden; /* App-like feel */
transition: background 0.3s ease, color 0.3s ease;
}
/* Animated Background */
.bg-animation {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
overflow: hidden;
pointer-events: none;
}
.bg-animation .circle {
position: absolute;
border-radius: 50%;
filter: blur(80px);
opacity: 0.3;
animation: float 20s infinite ease-in-out;
}
.circle:nth-child(1) {
width: 400px;
height: 400px;
background: var(--primary);
top: -100px;
right: -100px;
animation-delay: 0s;
}
.circle:nth-child(2) {
width: 300px;
height: 300px;
background: var(--secondary);
bottom: -50px;
left: -50px;
animation-delay: 5s;
}
@keyframes float {
0%, 100% { transform: translate(0, 0) scale(1); }
50% { transform: translate(-30px, 30px) scale(1.1); }
}
/* Header */
header {
position: fixed;
top: 0;
width: 100%;
padding: 0.8rem 2rem;
background: var(--glass);
backdrop-filter: blur(12px);
border-bottom: 1px solid var(--border);
z-index: 1000;
display: flex;
justify-content: space-between;
align-items: center;
height: 70px;
}
.logo {
font-size: 1.4rem;
font-weight: 900;
background: linear-gradient(135deg, var(--primary), var(--secondary));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
display: flex;
align-items: center;
gap: 0.5rem;
}
.header-actions {
display: flex;
gap: 1rem;
align-items: center;
}
.built-with {
font-size: 0.75rem;
color: var(--text-muted);
text-decoration: none;
padding: 0.5rem 1rem;
border-radius: 2rem;
background: var(--glass);
border: 1px solid var(--border);
transition: all 0.3s ease;
}
.built-with:hover {
background: var(--primary);
color: white;
transform: translateY(-2px);
}
.theme-toggle {
background: var(--glass);
border: 1px solid var(--border);
color: var(--text);
width: 40px;
height: 40px;
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
}
.theme-toggle:hover {
transform: rotate(180deg);
background: var(--primary);
}
/* Main Container */
.container {
width: 100%;
height: 100vh;
padding-top: 70px;
display: flex;
flex-direction: column;
}
/* Auth Screens */
.auth-container {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
padding: 2rem;
animation: fadeIn 0.5s ease;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.auth-box {
background: var(--surface);
backdrop-filter: blur(16px);
border: 1px solid var(--border);
border-radius: 2rem;
padding: 3rem;
width: 100%;
max-width: 420px;
text-align: center;
box-shadow: var(--shadow);
}
.auth-box h2 {
margin-bottom: 2rem;
font-size: 1.8rem;
}
.form-group {
margin-bottom: 1.5rem;
text-align: right;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
color: var(--text-muted);
font-size: 0.9rem;
}
.form-group input {
width: 100%;
padding: 0.9rem 1rem;
border: 2px solid var(--border);
border-radius: 1rem;
background: var(--glass);
color: var(--text);
font-family: inherit;
font-size: 1rem;
transition: all 0.3s;
}
.form-group input:focus {
border-color: var(--primary);
box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.1);
}
.btn {
width: 100%;
padding: 1rem;
border-radius: 1rem;
font-family: inherit;
font-size: 1rem;
font-weight: 700;
cursor: pointer;
transition: all 0.3s ease;
border: none;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
.btn-primary {
background: var(--primary);
color: white;
box-shadow: 0 4px 15px rgba(99, 102, 241, 0.4);
}
.btn-primary:hover {
background: var(--primary-dark);
transform: translateY(-2px);
}
.switch-auth {
margin-top: 1.5rem;
font-size: 0.9rem;
color: var(--text-muted);
}
.switch-auth a {
color: var(--primary);
text-decoration: none;
font-weight: 700;
cursor: pointer;
}
/* Chat Layout */
#chat-app {
display: none;
width: 100%;
height: calc(100vh - 70px);
max-width: 1600px;
margin: 0 auto;
padding: 1rem;
gap: 1rem;
}
.chat-layout {
display: grid;
grid-template-columns: 320px 1fr;
height: 100%;
gap: 1rem;
}
/* Sidebar */
.sidebar {
background: var(--surface);
backdrop-filter: blur(16px);
border: 1px solid var(--border);
border-radius: 1.5rem;
display: flex;
flex-direction: column;
overflow: hidden;
transition: transform 0.3s ease;
}
.sidebar-header {
padding: 1.5rem;
border-bottom: 1px solid var(--border);
display: flex;
align-items: center;
gap: 1rem;
}
.my-avatar {
width: 50px;
height: 50px;
border-radius: 50%;
background: linear-gradient(135deg, var(--secondary), var(--primary));
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
font-weight: bold;
color: white;
flex-shrink: 0;
}
.user-info h4 {
font-size: 1rem;
margin-bottom: 0.2rem;
}
.user-status {
font-size: 0.8rem;
color: var(--success);
display: flex;
align-items: center;
gap: 0.3rem;
}
.user-status::before {
content: '';
width: 8px;
height: 8px;
background: var(--success);
border-radius: 50%;
display: inline-block;
}
.rooms-section {
padding: 1rem;
flex: 1;
overflow-y: auto;
}
.section-title {
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 1px;
color: var(--text-muted);
margin-bottom: 1rem;
padding-right: 0.5rem;
}
.room-btn {
width: 100%;
padding: 1rem;
margin-bottom: 0.5rem;
border-radius: 1rem;
background: transparent;
border: none;
color: var(--text);
text-align: right;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
gap: 1rem;
position: relative;
}
.room-btn:hover {
background: var(--glass);
}
.room-btn.active {
background: rgba(99, 102, 241, 0.1);
color: var(--primary);
}
.room-btn.active::before {
content: '';
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
width: 4px;
height: 60%;
background: var(--primary);
border-radius: 4px 0 0 4px;
}
.logout-btn {
margin: 1rem;
padding: 1rem;
background: var(--glass);
border: 1px solid var(--border);
color: var(--text-muted);
border-radius: 1rem;
cursor: pointer;
transition: all 0.3s;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
.logout-btn:hover {
background: #ef4444;
color: white;
border-color: #ef4444;
}
/* Main Chat Area */
.chat-area {
background: var(--surface);
backdrop-filter: blur(16px);
border: 1px solid var(--border);
border-radius: 1.5rem;
display: flex;
flex-direction: column;
overflow: hidden;
position: relative;
}
.chat-area-header {
padding: 1rem 1.5rem;
border-bottom: 1px solid var(--border);
display: flex;
justify-content: space-between;
align-items: center;
background: rgba(0,0,0,0.02);
}
.chat-title h3 {
font-size: 1.1rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.messages-container {
flex: 1;
padding: 1.5rem;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 1rem;
}
/* Messages */
.message {
max-width: 70%;
padding: 0.8rem 1.2rem;
border-radius: 1.5rem;
position: relative;
line-height: 1.5;
font-size: 0.95rem;
word-wrap: break-word;
animation: popIn 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
@keyframes popIn {
from { opacity: 0; transform: scale(0.9); }
to { opacity: 1; transform: scale(1); }
}
/* RTL Specifics */
.message.sent {
align-self: flex-end; /* Right side in RTL */
background: var(--message-sent);
color: white;
border-bottom-right-radius: 0.25rem;
box-shadow: 0 4px 15px rgba(99, 102, 241, 0.2);
}
.message.received {
align-self: flex-start; /* Left side in RTL */
background: var(--message-received);
color: var(--text);
border-bottom-left-radius: 0.25rem;
border: 1px solid var(--border);
}
.message-meta {
font-size: 0.7rem;
margin-top: 0.3rem;
opacity: 0.7;
display: flex;
align-items: center;
justify-content: flex-end;
gap: 0.3rem;
}
.message-author {
font-weight: 700;
font-size: 0.8rem;
margin-bottom: 0.2rem;
display: block;
}
.date-divider {
text-align: center;
margin: 1.5rem 0;
position: relative;
}
.date-divider span {
background: var(--glass);
padding: 0.3rem 1rem;
border-radius: 1rem;
font-size: 0.75rem;
color: var(--text-muted);
border: 1px solid var(--border);
}
/* Input Area */
.input-area {
padding: 1rem 1.5rem;
background: var(--glass);
border-top: 1px solid var(--border);
display: flex;
align-items: center;
gap: 1rem;
}
.input-wrapper {
flex: 1;
position: relative;
}
.input-wrapper input {
width: 100%;
padding: 0.9rem 3.5rem 0.9rem 1.2rem;
border-radius: 2rem;
border: 2px solid var(--border);
background: var(--background);
color: var(--text);
font-family: inherit;
transition: all 0.3s;
}
.input-wrapper input:focus {
border-color: var(--primary);
}
.send-btn {
width: 50px;
height: 50px;
border-radius: 50%;
background: var(--primary);
color: white;
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s;
flex-shrink: 0;
}
.send-btn:hover {
background: var(--primary-dark);
transform: scale(1.1) rotate(-10deg);
}
.send-btn:active {
transform: scale(0.95);
}
/* Typing Indicator */
.typing-indicator-wrapper {
padding: 0 1.5rem 0.5rem;
height: 24px;
position: absolute;
bottom: 70px;
right: 20px;
pointer-events: none;
}
.typing-indicator {
display: inline-flex;
gap: 4px;
background: var(--message-received);
padding: 0.5rem 1rem;
border-radius: 1rem;
border: 1px solid var(--border);
align-items: center;
font-size: 0.75rem;
color: var(--text-muted);
}
.typing-indicator span {
width: 6px;
height: 6px;
background: var(--text-muted);
border-radius: 50%;
animation: bounce 1.4s infinite ease-in-out both;
}
.typing-indicator span:nth-child(1) { animation-delay: -0.32s; }
.typing-indicator span:nth-child(2) { animation-delay: -0.16s; }
@keyframes bounce {
0%, 80%, 100% { transform: scale(0); }
40% { transform: scale(1); }
}
/* Toast Notification */
.toast-container {
position: fixed;
top: 90px;
left: 20px; /* Left in RTL to not cover chat */
z-index: 2000;
display: flex;
flex-direction: column;
gap: 10px;
}
.toast {
background: var(--surface-solid);
border: 1px solid var(--border);
padding: 1rem 1.5rem;
border-radius: 1rem;
display: flex;
align-items: center;
gap: 0.8rem;
box-shadow: var(--shadow);
animation: slideInLeft 0.3s ease;
max-width: 300px;
}
@keyframes slideInLeft {
from { opacity: 0; transform: translateX(-50px); }
to { opacity: 1; transform: translateX(0); }
}
/* Responsive */
@media (max-width: 768px) {
.chat-layout {
grid-template-columns: 1fr;
}
.sidebar {
position: absolute;
top: 0;
right: 0;
width: 85%;
height: 100%;
z-index: 100;
transform: translateX(100%);
border-radius: 0;
}
.sidebar.active {
transform: translateX(0);
box-shadow: -10px 0 30px rgba(0,0,0,0.5);
}
.sidebar-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
z-index: 90;
display: none;
backdrop-filter: blur(2px);
}
.sidebar-overlay.active {
display: block;
}
.mobile-toggle {
display: flex !important;
}
.message {
max-width: 85%;
}
}
.mobile-toggle {
display: none;
background: none;
border: none;
color: var(--text);
font-size: 1.2rem;
cursor: pointer;
margin-left: 1rem;
}
/* Scrollbar */
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: var(--text-muted); }
</style>
</head>
<body>
<!-- Animated Background -->
<div class="bg-animation">
<div class="circle"></div>
<div class="circle"></div>
</div>
<!-- Header -->
<header>
<div class="logo">
<i class="fas fa-comments"></i>
<span>چت روم واقعی</span>
</div>
<div class="header-actions">
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="built-with">
<i class="fas fa-code"></i> Built with anycoder
</a>
<button class="theme-toggle" onclick="toggleTheme()" title="تغییر تم">
<i class="fas fa-moon"></i>
</button>
</div>
</header>
<div class="container">
<!-- Auth View -->
<div id="auth-view" class="auth-container">
<div class="auth-box">
<h2 id="auth-title">ورود به حساب</h2>
<form id="auth-form" onsubmit="handleAuth(event)">
<div class="form-group">
<label>نام کاربری</label>
<input type="text" id="username-input" placeholder="مثلاً: علی" required autocomplete="off">
</div>
<button type="submit" class="btn btn-primary">
<i class="fas fa-arrow-left"></i>
<span id="auth-btn-text">ورود</span>
</button>
</form>
<div class="switch-auth">
<a onclick="toggleAuthMode()" id="auth-switch-link">ثبت‌نام کنید</a>
</div>
<p style="margin-top: 2rem; font-size: 0.8rem; color: var(--text-muted); opacity: 0.7;">
<i class="fas fa-info-circle"></i>
برای تست واقعی، این صفحه را در دو تب مختلف باز کنید.
</p>
</div>
</div>
<!-- Chat View -->
<div id="chat-app">
<div class="chat-layout">
<!-- Mobile Overlay -->
<div class="sidebar-overlay" onclick="toggleSidebar()"></div>
<!-- Sidebar -->
<aside class="sidebar" id="sidebar">
<div class="sidebar-header">
<div class="my-avatar" id="current-user-avatar">A</div>
<div class="user-info">
<h4 id="current-user-name">نام کاربر</h4>
<span class="user-status">آنلاین</span>
</div>
</div>
<div class="rooms-section">
<div class="section-title">اتاق‌های گفتگو</div>
<div id="rooms-list">
<!-- Rooms will be injected here -->
</div>
</div>
<button class="logout-btn" onclick="logout()">
<i class="fas fa-sign-out-alt"></i>
خروج از حساب
</button>
</aside>
<!-- Main Chat -->
<main class="chat-area">
<div class="chat-area-header">
<div class="chat-title">
<button class="mobile-toggle" onclick="toggleSidebar()">
<i class="fas fa-bars"></i>
</button>
<h3 id="room-title">
<i class="fas fa-hashtag"></i>
<span>عمومی</span>
</h3>
</div>
<div style="display: flex; gap: 0.5rem;">
<button class="theme-toggle" style="width: 32px; height: 32px;" onclick="clearHistory()">
<i class="fas fa-trash-alt" title="پاک کردن تاریخچه"></i>
</button>
</div>
</div>
<div class="messages-container" id="messages-container">
<!-- Messages will be injected here -->
</div>
<div class="typing-indicator-wrapper" id="typing-indicator">
<div class="typing-indicator">
<span></span><span></span><span></span>
<span style="margin-right: 5px;" id="typer-name">کاربر</span> در حال نوشتن...
</div>
</div>
<div class="input-area">
<div class="input-wrapper">
<input type="text" id="message-input" placeholder="پیام خود را بنویسید..." autocomplete="off">
</div>
<button class="send-btn" onclick="sendMessage()">
<i class="fas fa-paper-plane"></i>
</button>
</div>
</main>
</div>
</div>
</div>
<!-- Toast Container -->
<div class="toast-container" id="toast-container"></div>
<!-- Sound Effect (Base64 for simplicity) -->
<script>
// Short pop sound for messages
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
function playNotificationSound() {
if (audioContext.state === 'suspended') audioContext.resume();
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.type = 'sine';
oscillator.frequency.setValueAtTime(500, audioContext.currentTime);
oscillator.frequency.exponentialRampToValueAtTime(1000, audioContext.currentTime + 0.1);
gainNode.gain.setValueAtTime(0.1, audioContext.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.3);
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.start();
oscillator.stop(audioContext.currentTime + 0.3);
}
</script>
<script>
// --- State Management ---
const STORE_KEY = 'persian_chat_v1';
const CHANNEL_NAME = 'persian_chat_realtime';
// Default Data Structure
const defaultData = {
users: [], // { username, color, joinedAt }
messages: [], // { id, roomId, username, text, timestamp, type }
rooms: [
{ id: 'general', name: 'عمومی', icon: 'fa-globe' },
{ id: 'tech', name: 'فناوری', icon: 'fa-laptop-code' },
{ id: 'random', name: 'گفتگوی آزاد', icon: 'fa-comments' }
]
};
let currentState = JSON.parse(localStorage.getItem(STORE_KEY)) || defaultData;
let currentUser = JSON.parse(sessionStorage.getItem('currentUser')) || null;
let currentRoomId = 'general';
let broadcastChannel = new BroadcastChannel(CHANNEL_NAME);
let typingTimeout = null;
// --- Core Logic ---
function saveState() {
localStorage.setItem(STORE_KEY, JSON.stringify(currentState));
}
function generateId() {
return Date.now().toString(36) + Math.random().toString(36).substr(2);
}
function getCurrentUserColor(username) {
const user = currentState.users.find(u => u.username === username);
return user ? user.color : '#6366f1';
}
// --- Auth Functions ---
function handleAuth(e) {
e.preventDefault();
const username = document.getElementById('username-input').value.trim();
if (!username) return showToast('لطفا نام کاربری را وارد کنید', 'error');
if (username.length < 3) return showToast('نام کاربری باید حداقل ۳ حرف باشد', 'error');
// Simulate login/register combined logic
const existingUser = currentState.users.find(u => u.username === username);
const userObj = existingUser || {
username: username,
color: `hsl(${Math.random() * 360}, 70%, 60%)`,
joinedAt: Date.now()
};
if (!existingUser) {
currentState.users.push(userObj);
saveState();
}
currentUser = userObj;
sessionStorage.setItem('currentUser', JSON.stringify(currentUser));
showToast(`خوش آمدید ${username}!`, 'success');
initChatApp();
}
function logout() {
currentUser = null;
sessionStorage.removeItem('currentUser');
document.getElementById('chat-app').style.display = 'none';
document.getElementById('auth-view').style.display = 'flex';
document.getElementById('username-input').value = '';
}
function initChatApp() {
if (!currentUser) return;
// UI Updates
document.getElementById('auth-view').style.display = 'none';
document.getElementById('chat-app').style.display = 'block';
document.getElementById('current-user-name').textContent = currentUser.username;
document.getElementById('current-user-avatar').textContent = currentUser.username.charAt(0).toUpperCase();
document.getElementById('current-user-avatar').style.background = currentUser.color;
renderRooms();
switchRoom('general');
// Broadcast Join Event
broadcastChannel.postMessage({
type: 'USER_JOINED',
payload: { username: currentUser.username }
});
}
// --- Chat Functions ---
function renderRooms() {
const container = document.getElementById('rooms-list');
container.innerHTML = currentState.rooms.map(room => `
<button class="room-btn ${room.id === currentRoomId ? 'active' : ''}" onclick="switchRoom('${room.id}')">
<i class="fas ${room.icon}" style="width: 20px; text-align: center;"></i>
<span>${room.name}</span>
</button>
`).join('');
}
function switchRoom(roomId) {
currentRoomId = roomId;
const room = currentState.rooms.find(r => r.id === roomId);
document.getElementById('room-title').innerHTML = `<i class="fas ${room.icon}"></i> ${room.name}`;
renderRooms();
renderMessages();
// Mobile: close sidebar
if (window.innerWidth <= 768) {
document.getElementById('sidebar').classList.remove('active');
document.querySelector('.sidebar-overlay').classList.remove('active');
}
}
function renderMessages() {
const container = document.getElementById('messages-container');
const roomMessages = currentState.messages.filter(m => m.roomId === currentRoomId);
container.innerHTML = '';
if (roomMessages.length === 0) {
container.innerHTML = `
<div style="text-align:center; padding: 2rem; color: var(--text-muted);">
<i class="fas fa-comments" style="font-size: 3rem; margin-bottom: 1rem; opacity: 0.3;"></i>
<p>هنوز پیامی ارسال نشده است.</p>
<p style="font-size: 0.8rem;">اولین نفری باشید که سلام می‌کند!</p>
</div>
`;
return;
}
let lastDate = null;
roomMessages.forEach((msg, index) => {
const msgDate = new Date(msg.timestamp).toLocaleDateString('fa-IR');
// Date Divider
if (msgDate !== lastDate) {
container.innerHTML += `
<div class="date-divider"><span>${msgDate}</span></div>
`;
lastDate = msgDate;
}
const isMe = msg.username === currentUser.username;
const time = new Date(msg.timestamp).toLocaleTimeString('fa-IR', { hour: '2-digit', minute: '2-digit' });
const msgEl = document.createElement('div');
msgEl.className = `message ${isMe ? 'sent' : 'received'}`;
msgEl.innerHTML = `
${!isMe ? `<span class="message-author" style="color: ${getCurrentUserColor(msg.username)}">${msg.username}</span>` : ''}
${escapeHtml(msg.text)}
<div class="message-meta">
<span>${time}</span>
${isMe ? '<i class="fas fa-check-double"></i>' : ''}
</div>
`;
container.appendChild(msgEl);
});
scrollToBottom();
}
function sendMessage() {
const input = document.getElementById('message-input');
const text = input.value.trim();
if (!text) return;
const newMessage = {
id: generateId(),
roomId: currentRoomId,
username: currentUser.username,
text: text,
timestamp: Date.now(),
type: 'text'
};
// Update Local State
currentState.messages.push(newMessage);
saveState();
// Update UI
input.value = '';
renderMessages();
// Broadcast to other tabs
broadcastChannel.postMessage({
type: 'NEW_MESSAGE',
payload: newMessage
});
// Stop typing indicator
stopTyping();
}
function scrollToBottom() {
const container = document.getElementById('messages-container');
container.scrollTop = container.scrollHeight;
}
function clearHistory() {
if(confirm('آیا مطمئن هستید که می‌خواهید تاریخچه این اتاق را پاک کنید؟')) {
currentState.messages = currentState.messages.filter(m => m.roomId !== currentRoomId);
saveState();
renderMessages();
showToast('تاریخچه پاک شد', 'success');
}
}
// --- Real-time Features (BroadcastChannel) ---
broadcastChannel.onmessage = (event) => {
const { type, payload } = event.data;
if (type === 'NEW_MESSAGE') {
// Only add if not exists (deduplication)
if (!currentState.messages.find(m => m.id === payload.id)) {
currentState.messages.push(payload);
saveState();
if (payload.roomId === currentRoomId) {
renderMessages();
if (payload.username !== currentUser.username) {
playNotificationSound();
showToast(`پیام جدید از ${payload.username}`, 'info');
}
}
}
}
else if (type === 'TYPING_START') {
if (payload.roomId === currentRoomId && payload.username !== currentUser.username) {
showTypingIndicator(payload.username);
}
}
else if (type