| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>ZAYA1-8B — Zyphra Reasoning Model</title> |
| <meta name="description" content="Chat with ZAYA1-8B, a highly efficient reasoning model by Zyphra with 760M active parameters."> |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet"> |
| <style> |
| *{margin:0;padding:0;box-sizing:border-box} |
| :root{ |
| --bg:#0a0a0f;--surface:#12121a;--surface2:#1a1a28;--surface3:#22223a; |
| --border:#2a2a40;--border-glow:rgba(139,92,246,.25); |
| --text:#e4e4ef;--text2:#9898b0;--text3:#6a6a80; |
| --accent:#8b5cf6;--accent2:#a78bfa;--accent-glow:rgba(139,92,246,.15); |
| --green:#34d399;--orange:#fb923c;--red:#f87171; |
| --user-bg:linear-gradient(135deg,#8b5cf6 0%,#6d28d9 100%); |
| --radius:16px;--radius-sm:10px; |
| } |
| html,body{height:100%;font-family:'Inter',sans-serif;background:var(--bg);color:var(--text);overflow:hidden} |
|
|
| /* ── Layout ── */ |
| .app{display:flex;height:100vh;width:100vw} |
| .sidebar{width:300px;background:var(--surface);border-right:1px solid var(--border);display:flex;flex-direction:column;flex-shrink:0;transition:transform .3s} |
| .main{flex:1;display:flex;flex-direction:column;min-width:0} |
|
|
| /* ── Sidebar ── */ |
| .sidebar-header{padding:20px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:12px} |
| .logo{width:36px;height:36px;border-radius:10px;background:var(--user-bg);display:flex;align-items:center;justify-content:center;font-weight:700;font-size:14px;color:#fff} |
| .brand h1{font-size:16px;font-weight:700;background:linear-gradient(135deg,var(--accent2),var(--accent));-webkit-background-clip:text;-webkit-text-fill-color:transparent} |
| .brand span{font-size:11px;color:var(--text3);font-weight:500} |
| .sidebar-body{flex:1;overflow-y:auto;padding:12px} |
| .new-chat-btn{width:100%;padding:12px;border-radius:var(--radius-sm);border:1px dashed var(--border);background:transparent;color:var(--text2);cursor:pointer;font-size:13px;font-family:inherit;transition:all .2s;margin-bottom:8px} |
| .new-chat-btn:hover{border-color:var(--accent);color:var(--accent);background:var(--accent-glow)} |
| .chat-item{padding:10px 12px;border-radius:var(--radius-sm);cursor:pointer;font-size:13px;color:var(--text2);transition:all .15s;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-bottom:2px} |
| .chat-item:hover,.chat-item.active{background:var(--surface2);color:var(--text)} |
| .sidebar-footer{padding:16px;border-top:1px solid var(--border)} |
| .model-badge{display:flex;align-items:center;gap:8px;padding:10px 12px;border-radius:var(--radius-sm);background:var(--surface2);font-size:12px;color:var(--text2)} |
| .model-badge .dot{width:8px;height:8px;border-radius:50%;background:var(--green);box-shadow:0 0 8px var(--green)} |
|
|
| /* ── Chat Area ── */ |
| .chat-header{padding:16px 24px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:between;gap:12px;background:rgba(18,18,26,.8);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px)} |
| .chat-header h2{font-size:15px;font-weight:600;flex:1} |
| .header-controls{display:flex;gap:8px} |
| .icon-btn{width:36px;height:36px;border-radius:var(--radius-sm);border:1px solid var(--border);background:transparent;color:var(--text2);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s;font-size:16px} |
| .icon-btn:hover{border-color:var(--accent);color:var(--accent);background:var(--accent-glow)} |
|
|
| .messages{flex:1;overflow-y:auto;padding:24px;display:flex;flex-direction:column;gap:20px;scroll-behavior:smooth} |
|
|
| /* ── Welcome ── */ |
| .welcome{display:flex;flex-direction:column;align-items:center;justify-content:center;flex:1;text-align:center;gap:16px;padding:40px;animation:fadeUp .6s ease} |
| .welcome-icon{width:72px;height:72px;border-radius:20px;background:var(--user-bg);display:flex;align-items:center;justify-content:center;font-size:28px;box-shadow:0 8px 32px rgba(139,92,246,.3)} |
| .welcome h2{font-size:24px;font-weight:700;background:linear-gradient(135deg,#fff,var(--accent2));-webkit-background-clip:text;-webkit-text-fill-color:transparent} |
| .welcome p{color:var(--text3);font-size:14px;max-width:480px;line-height:1.6} |
| .suggestions{display:grid;grid-template-columns:1fr 1fr;gap:10px;max-width:520px;width:100%;margin-top:8px} |
| .suggestion{padding:14px 16px;border-radius:var(--radius-sm);border:1px solid var(--border);background:var(--surface);cursor:pointer;text-align:left;font-size:13px;color:var(--text2);transition:all .2s;font-family:inherit;line-height:1.4} |
| .suggestion:hover{border-color:var(--accent);background:var(--accent-glow);color:var(--text);transform:translateY(-1px)} |
| .suggestion strong{display:block;color:var(--text);margin-bottom:4px;font-size:12px;font-weight:600} |
|
|
| /* ── Messages ── */ |
| .msg{display:flex;gap:12px;max-width:800px;width:100%;margin:0 auto;animation:fadeUp .3s ease} |
| .msg.user{flex-direction:row-reverse} |
| .avatar{width:32px;height:32px;border-radius:10px;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:14px;font-weight:600} |
| .msg.user .avatar{background:var(--user-bg);color:#fff} |
| .msg.assistant .avatar{background:var(--surface3);color:var(--accent2);border:1px solid var(--border)} |
| .bubble{padding:14px 18px;border-radius:var(--radius);font-size:14px;line-height:1.7;max-width:75%;word-wrap:break-word} |
| .msg.user .bubble{background:var(--surface2);border:1px solid var(--border);color:var(--text)} |
| .msg.assistant .bubble{background:var(--surface);border:1px solid var(--border);color:var(--text)} |
| .msg.assistant .bubble p{margin-bottom:8px} |
| .msg.assistant .bubble p:last-child{margin-bottom:0} |
|
|
| /* Thinking block */ |
| .think-block{background:var(--surface2);border:1px solid var(--border);border-left:3px solid var(--accent);border-radius:var(--radius-sm);padding:12px 16px;margin-bottom:10px;font-size:13px;color:var(--text2);line-height:1.6} |
| .think-toggle{display:flex;align-items:center;gap:6px;cursor:pointer;font-size:12px;font-weight:600;color:var(--accent2);margin-bottom:6px;user-select:none} |
| .think-toggle svg{transition:transform .2s} |
| .think-toggle.collapsed svg{transform:rotate(-90deg)} |
| .think-content{overflow:hidden;transition:max-height .3s} |
| .think-content.hidden{max-height:0!important;margin:0;padding:0} |
|
|
| /* Code blocks */ |
| .bubble pre{background:var(--surface2);border:1px solid var(--border);border-radius:var(--radius-sm);padding:14px 16px;overflow-x:auto;margin:8px 0;font-size:13px;line-height:1.5} |
| .bubble code{font-family:'JetBrains Mono',monospace;font-size:13px} |
| .bubble :not(pre)>code{background:var(--surface2);padding:2px 6px;border-radius:4px;font-size:12px;border:1px solid var(--border)} |
|
|
| /* ── Typing Indicator ── */ |
| .typing{display:flex;gap:4px;padding:8px 0} |
| .typing span{width:6px;height:6px;border-radius:50%;background:var(--accent2);animation:bounce .6s infinite alternate} |
| .typing span:nth-child(2){animation-delay:.15s} |
| .typing span:nth-child(3){animation-delay:.3s} |
|
|
| /* ── Input Area ── */ |
| .input-area{padding:16px 24px 20px;border-top:1px solid var(--border);background:rgba(18,18,26,.8);backdrop-filter:blur(20px)} |
| .input-wrap{max-width:800px;margin:0 auto;display:flex;gap:10px;align-items:flex-end} |
| .input-box{flex:1;position:relative} |
| #userInput{width:100%;resize:none;border:1px solid var(--border);border-radius:var(--radius);background:var(--surface);color:var(--text);padding:14px 18px;font-size:14px;font-family:'Inter',sans-serif;line-height:1.5;outline:none;min-height:52px;max-height:200px;transition:border-color .2s} |
| #userInput:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-glow)} |
| #userInput::placeholder{color:var(--text3)} |
| #sendBtn{width:48px;height:48px;border-radius:var(--radius-sm);border:none;background:var(--user-bg);color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s;flex-shrink:0} |
| #sendBtn:hover{transform:scale(1.05);box-shadow:0 4px 16px rgba(139,92,246,.4)} |
| #sendBtn:disabled{opacity:.4;cursor:not-allowed;transform:none;box-shadow:none} |
| .input-hint{text-align:center;font-size:11px;color:var(--text3);margin-top:8px} |
|
|
| /* ── Settings Panel ── */ |
| .settings-panel{display:none;position:absolute;top:60px;right:24px;width:320px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:20px;z-index:100;box-shadow:0 16px 48px rgba(0,0,0,.5);animation:fadeUp .2s ease} |
| .settings-panel.show{display:block} |
| .settings-panel h3{font-size:14px;font-weight:600;margin-bottom:16px;color:var(--text)} |
| .setting-row{margin-bottom:14px} |
| .setting-row label{display:flex;justify-content:space-between;font-size:12px;color:var(--text2);margin-bottom:6px;font-weight:500} |
| .setting-row input[type=range]{width:100%;accent-color:var(--accent);height:4px;-webkit-appearance:none;background:var(--surface3);border-radius:2px;outline:none} |
| .setting-row input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:16px;height:16px;border-radius:50%;background:var(--accent);cursor:pointer;border:2px solid var(--surface)} |
| .setting-row textarea{width:100%;min-height:80px;border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--surface2);color:var(--text);padding:10px;font-size:12px;font-family:'Inter',sans-serif;resize:vertical;outline:none} |
| .setting-row textarea:focus{border-color:var(--accent)} |
|
|
| /* ── Mobile ── */ |
| .menu-btn{display:none;position:fixed;top:12px;left:12px;z-index:200} |
| @media(max-width:768px){ |
| .sidebar{position:fixed;left:0;top:0;bottom:0;z-index:150;transform:translateX(-100%)} |
| .sidebar.open{transform:translateX(0)} |
| .menu-btn{display:flex} |
| .suggestions{grid-template-columns:1fr} |
| .bubble{max-width:90%} |
| } |
|
|
| /* ── Animations ── */ |
| @keyframes fadeUp{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}} |
| @keyframes bounce{to{transform:translateY(-6px);opacity:.4}} |
| @keyframes pulse{0%,100%{opacity:1}50%{opacity:.5}} |
|
|
| /* Scrollbar */ |
| ::-webkit-scrollbar{width:6px} |
| ::-webkit-scrollbar-track{background:transparent} |
| ::-webkit-scrollbar-thumb{background:var(--surface3);border-radius:3px} |
| ::-webkit-scrollbar-thumb:hover{background:var(--border)} |
| </style> |
| </head> |
| <body> |
| <div class="app"> |
| |
| <aside class="sidebar" id="sidebar"> |
| <div class="sidebar-header"> |
| <div class="logo">Z</div> |
| <div class="brand"><h1>ZAYA1-8B</h1><span>by Zyphra</span></div> |
| </div> |
| <div class="sidebar-body"> |
| <button class="new-chat-btn" onclick="newChat()">+ New Chat</button> |
| <div id="chatList"></div> |
| </div> |
| <div class="sidebar-footer"> |
| <div class="model-badge"><span class="dot"></span> ZAYA1-8B · 760M active · MoE</div> |
| </div> |
| </aside> |
|
|
| |
| <div class="main"> |
| <button class="icon-btn menu-btn" onclick="document.getElementById('sidebar').classList.toggle('open')">☰</button> |
| <div class="chat-header"> |
| <h2 id="chatTitle">New Chat</h2> |
| <div class="header-controls"> |
| <button class="icon-btn" id="settingsBtn" onclick="toggleSettings()" title="Settings">⚙</button> |
| </div> |
| <div class="settings-panel" id="settingsPanel"> |
| <h3>Generation Settings</h3> |
| <div class="setting-row"><label>Temperature <span id="tempVal">1.0</span></label><input type="range" id="temperature" min="0.1" max="2" step="0.05" value="1.0" oninput="document.getElementById('tempVal').textContent=this.value"></div> |
| <div class="setting-row"><label>Top-P <span id="topPVal">0.95</span></label><input type="range" id="topP" min="0.1" max="1" step="0.05" value="0.95" oninput="document.getElementById('topPVal').textContent=this.value"></div> |
| <div class="setting-row"><label>Max Tokens <span id="maxTVal">2048</span></label><input type="range" id="maxTokens" min="256" max="4096" step="256" value="2048" oninput="document.getElementById('maxTVal').textContent=this.value"></div> |
| <div class="setting-row"><label>System Prompt</label><textarea id="systemPrompt">You are ZAYA1-8B, a highly capable reasoning assistant built by Zyphra. You excel at detailed long-form reasoning, mathematics, and coding. Think step by step when solving complex problems.</textarea></div> |
| </div> |
| </div> |
|
|
| <div class="messages" id="messages"> |
| <div class="welcome" id="welcome"> |
| <div class="welcome-icon">Z</div> |
| <h2>ZAYA1-8B</h2> |
| <p>A highly efficient reasoning model with 760M active parameters. Excels at mathematics, coding, and complex reasoning tasks.</p> |
| <div class="suggestions"> |
| <button class="suggestion" onclick="useSuggestion(this)"><strong>🧮 Math</strong>Prove that √2 is irrational using proof by contradiction.</button> |
| <button class="suggestion" onclick="useSuggestion(this)"><strong>💻 Coding</strong>Write a Python function for merge sort with detailed comments.</button> |
| <button class="suggestion" onclick="useSuggestion(this)"><strong>🧠 Reasoning</strong>Explain the Monty Hall problem and why switching is optimal.</button> |
| <button class="suggestion" onclick="useSuggestion(this)"><strong>📐 Logic</strong>Solve: If all A are B, and some B are C, what can we conclude?</button> |
| </div> |
| </div> |
| </div> |
|
|
| <div class="input-area"> |
| <div class="input-wrap"> |
| <div class="input-box"><textarea id="userInput" placeholder="Ask ZAYA1 anything..." rows="1"></textarea></div> |
| <button id="sendBtn" onclick="sendMessage()"> |
| <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 2L11 13"/><path d="M22 2L15 22L11 13L2 9L22 2Z"/></svg> |
| </button> |
| </div> |
| <div class="input-hint">ZAYA1-8B can make mistakes. Verify important information.</div> |
| </div> |
| </div> |
| </div> |
|
|
| <script type="module"> |
| import { Client } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js"; |
| |
| |
| let gradioClient = null; |
| async function getClient() { |
| if (!gradioClient) { |
| gradioClient = await Client.connect(window.location.origin); |
| } |
| return gradioClient; |
| } |
| |
| let conversations = []; |
| let currentConv = null; |
| let isGenerating = false; |
| let currentJob = null; |
| |
| |
| function init() { |
| newChat(); |
| document.getElementById('userInput').addEventListener('keydown', e => { |
| if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } |
| }); |
| document.getElementById('userInput').addEventListener('input', autoResize); |
| document.addEventListener('click', e => { |
| const sp = document.getElementById('settingsPanel'); |
| if (sp.classList.contains('show') && !sp.contains(e.target) && e.target.id !== 'settingsBtn') sp.classList.remove('show'); |
| }); |
| } |
| |
| function autoResize() { |
| const ta = document.getElementById('userInput'); |
| ta.style.height = 'auto'; |
| ta.style.height = Math.min(ta.scrollHeight, 200) + 'px'; |
| } |
| |
| |
| function newChat() { |
| if (isGenerating && currentJob) { |
| try { currentJob.destroy(); } catch(e) {} |
| } |
| isGenerating = false; |
| currentJob = null; |
| document.getElementById('sendBtn').disabled = false; |
| updateSendBtnIcon(false); |
| |
| const conv = { id: Date.now(), title: 'New Chat', messages: [] }; |
| conversations.unshift(conv); |
| currentConv = conv; |
| renderChatList(); |
| renderMessages(); |
| document.getElementById('chatTitle').textContent = 'New Chat'; |
| } |
| |
| function switchChat(id) { |
| if (isGenerating && currentJob) { |
| try { currentJob.destroy(); } catch(e) {} |
| } |
| isGenerating = false; |
| currentJob = null; |
| document.getElementById('sendBtn').disabled = false; |
| updateSendBtnIcon(false); |
| |
| currentConv = conversations.find(c => c.id === id); |
| renderChatList(); |
| renderMessages(); |
| document.getElementById('chatTitle').textContent = currentConv.title; |
| document.getElementById('sidebar').classList.remove('open'); |
| } |
| |
| function renderChatList() { |
| const el = document.getElementById('chatList'); |
| el.innerHTML = conversations.map(c => |
| `<div class="chat-item ${c.id === currentConv?.id ? 'active' : ''}" onclick="switchChat(${c.id})">${c.title}</div>` |
| ).join(''); |
| } |
| |
| |
| function renderMessages() { |
| const el = document.getElementById('messages'); |
| if (!currentConv || currentConv.messages.length === 0) { |
| el.innerHTML = `<div class="welcome" id="welcome"> |
| <div class="welcome-icon">Z</div><h2>ZAYA1-8B</h2> |
| <p>A highly efficient reasoning model with 760M active parameters. Excels at mathematics, coding, and complex reasoning tasks.</p> |
| <div class="suggestions"> |
| <button class="suggestion" onclick="useSuggestion(this)"><strong>🧮 Math</strong>Prove that √2 is irrational using proof by contradiction.</button> |
| <button class="suggestion" onclick="useSuggestion(this)"><strong>💻 Coding</strong>Write a Python function for merge sort with detailed comments.</button> |
| <button class="suggestion" onclick="useSuggestion(this)"><strong>🧠 Reasoning</strong>Explain the Monty Hall problem and why switching is optimal.</button> |
| <button class="suggestion" onclick="useSuggestion(this)"><strong>📐 Logic</strong>Solve: If all A are B, and some B are C, what can we conclude?</button> |
| </div></div>`; |
| return; |
| } |
| el.innerHTML = currentConv.messages.map(m => msgHTML(m.role, m.content)).join(''); |
| el.scrollTop = el.scrollHeight; |
| } |
| |
| function msgHTML(role, content) { |
| const avatar = role === 'user' ? 'U' : 'Z'; |
| const rendered = role === 'assistant' ? formatResponse(content) : escapeHTML(content); |
| return `<div class="msg ${role}"><div class="avatar">${avatar}</div><div class="bubble">${rendered}</div></div>`; |
| } |
| |
| |
| function formatResponse(text) { |
| let html = text; |
| |
| html = html.replace(/<think>([\s\S]*?)<\/think>/g, (_, inner) => { |
| const id = 'think_' + Math.random().toString(36).substr(2, 6); |
| return `<div class="think-block"><div class="think-toggle" onclick="toggleThink('${id}')"> |
| <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6"/></svg> |
| Reasoning</div><div class="think-content" id="${id}">${escapeHTML(inner.trim())}</div></div>`; |
| }); |
| |
| html = html.replace(/```(\w*)\n?([\s\S]*?)```/g, (_, lang, code) => |
| `<pre><code class="language-${lang}">${escapeHTML(code.trim())}</code></pre>`); |
| |
| html = html.replace(/`([^`]+)`/g, '<code>$1</code>'); |
| |
| html = html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>'); |
| |
| html = html.replace(/\*(.+?)\*/g, '<em>$1</em>'); |
| |
| html = html.replace(/\n/g, '<br>'); |
| return html; |
| } |
| |
| function escapeHTML(s) { |
| const d = document.createElement('div'); d.textContent = s; return d.innerHTML; |
| } |
| |
| |
| |
| async function sendMessage() { |
| if (isGenerating) { |
| if (currentJob) { |
| try { currentJob.destroy(); } catch(e) {} |
| } |
| isGenerating = false; |
| currentJob = null; |
| document.getElementById('sendBtn').disabled = false; |
| updateSendBtnIcon(false); |
| return; |
| } |
| |
| const input = document.getElementById('userInput'); |
| const text = input.value.trim(); |
| if (!text) return; |
| |
| |
| const welcome = document.getElementById('welcome'); |
| if (welcome) welcome.remove(); |
| |
| |
| currentConv.messages.push({ role: 'user', content: text }); |
| if (currentConv.messages.length === 1) { |
| currentConv.title = text.slice(0, 40) + (text.length > 40 ? '…' : ''); |
| document.getElementById('chatTitle').textContent = currentConv.title; |
| renderChatList(); |
| } |
| |
| const messagesEl = document.getElementById('messages'); |
| messagesEl.innerHTML += msgHTML('user', text); |
| |
| input.value = ''; |
| input.style.height = 'auto'; |
| isGenerating = true; |
| updateSendBtnIcon(true); |
| |
| |
| const assistantDiv = document.createElement('div'); |
| assistantDiv.className = 'msg assistant'; |
| assistantDiv.innerHTML = `<div class="avatar">Z</div><div class="bubble"><div class="typing"><span></span><span></span><span></span></div></div>`; |
| messagesEl.appendChild(assistantDiv); |
| messagesEl.scrollTop = messagesEl.scrollHeight; |
| |
| const bubble = assistantDiv.querySelector('.bubble'); |
| let fullText = ''; |
| |
| try { |
| const client = await getClient(); |
| const history = currentConv.messages.slice(0, -1); |
| |
| currentJob = client.submit("/generate", { |
| message: text, |
| history: JSON.stringify(history), |
| system_prompt: document.getElementById('systemPrompt').value, |
| temperature: parseFloat(document.getElementById('temperature').value), |
| top_p: parseFloat(document.getElementById('topP').value), |
| max_new_tokens: parseInt(document.getElementById('maxTokens').value), |
| }); |
| |
| await new Promise((resolve, reject) => { |
| currentJob.on("data", (event) => { |
| if (event.data && event.data[0]) { |
| fullText = event.data[0]; |
| bubble.innerHTML = formatResponse(fullText); |
| messagesEl.scrollTop = messagesEl.scrollHeight; |
| } |
| }); |
| currentJob.on("status", (status) => { |
| if (status.stage === "complete") resolve(); |
| if (status.stage === "error") reject(new Error(status.message || "Generation failed")); |
| }); |
| }); |
| } catch (err) { |
| console.error("Generation error:", err); |
| if (!fullText) { |
| fullText = 'Sorry, an error occurred. Please try again.'; |
| bubble.innerHTML = `<span style="color:var(--red)">${escapeHTML(err.message || fullText)}</span>`; |
| } |
| } finally { |
| isGenerating = false; |
| currentJob = null; |
| document.getElementById('sendBtn').disabled = false; |
| updateSendBtnIcon(false); |
| if (fullText) { |
| currentConv.messages.push({ role: 'assistant', content: fullText }); |
| } |
| document.getElementById('userInput').focus(); |
| } |
| } |
| |
| function updateSendBtnIcon(loading) { |
| const btn = document.getElementById('sendBtn'); |
| if (loading) { |
| btn.innerHTML = `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="6" y="6" width="12" height="12"/></svg>`; |
| btn.title = "Stop generating"; |
| } else { |
| btn.innerHTML = `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 2L11 13"/><path d="M22 2L15 22L11 13L2 9L22 2Z"/></svg>`; |
| btn.title = "Send message"; |
| } |
| } |
| |
| |
| window.newChat = newChat; |
| window.switchChat = switchChat; |
| window.sendMessage = sendMessage; |
| window.toggleSettings = () => document.getElementById('settingsPanel').classList.toggle('show'); |
| window.toggleThink = (id) => { |
| const el = document.getElementById(id); |
| el.classList.toggle('hidden'); |
| el.previousElementSibling.classList.toggle('collapsed'); |
| }; |
| window.useSuggestion = (btn) => { |
| const text = btn.textContent.replace(btn.querySelector('strong').textContent, '').trim(); |
| document.getElementById('userInput').value = text; |
| sendMessage(); |
| }; |
| |
| init(); |
| </script> |
| </body> |
| </html> |
|
|