Download Chat12b.html from Opera8/tts10: direct link, hf CLI and curl.
- Browser
- Download file 15.7 kB
-
https://huggingface.co/spaces/Opera8/tts10/resolve/main/Chat12b.html
- Command line
-
hf download hf://spaces/Opera8/tts10/Chat12b.html
-
curl -L -o Chat12b.html https://huggingface.co/spaces/Opera8/tts10/resolve/main/Chat12b.html
15.7 kB
| <html lang="fa" dir="rtl"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> | |
| <title>چتبات مالتیمدیا Gemma 4</title> | |
| <style> | |
| :root { | |
| --bg-color: #0b0f19; | |
| --chat-bg: #151c2c; | |
| --text-color: #f3f4f6; | |
| --primary-color: #3b82f6; | |
| --primary-hover: #2563eb; | |
| --user-msg: #2563eb; | |
| --bot-msg: #1f293d; | |
| --border-color: #374151; | |
| } | |
| * { | |
| box-sizing: border-box; | |
| margin: 0; | |
| padding: 0; | |
| font-family: Tahoma, Geneva, sans-serif; | |
| -webkit-tap-highlight-color: transparent; | |
| } | |
| body { | |
| background-color: var(--bg-color); | |
| color: var(--text-color); | |
| display: flex; | |
| flex-direction: column; | |
| height: 100vh; | |
| height: -webkit-fill-available; | |
| overflow: hidden; | |
| } | |
| html { | |
| height: -webkit-fill-available; | |
| } | |
| header { | |
| background-color: var(--chat-bg); | |
| padding: 15px; | |
| text-align: center; | |
| border-bottom: 1px solid var(--border-color); | |
| font-size: 1.1rem; | |
| font-weight: bold; | |
| box-shadow: 0 2px 4px rgba(0,0,0,0.2); | |
| } | |
| #chat-container { | |
| flex: 1; | |
| overflow-y: auto; | |
| padding: 15px; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 15px; | |
| background-color: var(--bg-color); | |
| } | |
| .message { | |
| max-width: 85%; | |
| padding: 12px 16px; | |
| border-radius: 18px; | |
| line-height: 1.6; | |
| font-size: 0.95rem; | |
| word-wrap: break-word; | |
| white-space: pre-wrap; | |
| box-shadow: 0 1px 2px rgba(0,0,0,0.1); | |
| } | |
| .user-message { | |
| background-color: var(--user-msg); | |
| color: #ffffff; | |
| align-self: flex-start; | |
| border-bottom-right-radius: 4px; | |
| } | |
| .bot-message { | |
| background-color: var(--bot-msg); | |
| color: var(--text-color); | |
| align-self: flex-end; | |
| border-bottom-left-radius: 4px; | |
| border: 1px solid var(--border-color); | |
| } | |
| .media-preview { | |
| max-width: 100%; | |
| max-height: 200px; | |
| border-radius: 8px; | |
| margin-top: 8px; | |
| display: block; | |
| } | |
| #input-container { | |
| background-color: var(--chat-bg); | |
| padding: 12px; | |
| border-top: 1px solid var(--border-color); | |
| display: flex; | |
| flex-direction: column; | |
| gap: 8px; | |
| } | |
| .input-row { | |
| display: flex; | |
| gap: 8px; | |
| align-items: center; | |
| } | |
| textarea { | |
| flex: 1; | |
| background-color: var(--bg-color); | |
| border: 1px solid var(--border-color); | |
| color: var(--text-color); | |
| padding: 12px; | |
| border-radius: 24px; | |
| resize: none; | |
| height: 48px; | |
| max-height: 100px; | |
| font-size: 0.95rem; | |
| outline: none; | |
| direction: rtl; | |
| } | |
| textarea:focus { | |
| border-color: var(--primary-color); | |
| } | |
| .btn { | |
| background-color: var(--primary-color); | |
| color: white; | |
| border: none; | |
| width: 48px; | |
| height: 48px; | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| transition: background 0.2s; | |
| flex-shrink: 0; | |
| } | |
| .btn:hover { | |
| background-color: var(--primary-hover); | |
| } | |
| .btn:disabled { | |
| background-color: #4b5563; | |
| cursor: not-allowed; | |
| } | |
| .file-label { | |
| background-color: #374151; | |
| color: white; | |
| width: 48px; | |
| height: 48px; | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| flex-shrink: 0; | |
| } | |
| #file-input { | |
| display: none; | |
| } | |
| #attachment-bar { | |
| display: none; | |
| background-color: #1f293d; | |
| padding: 6px 12px; | |
| border-radius: 8px; | |
| font-size: 0.85rem; | |
| align-items: center; | |
| justify-content: space-between; | |
| } | |
| #remove-file { | |
| color: #ef4444; | |
| cursor: pointer; | |
| font-weight: bold; | |
| padding: 0 5px; | |
| } | |
| .typing-indicator { | |
| display: flex; | |
| gap: 4px; | |
| padding: 8px; | |
| align-self: flex-end; | |
| } | |
| .typing-indicator span { | |
| width: 8px; | |
| height: 8px; | |
| background-color: #9ca3af; | |
| border-radius: 50%; | |
| animation: bounce 1.3s infinite both; | |
| } | |
| .typing-indicator span:nth-child(2) { animation-delay: 0.15s; } | |
| .typing-indicator span:nth-child(3) { animation-delay: 0.3s; } | |
| @keyframes bounce { | |
| 0%, 80%, 100% { transform: scale(0); } | |
| 40% { transform: scale(1); } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header>دستیار هوشمند Gemma 4 (12B)</header> | |
| <div id="chat-container"> | |
| <div class="message bot-message">سلام! من دستیار هوش مصنوعی شما هستم. میتوانید متن، تصویر یا فایل صوتی برای من ارسال کنید تا آن را تحلیل کنم.</div> | |
| </div> | |
| <div id="input-container"> | |
| <div id="attachment-bar"> | |
| <span id="file-name">فایلی انتخاب نشده</span> | |
| <span id="remove-file" onclick="clearAttachment()">✕</span> | |
| </div> | |
| <div class="input-row"> | |
| <label for="file-input" class="file-label"> | |
| 📎 | |
| </label> | |
| <input type="file" id="file-input" accept="image/*,audio/*,video/*" onchange="handleFileSelect(this)"> | |
| <textarea id="user-input" placeholder="پیام خود را بنویسید..." rows="1" oninput="autoGrow(this)"></textarea> | |
| <button id="send-btn" class="btn" onclick="sendMessage()">➔</button> | |
| </div> | |
| </div> | |
| <script> | |
| const SPACE_API_URL = "https://huggingface-projects-gemma-4-12b-it.hf.space"; | |
| let selectedFileData = null; | |
| function autoGrow(element) { | |
| element.style.height = "48px"; | |
| element.style.height = (element.scrollHeight) + "px"; | |
| } | |
| function generateSessionHash() { | |
| return Math.random().toString(36).substring(2, 13); | |
| } | |
| function handleFileSelect(input) { | |
| if (input.files && input.files[0]) { | |
| const file = input.files[0]; | |
| document.getElementById('file-name').innerText = file.name; | |
| document.getElementById('attachment-bar').style.display = 'flex'; | |
| selectedFileData = file; | |
| } | |
| } | |
| function clearAttachment() { | |
| document.getElementById('file-input').value = ""; | |
| document.getElementById('attachment-bar').style.display = 'none'; | |
| selectedFileData = null; | |
| } | |
| async function uploadFile(file) { | |
| const formData = new FormData(); | |
| formData.append('files', file); | |
| const response = await fetch(`${SPACE_API_URL}/gradio_api/upload`, { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| if (!response.ok) throw new Error('خطا در آپلود فایل به سرور'); | |
| const result = await response.json(); | |
| return result[0]; // بازگرداندن مسیر فایل روی سرور | |
| } | |
| async function sendMessage() { | |
| const inputEl = document.getElementById('user-input'); | |
| const sendBtn = document.getElementById('send-btn'); | |
| const prompt = inputEl.value.trim(); | |
| if (!prompt && !selectedFileData) return; | |
| // غیرفعال کردن موقت ورودیها | |
| inputEl.value = ''; | |
| inputEl.style.height = "48px"; | |
| inputEl.disabled = true; | |
| sendBtn.disabled = true; | |
| // نمایش پیام کاربر در صفحه چت | |
| const chatContainer = document.getElementById('chat-container'); | |
| const userMsgDiv = document.createElement('div'); | |
| userMsgDiv.className = 'message user-message'; | |
| userMsgDiv.innerText = prompt; | |
| let currentFile = selectedFileData; | |
| let fileUrlForDisplay = ''; | |
| if (currentFile) { | |
| fileUrlForDisplay = URL.createObjectURL(currentFile); | |
| if (currentFile.type.startsWith('image/')) { | |
| const img = document.createElement('img'); | |
| img.src = fileUrlForDisplay; | |
| img.className = 'media-preview'; | |
| userMsgDiv.appendChild(img); | |
| } else if (currentFile.type.startsWith('audio/')) { | |
| const aud = document.createElement('audio'); | |
| aud.src = fileUrlForDisplay; | |
| aud.controls = true; | |
| aud.className = 'media-preview'; | |
| userMsgDiv.appendChild(aud); | |
| } | |
| } | |
| chatContainer.appendChild(userMsgDiv); | |
| clearAttachment(); | |
| chatContainer.scrollTop = chatContainer.scrollHeight; | |
| // نمایش پویانمایی در حال تایپ ربات | |
| const typingDiv = document.createElement('div'); | |
| typingDiv.className = 'typing-indicator bot-message'; | |
| typingDiv.innerHTML = '<span></span><span></span><span></span>'; | |
| chatContainer.appendChild(typingDiv); | |
| chatContainer.scrollTop = chatContainer.scrollHeight; | |
| try { | |
| let uploadedPath = null; | |
| if (currentFile) { | |
| uploadedPath = await uploadFile(currentFile); | |
| } | |
| const sessionHash = generateSessionHash(); | |
| // آمادهسازی آرایه فایل بر اساس ساختار لاگ ارسالی شما | |
| const filesParam = uploadedPath ? [uploadedPath] : []; | |
| // ایجاد درخواست به صف Gradio | |
| const queueResponse = await fetch(`${SPACE_API_URL}/gradio_api/queue/join?`, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ | |
| data: [ | |
| prompt, | |
| filesParam, | |
| [], | |
| false, // thinking | |
| 4000, // max_new_tokens | |
| 560, // max_soft_tokens | |
| "شما یک دستیار هوش مصنوعی بسیار باهوش، مفید و مودب به زبان فارسی هستید.", | |
| 1, 0.95, 64, 1 | |
| ], | |
| fn_index: 0, | |
| session_hash: sessionHash | |
| }) | |
| }); | |
| if (!queueResponse.ok) throw new Error('سرور شلوغ است یا پاسخ نمیدهد.'); | |
| const queueData = await queueResponse.json(); | |
| const eventId = queueData.event_id; | |
| // پولینگ (Polling) برای دریافت پاسخ نهایی از صف سرور | |
| let isFinished = false; | |
| let botAnswer = "خطا در دریافت پاسخ نهایی"; | |
| while (!isFinished) { | |
| const statusResponse = await fetch(`${SPACE_API_URL}/gradio_api/queue/data?session_hash=${sessionHash}`); | |
| if (statusResponse.ok) { | |
| const textData = await statusResponse.text(); | |
| // پردازش خط به خط دادههای استریم SSE دیتا متداول در Gradio | |
| const lines = textData.split('\n'); | |
| for (let line of lines) { | |
| if (line.startsWith('data:')) { | |
| const jsonData = JSON.parse(line.substring(5)); | |
| if (jsonData.msg === 'process_completed') { | |
| const output = jsonData.output.data[0]; | |
| botAnswer = typeof output === 'object' ? (output.text || JSON.stringify(output)) : output; | |
| isFinished = true; | |
| break; | |
| } else if (jsonData.msg === 'process_generating' && jsonData.output && jsonData.output.data) { | |
| // پشتیبانی سطحی از استریم آنی متن در صورت ارسال توسط سرور | |
| const output = jsonData.output.data[0]; | |
| botAnswer = typeof output === 'object' ? (output.text || botAnswer) : output; | |
| } | |
| } | |
| } | |
| } | |
| if (!isFinished) { | |
| await new Promise(resolve => setTimeout(resolve, 1000)); // ۱ ثانیه صبر بین هر آپدیت وضعیت | |
| } | |
| } | |
| // حذف انیمیشن وضعیت در حال تایپ | |
| chatContainer.removeChild(typingDiv); | |
| // نمایش نهایی پاسخ ربات | |
| const botMsgDiv = document.createElement('div'); | |
| botMsgDiv.className = 'message bot-message'; | |
| botMsgDiv.innerText = botAnswer; | |
| chatContainer.appendChild(botMsgDiv); | |
| } catch (error) { | |
| console.error(error); | |
| chatContainer.removeChild(typingDiv); | |
| const errorMsgDiv = document.createElement('div'); | |
| errorMsgDiv.className = 'message bot-message'; | |
| errorMsgDiv.style.color = '#ef4444'; | |
| errorMsgDiv.innerText = `⚠️ خطا: ${error.message}. لطفا مجددا تلاش کنید یا از حجم فایل خود بکاهید.`; | |
| chatContainer.appendChild(errorMsgDiv); | |
| } finally { | |
| // فعالسازی مجدد فیلدها جهت ارسال پیام بعدی | |
| inputEl.disabled = false; | |
| sendBtn.disabled = false; | |
| inputEl.focus(); | |
| chatContainer.scrollTop = chatContainer.scrollHeight; | |
| } | |
| } | |
| // ارسال پیام با زدن دکمه اینتر (بدون Shift) | |
| document.getElementById('user-input').addEventListener('keydown', function(e) { | |
| if (e.key === 'Enter' && !e.shiftKey) { | |
| e.preventDefault(); | |
| sendMessage(); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> | |