Download index.html from Radmana/NovaAI: direct link, hf CLI and curl.
- Browser
- Download file 14.7 kB
-
https://huggingface.co/spaces/Radmana/NovaAI/resolve/main/index.html
- Command line
-
hf download hf://spaces/Radmana/NovaAI/index.html
-
curl -L -o index.html https://huggingface.co/spaces/Radmana/NovaAI/resolve/main/index.html
14.7 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Nova AI</title> | |
| <style> | |
| * { | |
| box-sizing: border-box; | |
| } | |
| body { | |
| margin: 0; | |
| font-family: Arial, Helvetica, sans-serif; | |
| background: #0f1117; | |
| color: #ffffff; | |
| height: 100vh; | |
| overflow: hidden; | |
| } | |
| .app { | |
| display: flex; | |
| height: 100vh; | |
| } | |
| .sidebar { | |
| width: 260px; | |
| background: #171a21; | |
| border-right: 1px solid #292d36; | |
| padding: 18px; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| .logo { | |
| font-size: 21px; | |
| font-weight: bold; | |
| margin-bottom: 25px; | |
| } | |
| .new-chat { | |
| border: 1px solid #363b46; | |
| background: #222630; | |
| color: white; | |
| padding: 12px; | |
| border-radius: 9px; | |
| cursor: pointer; | |
| font-size: 14px; | |
| } | |
| .new-chat:hover { | |
| background: #2b303c; | |
| } | |
| .sidebar-bottom { | |
| margin-top: auto; | |
| color: #858b98; | |
| font-size: 12px; | |
| line-height: 1.5; | |
| } | |
| .main { | |
| flex: 1; | |
| display: flex; | |
| flex-direction: column; | |
| min-width: 0; | |
| } | |
| .topbar { | |
| height: 60px; | |
| border-bottom: 1px solid #292d36; | |
| display: flex; | |
| align-items: center; | |
| padding: 0 22px; | |
| font-weight: bold; | |
| } | |
| .chat { | |
| flex: 1; | |
| overflow-y: auto; | |
| padding: 30px 15%; | |
| } | |
| .welcome { | |
| text-align: center; | |
| margin-top: 15vh; | |
| } | |
| .welcome h1 { | |
| font-size: 34px; | |
| margin-bottom: 10px; | |
| } | |
| .welcome p { | |
| color: #9ba1ad; | |
| } | |
| .message { | |
| display: flex; | |
| margin-bottom: 25px; | |
| gap: 13px; | |
| line-height: 1.6; | |
| } | |
| .avatar { | |
| width: 34px; | |
| height: 34px; | |
| min-width: 34px; | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| background: #303642; | |
| font-size: 15px; | |
| } | |
| .user .avatar { | |
| background: #555b68; | |
| } | |
| .content { | |
| max-width: 850px; | |
| word-break: break-word; | |
| } | |
| .name { | |
| font-size: 13px; | |
| color: #9ba1ad; | |
| margin-bottom: 3px; | |
| } | |
| .content strong { | |
| font-weight: bold; | |
| } | |
| .content code { | |
| background: #20242d; | |
| padding: 2px 5px; | |
| border-radius: 5px; | |
| font-family: Consolas, monospace; | |
| } | |
| .content pre { | |
| background: #171a21; | |
| border: 1px solid #303540; | |
| padding: 12px; | |
| border-radius: 8px; | |
| overflow-x: auto; | |
| } | |
| .input-area { | |
| padding: 15px 15%; | |
| border-top: 1px solid #292d36; | |
| background: #0f1117; | |
| } | |
| .input-box { | |
| display: flex; | |
| align-items: flex-end; | |
| gap: 8px; | |
| background: #1d2129; | |
| border: 1px solid #343944; | |
| border-radius: 13px; | |
| padding: 9px; | |
| } | |
| textarea { | |
| flex: 1; | |
| resize: none; | |
| background: transparent; | |
| border: none; | |
| outline: none; | |
| color: white; | |
| font-size: 15px; | |
| padding: 8px; | |
| min-height: 25px; | |
| max-height: 150px; | |
| font-family: Arial, Helvetica, sans-serif; | |
| } | |
| textarea::placeholder { | |
| color: #777d89; | |
| } | |
| #send { | |
| width: 40px; | |
| height: 40px; | |
| border: none; | |
| border-radius: 9px; | |
| background: #ffffff; | |
| color: #111111; | |
| cursor: pointer; | |
| font-size: 18px; | |
| } | |
| #send:disabled { | |
| opacity: 0.4; | |
| cursor: default; | |
| } | |
| .token-panel { | |
| position: fixed; | |
| inset: 0; | |
| background: rgba(0,0,0,.75); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| z-index: 10; | |
| } | |
| .token-card { | |
| width: min(450px, 90%); | |
| background: #1b1f27; | |
| border: 1px solid #343944; | |
| border-radius: 14px; | |
| padding: 25px; | |
| } | |
| .token-card h2 { | |
| margin-top: 0; | |
| } | |
| .token-card p { | |
| color: #a4a9b4; | |
| font-size: 14px; | |
| line-height: 1.5; | |
| } | |
| .token-input { | |
| width: 100%; | |
| padding: 12px; | |
| border-radius: 8px; | |
| border: 1px solid #3a3f4b; | |
| background: #10131a; | |
| color: white; | |
| margin: 10px 0; | |
| outline: none; | |
| } | |
| .token-button { | |
| width: 100%; | |
| padding: 12px; | |
| border: none; | |
| border-radius: 8px; | |
| background: white; | |
| color: black; | |
| cursor: pointer; | |
| font-weight: bold; | |
| } | |
| .status { | |
| color: #888f9b; | |
| font-size: 12px; | |
| margin-top: 8px; | |
| text-align: center; | |
| } | |
| @media (max-width: 700px) { | |
| .sidebar { | |
| display: none; | |
| } | |
| .chat, | |
| .input-area { | |
| padding-left: 15px; | |
| padding-right: 15px; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="app"> | |
| <aside class="sidebar"> | |
| <div class="logo"> | |
| Nova AI | |
| </div> | |
| <button class="new-chat" onclick="newChat()"> | |
| + New chat | |
| </button> | |
| <div class="sidebar-bottom"> | |
| Powered by Hugging Face<br> | |
| AI responses may contain mistakes. | |
| </div> | |
| </aside> | |
| <main class="main"> | |
| <div class="topbar"> | |
| Nova AI | |
| </div> | |
| <div class="chat" id="chat"> | |
| <div class="welcome" id="welcome"> | |
| <h1>How can I help?</h1> | |
| <p>Ask me anything.</p> | |
| </div> | |
| </div> | |
| <div class="input-area"> | |
| <div class="input-box"> | |
| <textarea | |
| id="message" | |
| placeholder="Message Nova AI..." | |
| rows="1" | |
| onkeydown="handleKey(event)" | |
| ></textarea> | |
| <button id="send" onclick="sendMessage()">➤</button> | |
| </div> | |
| <div class="status" id="status"> | |
| Ready | |
| </div> | |
| </div> | |
| </main> | |
| </div> | |
| <div class="token-panel" id="tokenPanel"> | |
| <div class="token-card"> | |
| <h2>Connect Nova AI</h2> | |
| <p> | |
| Enter your Hugging Face access token to connect | |
| this chatbot to an AI model. | |
| </p> | |
| <input | |
| class="token-input" | |
| id="token" | |
| type="password" | |
| placeholder="hf_..." | |
| > | |
| <button class="token-button" onclick="saveToken()"> | |
| Connect | |
| </button> | |
| <p style="font-size:12px;"> | |
| Your token is kept in this browser and is not written | |
| into the website code. | |
| </p> | |
| </div> | |
| </div> | |
| <script type="module"> | |
| import { InferenceClient } | |
| from "https://esm.sh/@huggingface/inference"; | |
| let token = sessionStorage.getItem("nova_hf_token"); | |
| const MODEL = "openai/gpt-oss-120b:fastest"; | |
| const SYSTEM_PROMPT = | |
| "You are Nova AI, a helpful, friendly and intelligent AI assistant. " + | |
| "Give clear and accurate answers. " + | |
| "Use normal text for simple math whenever possible. " + | |
| "Do not use LaTeX commands such as \\\\(, \\\\), \\\\[ or \\\\]. " + | |
| "Do not use commands like \\\\times, \\\\frac, \\\\sqrt or \\\\boxed. " + | |
| "Instead write math in plain text, for example 8 × 8 = 64. " + | |
| "Do not add your own name before answers. " + | |
| "Do not claim to be ChatGPT or another specific AI."; | |
| let messages = [ | |
| { | |
| role: "system", | |
| content: SYSTEM_PROMPT | |
| } | |
| ]; | |
| const tokenPanel = | |
| document.getElementById("tokenPanel"); | |
| if (token) { | |
| tokenPanel.style.display = "none"; | |
| } | |
| /* SAVE TOKEN */ | |
| window.saveToken = function() { | |
| const value = | |
| document | |
| .getElementById("token") | |
| .value | |
| .trim(); | |
| if (!value) { | |
| alert("Enter your Hugging Face token."); | |
| return; | |
| } | |
| token = value; | |
| sessionStorage.setItem( | |
| "nova_hf_token", | |
| token | |
| ); | |
| tokenPanel.style.display = "none"; | |
| document.getElementById("status").textContent = | |
| "Connected"; | |
| }; | |
| /* CLEAN MATH */ | |
| function cleanMath(text) { | |
| return text | |
| // Remove LaTeX brackets | |
| .replace(/\\\(/g, "") | |
| .replace(/\\\)/g, "") | |
| .replace(/\\\[/g, "") | |
| .replace(/\\\]/g, "") | |
| // Common LaTeX commands | |
| .replace(/\\times/g, "×") | |
| .replace(/\\cdot/g, "·") | |
| .replace(/\\div/g, "÷") | |
| .replace(/\\pm/g, "±") | |
| .replace(/\\leq/g, "≤") | |
| .replace(/\\geq/g, "≥") | |
| .replace(/\\neq/g, "≠") | |
| .replace(/\\sqrt\{([^}]+)\}/g, "√($1)") | |
| // Fractions | |
| .replace( | |
| /\\frac\{([^}]+)\}\{([^}]+)\}/g, | |
| "($1/$2)" | |
| ) | |
| // Remove boxed | |
| .replace( | |
| /\\boxed\{([^}]+)\}/g, | |
| "$1" | |
| ) | |
| // Remove remaining common commands | |
| .replace(/\\text\{([^}]+)\}/g, "$1") | |
| .replace(/\\mathrm\{([^}]+)\}/g, "$1") | |
| // Remove stray backslashes | |
| .replace(/\\([a-zA-Z]+)/g, "$1") | |
| .trim(); | |
| } | |
| /* SIMPLE SAFE MARKDOWN */ | |
| function formatText(text) { | |
| text = cleanMath(text); | |
| // Escape HTML first | |
| text = text | |
| .replace(/&/g, "&") | |
| .replace(/</g, "<") | |
| .replace(/>/g, ">"); | |
| // Code blocks | |
| text = text.replace( | |
| /```([\s\S]*?)```/g, | |
| "<pre><code>$1</code></pre>" | |
| ); | |
| // Inline code | |
| text = text.replace( | |
| /`([^`]+)`/g, | |
| "<code>$1</code>" | |
| ); | |
| // Bold | |
| text = text.replace( | |
| /\*\*(.*?)\*\*/g, | |
| "<strong>$1</strong>" | |
| ); | |
| // Italic | |
| text = text.replace( | |
| /(?<!\*)\*([^*]+)\*(?!\*)/g, | |
| "<em>$1</em>" | |
| ); | |
| // Line breaks | |
| text = text.replace(/\n/g, "<br>"); | |
| return text; | |
| } | |
| /* SEND MESSAGE */ | |
| window.sendMessage = async function() { | |
| const input = | |
| document.getElementById("message"); | |
| const text = | |
| input.value.trim(); | |
| if (!text || !token) { | |
| return; | |
| } | |
| document.getElementById("welcome").style.display = | |
| "none"; | |
| addMessage("user", text); | |
| input.value = ""; | |
| messages.push({ | |
| role: "user", | |
| content: text | |
| }); | |
| const sendButton = | |
| document.getElementById("send"); | |
| sendButton.disabled = true; | |
| document.getElementById("status").textContent = | |
| "Thinking..."; | |
| try { | |
| const client = | |
| new InferenceClient(token); | |
| const result = | |
| await client.chatCompletion({ | |
| model: MODEL, | |
| messages: messages, | |
| max_tokens: 1000, | |
| temperature: 0.7 | |
| }); | |
| let answer = | |
| result.choices?.[0]?.message?.content || | |
| "I couldn't generate a response."; | |
| // Remove accidental Nova AI prefixes | |
| answer = answer | |
| .replace(/^✦\s*Nova AI\s*/i, "") | |
| .replace(/^Nova AI\s*:\s*/i, "") | |
| .trim(); | |
| messages.push({ | |
| role: "assistant", | |
| content: answer | |
| }); | |
| addMessage( | |
| "assistant", | |
| answer | |
| ); | |
| document.getElementById("status").textContent = | |
| "Ready"; | |
| } catch (error) { | |
| console.error(error); | |
| addMessage( | |
| "assistant", | |
| "⚠️ I couldn't connect to the AI.\n\n" + | |
| "Check your Hugging Face token and make sure " + | |
| "the selected model has an available inference provider." | |
| ); | |
| document.getElementById("status").textContent = | |
| "Connection error"; | |
| } finally { | |
| sendButton.disabled = false; | |
| } | |
| }; | |
| /* ADD MESSAGE */ | |
| function addMessage(role, text) { | |
| const chat = | |
| document.getElementById("chat"); | |
| const message = | |
| document.createElement("div"); | |
| message.className = | |
| "message " + role; | |
| const avatar = | |
| document.createElement("div"); | |
| avatar.className = | |
| "avatar"; | |
| // Simple clean avatars | |
| avatar.textContent = | |
| role === "user" ? "U" : "N"; | |
| const wrapper = | |
| document.createElement("div"); | |
| const name = | |
| document.createElement("div"); | |
| name.className = | |
| "name"; | |
| name.textContent = | |
| role === "user" | |
| ? "You" | |
| : "Nova AI"; | |
| const content = | |
| document.createElement("div"); | |
| content.className = | |
| "content"; | |
| if (role === "assistant") { | |
| content.innerHTML = | |
| formatText(text); | |
| } else { | |
| // User messages stay completely safe | |
| content.textContent = | |
| text; | |
| } | |
| wrapper.appendChild(name); | |
| wrapper.appendChild(content); | |
| message.appendChild(avatar); | |
| message.appendChild(wrapper); | |
| chat.appendChild(message); | |
| chat.scrollTop = | |
| chat.scrollHeight; | |
| } | |
| /* NEW CHAT */ | |
| window.newChat = function() { | |
| messages = [ | |
| { | |
| role: "system", | |
| content: SYSTEM_PROMPT | |
| } | |
| ]; | |
| const chat = | |
| document.getElementById("chat"); | |
| chat.innerHTML = ` | |
| <div class="welcome" id="welcome"> | |
| <h1>How can I help?</h1> | |
| <p>Ask me anything.</p> | |
| </div> | |
| `; | |
| document.getElementById("status").textContent = | |
| "Ready"; | |
| }; | |
| /* ENTER TO SEND */ | |
| window.handleKey = function(event) { | |
| if ( | |
| event.key === "Enter" && | |
| !event.shiftKey | |
| ) { | |
| event.preventDefault(); | |
| sendMessage(); | |
| } | |
| }; | |
| </script> | |
| </body> | |
| </html> |