Download index.html from JustScriptzz/nexus-plus-v2-chat: direct link, hf CLI and curl.
- Browser
- Download file 8.63 kB
-
https://huggingface.co/spaces/JustScriptzz/nexus-plus-v2-chat/resolve/main/index.html
- Command line
-
hf download hf://spaces/JustScriptzz/nexus-plus-v2-chat/index.html
-
curl -L -o index.html https://huggingface.co/spaces/JustScriptzz/nexus-plus-v2-chat/resolve/main/index.html
8.63 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Nexus Plus v2</title> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap'); | |
| * { font-family: 'Inter', sans-serif; box-sizing: border-box; } | |
| body { background: #0a0a0f; color: #e1e1e6; margin: 0; min-height: 100vh; display: flex; flex-direction: column; align-items: center; } | |
| .container { max-width: 680px; width: 100%; padding: 16px; display: flex; flex-direction: column; height: 100vh; } | |
| .header { text-align: center; padding: 16px 0 12px; flex-shrink: 0; } | |
| .header h1 { font-size: 28px; font-weight: 700; margin: 0; } | |
| .header h1 span { color: #a78bfa; } | |
| .header p { color: #666; font-size: 13px; margin-top: 4px; } | |
| .status { display: flex; align-items: center; gap: 8px; padding: 6px 14px; background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.06); border-radius: 20px; font-size: 12px; color: #888; width: fit-content; margin: 0 auto 12px; } | |
| .dot { width: 6px; height: 6px; border-radius: 50%; background: #22c55e; flex-shrink: 0; } | |
| .dot.loading { background: #eab308; animation: pulse 1s infinite; } | |
| .dot.error { background: #ef4444; } | |
| @keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.3} } | |
| .chat-box { flex: 1; overflow-y: auto; padding: 8px 0; } | |
| .msg { padding: 12px 16px; margin: 8px 0; border-radius: 16px; max-width: 85%; line-height: 1.6; font-size: 15px; white-space: pre-wrap; word-wrap: break-word; } | |
| .msg-user { background: linear-gradient(135deg, #7c3aed22, #6d28d922); border: 1px solid #7c3aed33; margin-left: auto; } | |
| .msg-bot { background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.06); } | |
| .typing { display: inline-flex; gap: 4px; padding: 8px 14px; background: rgba(255,255,255,0.04); border-radius: 12px; margin: 8px 0; } | |
| .typing i { width: 7px; height: 7px; background: #7c3aed; border-radius: 50%; animation: bounce 1.4s infinite; display: inline-block; } | |
| .typing i:nth-child(2) { animation-delay: 0.16s; } | |
| .typing i:nth-child(3) { animation-delay: 0.32s; } | |
| @keyframes bounce { 0%,60%,100%{transform:translateY(0);opacity:0.4} 30%{transform:translateY(-6px);opacity:1} } | |
| .input-row { display: flex; gap: 8px; padding: 12px 0; flex-shrink: 0; } | |
| .input-row input { flex: 1; background: #111118; border: 1px solid rgba(255,255,255,0.1); color: #fff; border-radius: 16px; padding: 12px 16px; font-size: 15px; outline: none; } | |
| .input-row input:focus { box-shadow: 0 0 0 2px #7c3aed44; } | |
| .input-row button { background: #7c3aed; color: #fff; border: none; border-radius: 16px; padding: 12px 24px; font-weight: 600; cursor: pointer; flex-shrink: 0; } | |
| .input-row button:hover { background: #6d28d9; } | |
| .input-row button:disabled { opacity: 0.5; cursor: not-allowed; } | |
| .welcome { background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.06); border-radius: 16px; padding: 16px; margin: 8px 0; text-align: center; color: #888; font-size: 14px; } | |
| .links { text-align: center; padding: 8px 0; flex-shrink: 0; } | |
| .links a { color: #555; font-size: 12px; text-decoration: none; } | |
| .links a:hover { color: #888; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <div class="header"> | |
| <h1>Nexus <span>Plus</span> <span style="color:#555;font-size:18px;font-weight:400">v2</span></h1> | |
| <p>Qwen3-4B fine-tuned with QLoRA</p> | |
| </div> | |
| <div id="tokenBar" style="display:flex;gap:8px;margin-bottom:8px;flex-shrink:0;"> | |
| <input type="password" id="tokenInput" placeholder="HF token (hf_...)" style="flex:1;background:#111118;border:1px solid rgba(255,255,255,0.1);color:#fff;border-radius:16px;padding:10px 16px;font-size:13px;outline:none;"> | |
| <button onclick="saveToken()" style="background:#7c3aed;color:#fff;border:none;border-radius:16px;padding:10px 16px;font-size:13px;cursor:pointer;">Save</button> | |
| </div> | |
| <div class="status"><div class="dot" id="statusDot"></div><span id="statusText">Ready</span></div> | |
| <div class="chat-box" id="chatBox"> | |
| <div class="welcome"> | |
| <strong>Hello!</strong> I'm Nexus Plus v2 — powered by Qwen3-4B fine-tuned with QLoRA.<br>Ask me anything. | |
| </div> | |
| </div> | |
| <div class="input-row"> | |
| <input type="text" id="userInput" placeholder="Type a message..." autofocus> | |
| <button id="sendBtn" onclick="sendMessage()">Send</button> | |
| </div> | |
| <div class="links"> | |
| <a href="https://huggingface.co/JustScriptzz/nexus-plus-v2">Model</a> · | |
| <a href="https://github.com/JustScriptzz/nexus-smAll-web">GitHub</a> · | |
| <a href="https://justscriptzz-nexus-smAll-v1-chat.hf.space">SmAll v1</a> | |
| </div> | |
| </div> | |
| <script> | |
| const chatBox = document.getElementById('chatBox'); | |
| const userInput = document.getElementById('userInput'); | |
| const sendBtn = document.getElementById('sendBtn'); | |
| const statusDot = document.getElementById('statusDot'); | |
| const statusText = document.getElementById('statusText'); | |
| const HISTORY = []; | |
| let generating = false; | |
| let HF_TOKEN = localStorage.getItem('hf_token') || ''; | |
| if (HF_TOKEN) { | |
| document.getElementById('tokenInput').value = HF_TOKEN; | |
| document.getElementById('tokenBar').style.display = 'none'; | |
| } | |
| function saveToken() { | |
| HF_TOKEN = document.getElementById('tokenInput').value.trim(); | |
| if (HF_TOKEN) { | |
| localStorage.setItem('hf_token', HF_TOKEN); | |
| document.getElementById('tokenBar').style.display = 'none'; | |
| } | |
| } | |
| function setStatus(state, text) { | |
| statusDot.className = 'dot' + (state === 'loading' ? ' loading' : state === 'error' ? ' error' : ''); | |
| statusText.textContent = text; | |
| } | |
| function addMsg(role, text) { | |
| const div = document.createElement('div'); | |
| div.className = 'msg ' + (role === 'user' ? 'msg-user' : 'msg-bot'); | |
| div.textContent = text; | |
| chatBox.appendChild(div); | |
| chatBox.scrollTop = chatBox.scrollHeight; | |
| return div; | |
| } | |
| function addTyping() { | |
| const div = document.createElement('div'); | |
| div.className = 'typing'; | |
| div.id = 'typing'; | |
| div.innerHTML = '<i></i><i></i><i></i>'; | |
| chatBox.appendChild(div); | |
| chatBox.scrollTop = chatBox.scrollHeight; | |
| } | |
| function removeTyping() { | |
| const t = document.getElementById('typing'); | |
| if (t) t.remove(); | |
| } | |
| async function sendMessage() { | |
| const msg = userInput.value.trim(); | |
| if (!msg || generating) return; | |
| if (!HF_TOKEN) { | |
| document.getElementById('tokenBar').style.display = 'flex'; | |
| document.getElementById('tokenInput').focus(); | |
| return; | |
| } | |
| generating = true; | |
| sendBtn.disabled = true; | |
| userInput.value = ''; | |
| // Remove welcome | |
| const w = chatBox.querySelector('.welcome'); | |
| if (w) w.remove(); | |
| addMsg('user', msg); | |
| HISTORY.push({ role: 'user', content: msg }); | |
| addTyping(); | |
| setStatus('loading', 'Generating...'); | |
| try { | |
| const resp = await fetch('https://router.huggingface.co/hf-inference/models/JustScriptzz/nexus-plus-v2/v1/chat/completions', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'Authorization': 'Bearer ' + HF_TOKEN | |
| }, | |
| body: JSON.stringify({ | |
| model: 'JustScriptzz/nexus-plus-v2', | |
| messages: HISTORY, | |
| max_tokens: 512, | |
| temperature: 0.7, | |
| top_p: 0.8, | |
| stream: true | |
| }) | |
| }); | |
| if (!resp.ok) { | |
| const err = await resp.text(); | |
| throw new Error(`API error ${resp.status}: ${err}`); | |
| } | |
| removeTyping(); | |
| const botDiv = addMsg('bot', ''); | |
| const reader = resp.body.getReader(); | |
| const decoder = new TextDecoder(); | |
| let full = ''; | |
| let buffer = ''; | |
| while (true) { | |
| const { done, value } = await reader.read(); | |
| if (done) break; | |
| buffer += decoder.decode(value, { stream: true }); | |
| const lines = buffer.split('\n'); | |
| buffer = lines.pop(); | |
| for (const line of lines) { | |
| if (line.startsWith('data: ')) { | |
| const data = line.slice(6); | |
| if (data === '[DONE]') continue; | |
| try { | |
| const j = JSON.parse(data); | |
| const delta = j.choices?.[0]?.delta?.content; | |
| if (delta) { | |
| full += delta; | |
| botDiv.textContent = full; | |
| chatBox.scrollTop = chatBox.scrollHeight; | |
| } | |
| } catch(e) {} | |
| } | |
| } | |
| } | |
| HISTORY.push({ role: 'assistant', content: full }); | |
| setStatus('ready', 'Ready'); | |
| } catch (e) { | |
| removeTyping(); | |
| addMsg('bot', 'Error: ' + e.message); | |
| setStatus('error', 'Error — try again'); | |
| } | |
| generating = false; | |
| sendBtn.disabled = false; | |
| userInput.focus(); | |
| } | |
| userInput.addEventListener('keydown', e => { | |
| if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } | |
| }); | |
| </script> | |
| </body> | |
| </html> | |