UNPKG

@persian-caesar/chatbot

Version:

A TypeScript-based chatbot package for Persian Caesar

314 lines (275 loc) 8.57 kB
<!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> <style> :root { --bg: #202123; --container-bg: #343541; --user-bg: #444654; --bot-bg: #343541; --text-color: #e5e5e5; --accent: #19a37f; --input-bg: #40414f; --border-radius: 16px; --font: 'Segoe UI', Tahoma, sans-serif; } * { box-sizing: border-box; } body { margin: 0; padding: 0; background: var(--bg); color: var(--text-color); font-family: var(--font); display: flex; justify-content: center; align-items: center; height: 100vh; } .chat-container { display: flex; flex-direction: column; width: 100%; max-width: 720px; height: 90vh; background: var(--container-bg); border-radius: var(--border-radius); box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); overflow: hidden; } header { padding: 1rem; text-align: center; font-size: 1.2rem; border-bottom: 1px solid #444654; } #log { flex: 1; padding: 1rem; overflow-y: auto; display: flex; flex-direction: column; gap: 0.75rem; } /* scrollbar */ #log::-webkit-scrollbar { width: 8px; } #log::-webkit-scrollbar-track { background: var(--container-bg); } #log::-webkit-scrollbar-thumb { background: #565869; border-radius: 4px; } .message { max-width: 80%; padding: 0.75rem 1rem; border-radius: var(--border-radius); line-height: 1.4; word-wrap: break-word; white-space: pre-wrap; position: relative; opacity: 0; animation: fadeIn 0.15s ease-out forwards; } .message.user { background: var(--user-bg); align-self: flex-end; border-bottom-right-radius: 4px; } .message.bot { background: var(--bot-bg); align-self: flex-start; border-bottom-left-radius: 4px; } @keyframes fadeIn { to { opacity: 1; } } .typing { display: flex; align-items: center; gap: 0.4rem; padding: 0.75rem 1rem; background: var(--bot-bg); border-radius: var(--border-radius); align-self: flex-start; } .typing span { display: block; width: 6px; height: 6px; background: #c1c2c5; border-radius: 50%; animation: blink 1s infinite; } .typing span:nth-child(2) { animation-delay: 0.2s; } .typing span:nth-child(3) { animation-delay: 0.4s; } @keyframes blink { 0%, 80%, 100% { opacity: 0; } 40% { opacity: 1; } } footer { padding: 0.75rem; border-top: 1px solid #444654; display: flex; gap: 0.5rem; align-items: flex-end; } #input { flex: 1; background: var(--input-bg); color: var(--text-color); border: none; border-radius: var(--border-radius); padding: 0.75rem; font-size: 1rem; line-height: 1.4; resize: none; max-height: 120px; overflow-y: auto; scrollbar-width: thin; scrollbar-color: #565869 var(--input-bg); } /* textarea scrollbar */ #input::-webkit-scrollbar { width: 6px; } #input::-webkit-scrollbar-track { background: var(--input-bg); } #input::-webkit-scrollbar-thumb { background: #565869; border-radius: 3px; } #input:focus { outline: 2px solid var(--accent); } #send { background: var(--accent); color: #fff; border: none; border-radius: var(--border-radius); padding: 0 1rem; cursor: pointer; font-size: 1rem; height: 3rem; align-self: center; } #send:disabled { background: #555; /* رنگ کم‌نور */ cursor: not-allowed; opacity: 0.5; } #send:active { transform: scale(0.97); } </style> </head> <body> <div class="chat-container"> <header>Persian Caesar ChatBot</header> <div id="log"></div> <footer> <textarea id="input" placeholder="پیام شما..." rows="1"></textarea> <button id="send">ارسال</button> </footer> </div> <script> const log = document.getElementById('log'); const input = document.getElementById('input'); const send = document.getElementById('send'); let typingIndicator; send.disabled = input.value.trim() === ''; function appendMessage(who, text) { const div = document.createElement('div'); div.classList.add('message', who === 'شما' ? 'user' : 'bot'); if (who === 'ربات') { typeText(div, text); } else { // حفظ شکست خطوط div.textContent = text; log.appendChild(div); log.scrollTop = log.scrollHeight; } } function showTyping() { typingIndicator = document.createElement('div'); typingIndicator.className = 'typing'; typingIndicator.innerHTML = '<span></span><span></span><span></span>'; log.appendChild(typingIndicator); log.scrollTop = log.scrollHeight; } function hideTyping() { if (typingIndicator) { log.removeChild(typingIndicator); typingIndicator = null; } } function typeText(container, text) { let idx = 0; container.textContent = ''; log.appendChild(container); log.scrollTop = log.scrollHeight; const interval = setInterval(() => { container.textContent += text[idx++] || ''; log.scrollTop = log.scrollHeight; if (idx > text.length) clearInterval(interval); }, 20); } async function sendMessage() { const msg = input.value; if (!msg.trim()) return; send.disabled = true; appendMessage('شما', msg); input.value = ''; input.style.height = 'auto'; showTyping(); try { const res = await fetch('http://localhost:3000/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: msg }) }); const data = await res.json(); hideTyping(); appendMessage('ربات', data.reply); } catch (err) { hideTyping(); appendMessage('خطا', 'ارتباط با سرور برقرار نشد.'); console.error(err); } } send.addEventListener('click', sendMessage); input.addEventListener('keydown', e => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } }); input.addEventListener('input', () => { send.disabled = input.value.trim() === ''; input.style.height = 'auto'; input.style.height = Math.min(input.scrollHeight, 120) + 'px'; }); </script> </body> </html>