Ornith-1.0-9B / index.html
akhaliq's picture
akhaliq HF Staff
Remove image upload; switch to text-only coding assistant
69efd3e
Raw
History Blame Contribute Delete
13.2 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Ornith-1.0-9B · Terminal</title>
<style>
:root {
--bg: #0a0a0a;
--panel: #111111;
--panel-2: #171717;
--border: #2a2a2a;
--text: #e0e0e0;
--muted: #6b6b6b;
--green: #4ade80;
--green-dim: #22c55e;
--yellow: #facc15;
--red: #f87171;
--blue: #60a5fa;
--mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
}
* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body {
background: var(--bg);
color: var(--text);
font-family: var(--mono);
font-size: 14px;
line-height: 1.6;
display: flex;
flex-direction: column;
}
header {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 16px;
background: var(--panel);
border-bottom: 1px solid var(--border);
}
header img { height: 28px; width: auto; }
header .title { font-weight: 600; font-size: 13px; color: var(--text); }
header .title span { color: var(--muted); font-weight: 400; }
header .spacer { flex: 1; }
header .badge {
font-size: 11px; color: var(--muted); border: 1px solid var(--border);
padding: 2px 8px; border-radius: 4px; cursor: pointer; user-select: none;
}
header .badge:hover { color: var(--text); border-color: var(--muted); }
#chat {
flex: 1;
overflow-y: auto;
padding: 0 16px;
}
.turn { max-width: 900px; margin: 0 auto; padding: 10px 0; border-bottom: 1px solid var(--border); }
.turn:last-child { border-bottom: none; }
.prompt-line { display: flex; align-items: flex-start; gap: 10px; }
.prompt { color: var(--green); white-space: nowrap; font-weight: 600; user-select: none; }
.prompt.user::before { content: "❯"; }
.prompt.assistant::before { content: "◆"; color: var(--green-dim); }
.content { flex: 1; min-width: 0; }
.user-input { color: var(--text); white-space: pre-wrap; }
.thinking { margin: 6px 0 10px; }
.thinking summary {
cursor: pointer; color: var(--muted); font-size: 12px; list-style: none;
display: flex; align-items: center; gap: 6px;
}
.thinking summary::-webkit-details-marker { display: none; }
.thinking summary::before { content: "▸"; color: var(--green-dim); }
.thinking[open] summary::before { content: "▾"; }
.thinking pre {
margin: 6px 0 0; padding: 10px 12px; background: var(--panel-2);
border-left: 2px solid var(--green-dim); color: var(--muted);
font: inherit; white-space: pre-wrap; word-wrap: break-word;
max-height: 300px; overflow-y: auto;
}
.answer { color: var(--text); }
.answer p { margin: 0 0 8px; }
.answer p:last-child { margin-bottom: 0; }
.answer pre {
background: var(--panel-2); border: 1px solid var(--border); border-radius: 4px;
padding: 12px 14px; overflow-x: auto; margin: 10px 0; font: inherit;
}
.answer code { font-family: inherit; }
.answer :not(pre) > code { background: var(--panel-2); padding: 1px 4px; border-radius: 3px; color: var(--yellow); }
.answer ul, .answer ol { padding-left: 22px; margin: 6px 0; }
.answer a { color: var(--green); }
.answer blockquote { margin: 8px 0; padding-left: 12px; border-left: 2px solid var(--muted); color: var(--muted); }
.answer h1,.answer h2,.answer h3 { margin: 14px 0 6px; font-size: inherit; color: var(--green); }
.answer h1 { font-size: 16px; }
.answer h2 { font-size: 15px; }
.status { max-width: 900px; margin: 0 auto; padding: 4px 0; color: var(--muted); font-size: 12px; min-height: 18px; }
.status .err { color: var(--red); }
.composer {
border-top: 1px solid var(--border); background: var(--panel); padding: 12px 16px 16px;
}
.composer-inner { max-width: 900px; margin: 0 auto; }
#settings {
display: none; gap: 18px; flex-wrap: wrap; margin-bottom: 10px; color: var(--muted); font-size: 12px;
}
#settings.open { display: flex; }
#settings label { display: flex; flex-direction: column; gap: 4px; }
#settings input[type=range] { width: 150px; accent-color: var(--green); }
.input-row { display: flex; align-items: flex-end; gap: 8px; }
#text {
flex: 1; resize: none; min-height: 42px; max-height: 160px;
background: var(--bg); color: var(--text); border: 1px solid var(--border);
border-radius: 6px; padding: 10px 12px; font: inherit; outline: none;
}
#text:focus { border-color: var(--green); }
button {
background: var(--green); color: #052e16; border: none; border-radius: 6px;
padding: 10px 16px; font: 600 13px inherit; cursor: pointer;
}
button:disabled { opacity: .5; cursor: not-allowed; }
#stop { background: var(--panel-2); color: var(--text); border: 1px solid var(--border); display: none; }
.icon-btn { background: var(--panel-2); color: var(--text); border: 1px solid var(--border); padding: 10px 12px; font-size: 14px; }
.hint { color: var(--muted); font-size: 11px; margin-top: 8px; text-align: center; }
/* Empty state */
.welcome { max-width: 900px; margin: 24px auto; color: var(--muted); }
.welcome h2 { color: var(--green); margin: 0 0 8px; font-size: 14px; }
.welcome p { margin: 0; }
.welcome .cmd { color: var(--yellow); }
</style>
</head>
<body>
<header>
<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" />
<div class="spacer"></div>
<div class="badge" id="settings-toggle">⚙ settings</div>
</header>
<div id="chat">
<div class="welcome">
<h2>Welcome to Ornith-1.0-9B</h2>
<p>Agentic coding assistant. Ask me to write, debug, or explain code.</p>
</div>
</div>
<div class="status" id="status"></div>
<div class="composer">
<div class="composer-inner">
<div id="settings">
<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>
<label>Max tokens <input type="range" id="maxtok" min="256" max="8192" step="256" value="2048" /><span id="maxtok-v">2048</span></label>
</div>
<div class="input-row">
<textarea id="text" placeholder="Ask Ornith to write code, explain, or debug…"></textarea>
<button id="send">Send</button>
<button id="stop">Stop</button>
</div>
<div class="hint">Shift+Enter for newline · Enter to send</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/dompurify@3/dist/purify.min.js"></script>
<script type="module">
import { Client } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js";
const chatEl = document.getElementById("chat");
const statusEl = document.getElementById("status");
const textEl = document.getElementById("text");
const sendBtn = document.getElementById("send");
const stopBtn = document.getElementById("stop");
const setToggle= document.getElementById("settings-toggle");
const settings = document.getElementById("settings");
const tempEl = document.getElementById("temp");
const tempV = document.getElementById("temp-v");
const maxEl = document.getElementById("maxtok");
const maxV = document.getElementById("maxtok-v");
let conversation = [];
let client = null;
let job = null;
const connect = async () => {
if (!client) client = await Client.connect(window.location.origin, { events: ["status", "data"] });
return client;
};
setToggle.addEventListener("click", () => settings.classList.toggle("open"));
tempEl.addEventListener("input", () => tempV.textContent = tempEl.value);
maxEl.addEventListener("input", () => maxV.textContent = maxEl.value);
textEl.addEventListener("input", () => {
textEl.style.height = "auto";
textEl.style.height = Math.min(textEl.scrollHeight, 160) + "px";
});
textEl.addEventListener("keydown", (e) => {
if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); send(); }
});
sendBtn.addEventListener("click", send);
stopBtn.addEventListener("click", () => { if (job) job.cancel(); });
function scrollIfNear() {
const near = chatEl.scrollHeight - chatEl.scrollTop - chatEl.clientHeight < 120;
if (near) chatEl.scrollTop = chatEl.scrollHeight;
}
function addTurn(role) {
const turn = document.createElement("div");
turn.className = "turn";
const line = document.createElement("div");
line.className = "prompt-line";
const prompt = document.createElement("div");
prompt.className = "prompt " + role;
const content = document.createElement("div");
content.className = "content";
line.appendChild(prompt); line.appendChild(content);
turn.appendChild(line);
// Remove welcome message on first real turn.
const welcome = chatEl.querySelector(".welcome");
if (welcome) welcome.remove();
chatEl.appendChild(turn);
chatEl.scrollTop = chatEl.scrollHeight;
return content;
}
async function send() {
const message = textEl.value.trim();
if (!message) return;
if (job) return;
const userContent = addTurn("user");
userContent.innerHTML = `<div class="user-input">${escapeHtml(message)}</div>`;
textEl.value = ""; textEl.style.height = "auto";
const history = conversation.slice();
conversation.push({ role: "user", content: message });
const assistantContent = addTurn("assistant");
assistantContent.innerHTML = `<span style="color:var(--muted)">…</span>`;
setBusy(true);
statusEl.textContent = "connecting…";
try {
const c = await connect();
const payload = {
message,
history,
max_new_tokens: parseInt(maxEl.value, 10),
temperature: parseFloat(tempEl.value),
};
job = c.submit("/generate", payload);
let latestReasoning = "";
let latestAnswer = "";
let complete = false;
for await (const msg of job) {
if (msg.type === "status") {
const s = msg.status || msg.data?.status;
if (s === "pending") statusEl.textContent = `queued…${msg.position != null ? " (#" + msg.position + ")" : ""}`;
else if (s === "generating") statusEl.textContent = "generating…";
else if (s === "complete" || s === "error") statusEl.textContent = "";
continue;
}
if (msg.type !== "data") continue;
const raw = Array.isArray(msg.data) ? msg.data[0] : msg.data;
let d = raw;
if (typeof raw === "string") { try { d = JSON.parse(raw); } catch { d = { answer: raw }; } }
if (d?.error) {
renderAssistant(assistantContent, latestReasoning, latestAnswer, d.error);
statusEl.innerHTML = `<span class="err">error: ${escapeHtml(d.error)}</span>`;
break;
}
latestReasoning = d?.reasoning || "";
latestAnswer = d?.answer || "";
complete = d?.status === "complete";
renderAssistant(assistantContent, latestReasoning, latestAnswer);
scrollIfNear();
}
if (complete && latestAnswer) {
conversation.push({ role: "assistant", content: latestAnswer });
}
} catch (e) {
renderAssistant(assistantContent, "", "", String(e));
statusEl.innerHTML = `<span class="err">${escapeHtml(String(e))}</span>`;
} finally {
job = null;
setBusy(false);
statusEl.textContent = "";
}
}
function renderAssistant(el, reasoning, answer, error) {
let h = "";
if (reasoning && reasoning.trim()) {
h += `<details class="thinking"${answer ? "" : " open"}><summary>thinking</summary><pre>${escapeHtml(reasoning)}</pre></details>`;
}
if (error) {
h += `<div class="err">⚠ ${escapeHtml(error)}</div>`;
} else if (answer && answer.trim()) {
h += `<div class="answer">${DOMPurify.sanitize(marked.parse(answer))}</div>`;
} else if (!reasoning) {
h += `<span style="color:var(--muted)">…</span>`;
}
el.innerHTML = h;
}
function setBusy(busy) {
sendBtn.disabled = busy;
sendBtn.style.display = busy ? "none" : "";
stopBtn.style.display = busy ? "" : "none";
}
function escapeHtml(s) {
return String(s ?? "")
.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
.replace(/"/g, "&quot;").replace(/'/g, "&#39;");
}
textEl.focus();
</script>
</body>
</html>