1<!DOCTYPE html> 2<html lang="en"> 3<head> 4<meta charset="UTF-8" /> 5<meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6<title>Ornith-1.0-9B · Terminal</title> 7<style> 8 :root { 9 --bg: #0a0a0a; 10 --panel: #111111; 11 --panel-2: #171717; 12 --border: #2a2a2a; 13 --text: #e0e0e0; 14 --muted: #6b6b6b; 15 --green: #4ade80; 16 --green-dim: #22c55e; 17 --yellow: #facc15; 18 --red: #f87171; 19 --blue: #60a5fa; 20 --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; 21 } 22 23 * { box-sizing: border-box; } 24 html, body { height: 100%; margin: 0; } 25 body { 26 background: var(--bg); 27 color: var(--text); 28 font-family: var(--mono); 29 font-size: 14px; 30 line-height: 1.6; 31 display: flex; 32 flex-direction: column; 33 } 34 35 header { 36 display: flex; 37 align-items: center; 38 gap: 12px; 39 padding: 10px 16px; 40 background: var(--panel); 41 border-bottom: 1px solid var(--border); 42 } 43 header img { height: 28px; width: auto; } 44 header .title { font-weight: 600; font-size: 13px; color: var(--text); } 45 header .title span { color: var(--muted); font-weight: 400; } 46 header .spacer { flex: 1; } 47 header .badge { 48 font-size: 11px; color: var(--muted); border: 1px solid var(--border); 49 padding: 2px 8px; border-radius: 4px; cursor: pointer; user-select: none; 50 } 51 header .badge:hover { color: var(--text); border-color: var(--muted); } 52 53 #chat { 54 flex: 1; 55 overflow-y: auto; 56 padding: 0 16px; 57 } 58 .turn { max-width: 900px; margin: 0 auto; padding: 10px 0; border-bottom: 1px solid var(--border); } 59 .turn:last-child { border-bottom: none; } 60 61 .prompt-line { display: flex; align-items: flex-start; gap: 10px; } 62 .prompt { color: var(--green); white-space: nowrap; font-weight: 600; user-select: none; } 63 .prompt.user::before { content: "â¯"; } 64 .prompt.assistant::before { content: "â"; color: var(--green-dim); } 65 .content { flex: 1; min-width: 0; } 66 67 .user-input { color: var(--text); white-space: pre-wrap; } 68 69 .thinking { margin: 6px 0 10px; } 70 .thinking summary { 71 cursor: pointer; color: var(--muted); font-size: 12px; list-style: none; 72 display: flex; align-items: center; gap: 6px; 73 } 74 .thinking summary::-webkit-details-marker { display: none; } 75 .thinking summary::before { content: "â¸"; color: var(--green-dim); } 76 .thinking[open] summary::before { content: "â¾"; } 77 .thinking pre { 78 margin: 6px 0 0; padding: 10px 12px; background: var(--panel-2); 79 border-left: 2px solid var(--green-dim); color: var(--muted); 80 font: inherit; white-space: pre-wrap; word-wrap: break-word; 81 max-height: 300px; overflow-y: auto; 82 } 83 84 .answer { color: var(--text); } 85 .answer p { margin: 0 0 8px; } 86 .answer p:last-child { margin-bottom: 0; } 87 .answer pre { 88 background: var(--panel-2); border: 1px solid var(--border); border-radius: 4px; 89 padding: 12px 14px; overflow-x: auto; margin: 10px 0; font: inherit; 90 } 91 .answer code { font-family: inherit; } 92 .answer :not(pre) > code { background: var(--panel-2); padding: 1px 4px; border-radius: 3px; color: var(--yellow); } 93 .answer ul, .answer ol { padding-left: 22px; margin: 6px 0; } 94 .answer a { color: var(--green); } 95 .answer blockquote { margin: 8px 0; padding-left: 12px; border-left: 2px solid var(--muted); color: var(--muted); } 96 .answer h1,.answer h2,.answer h3 { margin: 14px 0 6px; font-size: inherit; color: var(--green); } 97 .answer h1 { font-size: 16px; } 98 .answer h2 { font-size: 15px; } 99 100 .status { max-width: 900px; margin: 0 auto; padding: 4px 0; color: var(--muted); font-size: 12px; min-height: 18px; } 101 .status .err { color: var(--red); } 102 103 .composer { 104 border-top: 1px solid var(--border); background: var(--panel); padding: 12px 16px 16px; 105 } 106 .composer-inner { max-width: 900px; margin: 0 auto; } 107 108 #settings { 109 display: none; gap: 18px; flex-wrap: wrap; margin-bottom: 10px; color: var(--muted); font-size: 12px; 110 } 111 #settings.open { display: flex; } 112 #settings label { display: flex; flex-direction: column; gap: 4px; } 113 #settings input[type=range] { width: 150px; accent-color: var(--green); } 114 115 .input-row { display: flex; align-items: flex-end; gap: 8px; } 116 #text { 117 flex: 1; resize: none; min-height: 42px; max-height: 160px; 118 background: var(--bg); color: var(--text); border: 1px solid var(--border); 119 border-radius: 6px; padding: 10px 12px; font: inherit; outline: none; 120 } 121 #text:focus { border-color: var(--green); } 122 button { 123 background: var(--green); color: #052e16; border: none; border-radius: 6px; 124 padding: 10px 16px; font: 600 13px inherit; cursor: pointer; 125 } 126 button:disabled { opacity: .5;
126 cursor: not-allowed; } 127 #stop { background: var(--panel-2); color: var(--text); border: 1px solid var(--border); display: none; } 128 .icon-btn { background: var(--panel-2); color: var(--text); border: 1px solid var(--border); padding: 10px 12px; font-size: 14px; } 129 130 .hint { color: var(--muted); font-size: 11px; margin-top: 8px; text-align: center; } 131 132 /* Empty state */ 133 .welcome { max-width: 900px; margin: 24px auto; color: var(--muted); } 134 .welcome h2 { color: var(--green); margin: 0 0 8px; font-size: 14px; } 135 .welcome p { margin: 0; } 136 .welcome .cmd { color: var(--yellow); } 137</style> 138</head> 139<body> 140 141<header> 142 <img src="https://us.aws.cdn.hf.co/xet-bridge-us/6a37681bb57803e616606089/2430ce5755ccaa545def0292cb0f1068d4af99d5093c299b8167c9bfc1128dab?X-Xet-Cas-Uid=60f1abe7544c2adfd699860c&response-content-disposition=inline%3B+filename*%3DUTF-8%27%27ornith_logo.png%3B+filename%3D%22ornith_logo.png%22%3B&response-content-type=image%2Fpng&user_id=60f1abe7544c2adfd699860c&Expires=1782415346&Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cHM6Ly91cy5hd3MuY2RuLmhmLmNvL3hldC1icmlkZ2UtdXMvNmEzNzY4MWJiNTc4MDNlNjE2NjA2MDg5LzI0MzBjZTU3NTVjY2FhNTQ1ZGVmMDI5MmNiMGYxMDY4ZDRhZjk5ZDUwOTNjMjk5YjgxNjdjOWJmYzExMjhkYWJcXD9YLVhldC1DYXMtVWlkPTYwZjFhYmU3NTQ0YzJhZGZkNjk5ODYwYyZyZXNwb25zZS1jb250ZW50LWRpc3Bvc2l0aW9uPWlubGluZSUzQitmaWxlbmFtZSUyQSUzRFVURi04JTI3JTI3b3JuaXRoX2xvZ28ucG5nJTNCK2ZpbGVuYW1lJTNEJTIyb3JuaXRoX2xvZ28ucG5nJTIyJTNCJnJlc3BvbnNlLWNvbnRlbnQtdHlwZT1pbWFnZSUyRnBuZyZ1c2VyX2lkPTYwZjFhYmU3NTQ0YzJhZGZkNjk5ODYwYyIsIkNvbmRpdGlvbiI6eyJEYXRlTGVzc1RoYW4iOnsiRXBvY2hUaW1lIjoxNzgyNDE1MzQ2fX19XX0_&Signature=MEUCIQCJDHCuf325VtWk4Lr7BrnsQw9LL9RPIuQzcVq7xPY7vAIgXghesfcDb0xuVJm-bq8mkneN1tG5zTgpzIY6NdpDehw_&Key-Pair-Id=01KAYHXK2CBJSW0YZTMNXK9W1M" alt="Ornith" /> 143 <div class="spacer"></div> 144 <div class="badge" id="settings-toggle">â settings</div> 145</header> 146 147<div id="chat"> 148 <div class="welcome"> 149 <h2>Welcome to Ornith-1.0-9B</h2> 150 <p>Agentic coding assistant. Ask me to write, debug, or explain code.</p> 151 </div> 152</div> 153<div class="status" id="status"></div> 154 155<div class="composer"> 156 <div class="composer-inner"> 157 <div id="settings"> 158 <label>Temperature <input type="range" id="temp" min="0.1" max="1.5" step="0.1" value="0.6" /><span id="temp-v">0.6</span></label> 159 <label>Max tokens <input type="range" id="maxtok" min="256" max="8192" step="256" value="2048" /><span id="maxtok-v">2048</span></label> 160 </div> 161 <div class="input-row"> 162 <textarea id="text" placeholder="Ask Ornith to write code, explain, or debugâ¦"></textarea> 163 <button id="send">Send</button> 164 <button id="stop">Stop</button> 165 </div> 166 <div class="hint">Shift+Enter for newline · Enter to send</div> 167 </div> 168</div> 169
170<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
vendor: 1 bytes, line 170
170
171<script src="https://cdn.jsdelivr.net/npm/dompurify@3/dist/purify.min.js"></script>
vendor: 1 bytes, line 171
171
172<script type="module"> 173import { Client } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js"; 174 175const chatEl = document.getElementById("chat"); 176const statusEl = document.getElementById("status"); 177const textEl = document.getElementById("text"); 178const sendBtn = document.getElementById("send"); 179const stopBtn = document.getElementById("stop"); 180const setToggle= document.getElementById("settings-toggle"); 181const settings = document.getElementById("settings"); 182const tempEl = document.getElementById("temp"); 183const tempV = document.getElementById("temp-v"); 184const maxEl = document.getElementById("maxtok"); 185const maxV = document.getElementById("maxtok-v"); 186 187let conversation = []; 188let client = null; 189let job = null; 190 191const connect = async () => { 192 if (!client) client = await Client.connect(window.location.origin, { events: ["status", "data"] }); 193 return client; 194}; 195 196setToggle.addEventListener("click", () => settings.classList.toggle("open")); 197tempEl.addEventListener("input", () => tempV.textContent = tempEl.value); 198maxEl.addEventListener("input", () => maxV.textContent = maxEl.value); 199 200textEl.addEventListener("input", () => { 201 textEl.style.height = "auto"; 202 textEl.style.height = Math.min(textEl.scrollHeight, 160) + "px"; 203}); 204textEl.addEventListener("keydown", (e) => { 205 if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); send(); } 206}); 207sendBtn.addEventListener("click", send); 208stopBtn.addEventListener("click", () => { if (job) job.cancel(); }); 209 210function scrollIfNear() { 211 const near = chatEl.scrollHeight - chatEl.scrollTop - chatEl.clientHeight < 120; 212 if (near) chatEl.scrollTop = chatEl.scrollHeight; 213} 214 215function addTurn(role) { 216 const turn = document.createElement("div"); 217 turn.className = "turn"; 218 const line = document.createElement("div"); 219 line.className = "prompt-line";
220 const prompt = document.createElement("div"); 221 prompt.className = "prompt " + role; 222 const content = document.createElement("div"); 223 content.className = "content"; 224 line.appendChild(prompt); line.appendChild(content); 225 turn.appendChild(line); 226 227 // Remove welcome message on first real turn. 228 const welcome = chatEl.querySelector(".welcome"); 229 if (welcome) welcome.remove(); 230 231 chatEl.appendChild(turn); 232 chatEl.scrollTop = chatEl.scrollHeight; 233 return content; 234} 235 236async function send() { 237 const message = textEl.value.trim(); 238 if (!message) return; 239 if (job) return; 240 241 const userContent = addTurn("user"); 242 userContent.innerHTML = `<div class="user-input">${escapeHtml(message)}</div>`; 243 textEl.value = ""; textEl.style.height = "auto"; 244 245 const history = conversation.slice(); 246 conversation.push({ role: "user", content: message }); 247 248 const assistantContent = addTurn("assistant"); 249 assistantContent.innerHTML = `<span style="color:var(--muted)">â¦</span>`; 250 251 setBusy(true); 252 statusEl.textContent = "connectingâ¦"; 253 254 try { 255 const c = await connect(); 256 const payload = { 257 message, 258 history, 259 max_new_tokens: parseInt(maxEl.value, 10), 260 temperature: parseFloat(tempEl.value), 261 }; 262 job = c.submit("/generate", payload); 263 264 let latestReasoning = ""; 265 let latestAnswer = ""; 266 let complete = false; 267 268 for await (const msg of job) { 269 if (msg.type === "status") { 270 const s = msg.status || msg.data?.status; 271 if (s === "pending") statusEl.textContent = `queuedâ¦${msg.position != null ? " (#" + msg.position + ")" : ""}`; 272 else if (s === "generating") statusEl.textContent = "generatingâ¦"; 273 else if (s === "complete" || s === "error") statusEl.textContent = ""; 274 continue; 275 } 276 if (msg.type !== "data") continue; 277 const raw = Array.isArray(msg.data) ? msg.data[0] : msg.data; 278 let d = raw; 279 if (typeof raw === "string") { try { d = JSON.parse(raw); } catch { d = { answer: raw }; } } 280 if (d?.error) { 281 renderAssistant(assistantContent, latestReasoning, latestAnswer, d.error); 282 statusEl.innerHTML = `<span class="err">error: ${escapeHtml(d.error)}</span>`; 283 break; 284 } 285 latestReasoning = d?.reasoning || ""; 286 latestAnswer = d?.answer || ""; 287 complete = d?.status === "complete"; 288 renderAssistant(assistantContent, latestReasoning, latestAnswer); 289 scrollIfNear(); 290 } 291 292 if (complete && latestAnswer) { 293 conversation.push({ role: "assistant", content: latestAnswer }); 294 } 295 } catch (e) { 296 renderAssistant(assistantContent, "", "", String(e)); 297 statusEl.innerHTML = `<span class="err">${escapeHtml(String(e))}</span>`; 298 } finally { 299 job = null; 300 setBusy(false); 301 statusEl.textContent = ""; 302 } 303} 304 305function renderAssistant(el, reasoning, answer, error) { 306 let h = ""; 307 if (reasoning && reasoning.trim()) { 308 h += `<details class="thinking"${answer ? "" : " open"}><summary>thinking</summary><pre>${escapeHtml(reasoning)}</pre></details>`; 309 } 310 if (error) { 311 h += `<div class="err">â ${escapeHtml(error)}</div>`; 312 } else if (answer && answer.trim()) { 313 h += `<div class="answer">${DOMPurify.sanitize(marked.parse(answer))}</div>`; 314 } else if (!reasoning) { 315 h += `<span style="color:var(--muted)">â¦</span>`; 316 } 317 el.innerHTML = h; 318} 319 320function setBusy(busy) { 321 sendBtn.disabled = busy; 322 sendBtn.style.display = busy ? "none" : ""; 323 stopBtn.style.display = busy ? "" : "none"; 324} 325 326function escapeHtml(s) { 327 return String(s ?? "") 328 .replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") 329 .replace(/"/g, """).replace(/'/g, "'"); 330} 331
332textEl.focus(); 333</script>
333 334</body> 335</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.