Muse-Glimmer-30B / index.html
akhaliq's picture
akhaliq HF Staff
Add clickable example prompt chips
9672613
Raw
History Blame
14.6 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Muse Glimmer 30B — Meta AI</title>
<style>
:root {
--blue: #1877F2;
--blue-dark: #1458b8;
--bg: #0a1128;
--bg2: #0d1b3e;
--card: #111d3a;
--border: #233460;
--text: #ffffff;
--muted: #9aa8c7;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background: radial-gradient(ellipse at 50% 30%, #14245c 0%, var(--bg2) 40%, var(--bg) 100%);
color: var(--text);
height: 100vh;
display: flex;
flex-direction: column;
}
/* Header */
header {
display: flex; align-items: center; gap: 32px;
padding: 14px 28px;
border-bottom: 1px solid rgba(255,255,255,.06);
}
header .brand { display: flex; align-items: center; gap: 8px; font-weight: 700; font-size: 17px; }
header nav { display: flex; gap: 28px; font-size: 14px; font-weight: 500; }
header nav a { color: #dfe6f5; text-decoration: none; }
header nav a:hover { color: #fff; }
header .cta {
margin-left: auto; background: var(--blue); color: #fff; border: none;
padding: 9px 20px; border-radius: 999px; font-weight: 600; font-size: 14px; cursor: pointer;
}
header .cta:hover { background: var(--blue-dark); }
@media (max-width: 760px) { header nav { display: none; } }
/* Orb */
.orb { display: flex; justify-content: center; padding: 36px 0 8px; }
.orb svg { width: 130px; height: 130px; animation: spin 24s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
/* Hero */
.hero { text-align: center; padding: 12px 20px 20px; }
.hero h1 { font-size: clamp(34px, 6vw, 58px); font-weight: 700; letter-spacing: -1px; }
.hero p { color: #c6d0e8; font-size: 17px; margin-top: 14px; line-height: 1.5; }
/* Chat */
#chat {
flex: 1; overflow-y: auto; padding: 10px 20px 20px;
display: flex; flex-direction: column; gap: 12px;
max-width: 800px; width: 100%; margin: 0 auto;
}
.msg { max-width: 78%; padding: 12px 16px; border-radius: 20px; line-height: 1.5; white-space: pre-wrap; word-break: break-word; font-size: 15px; }
.user { align-self: flex-end; background: var(--blue); color: #fff; border-bottom-right-radius: 6px; }
.bot { align-self: flex-start; background: var(--card); border: 1px solid var(--border); color: #eef2fb; border-bottom-left-radius: 6px; }
.msg img, .msg video { max-width: 240px; border-radius: 12px; display: block; margin-bottom: 8px; }
.sys { align-self: center; color: var(--muted); font-size: 12px; background: none; }
.reasoning { margin-bottom: 8px; border-left: 2px solid var(--border); padding-left: 10px; }
.reasoning summary { cursor: pointer; color: #8ab4ff; font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: .5px; }
.reasoning-body { color: var(--muted); font-size: 13px; font-style: italic; margin-top: 6px; white-space: pre-wrap; }
.bot.typing span::after { content: "▊"; animation: blink 1s steps(1) infinite; color: #8ab4ff; }
@keyframes blink { 50% { opacity: 0; } }
/* Controls */
#controls {
max-width: 800px; width: 100%; margin: 0 auto; padding: 0 20px 10px;
display: flex; gap: 18px; flex-wrap: wrap; font-size: 12px; color: var(--muted); align-items: center;
}
#controls label { display: flex; align-items: center; gap: 8px; }
select {
background: var(--card); color: var(--text); border: 1px solid var(--border);
border-radius: 8px; padding: 4px 8px; font-size: 12px;
}
input[type=range] { accent-color: var(--blue); }
#controls b { color: #dfe6f5; }
#examples { max-width: 800px; width: 100%; margin: 0 auto; padding: 4px 20px 14px; display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; }
.chip {
background: var(--card); border: 1px solid var(--border); color: #dfe6f5;
padding: 8px 16px; border-radius: 999px; font-size: 13px; font-weight: 500; cursor: pointer;
}
.chip:hover { border-color: var(--blue); color: #fff; background: #16244a; }
/* Composer */
#composer { padding: 12px 20px 18px; }
#composer .row {
max-width: 800px; margin: 0 auto; display: flex; gap: 10px; align-items: flex-end;
background: var(--card); border: 1px solid var(--border);
border-radius: 24px; padding: 8px 10px;
}
#composer .row:focus-within { border-color: var(--blue); box-shadow: 0 0 0 3px rgba(24,119,242,.25); }
#input {
flex: 1; resize: none; border: none; background: transparent; color: var(--text);
padding: 8px 10px; font-size: 15px; font-family: inherit; max-height: 140px;
}
#input:focus { outline: none; }
#input::placeholder { color: var(--muted); }
button {
background: var(--blue); border: none; color: #fff; font-weight: 600;
padding: 10px 18px; border-radius: 999px; cursor: pointer; font-size: 14px;
}
button:disabled { opacity: .5; cursor: default; }
button:hover:not(:disabled) { background: var(--blue-dark); }
#attach { background: transparent; color: var(--muted); padding: 10px 12px; font-size: 17px; }
#attach:hover:not(:disabled) { background: rgba(255,255,255,.08); color: #fff; }
#stop { background: #e0245e; }
#preview { max-width: 800px; margin: 0 auto 10px; display: none; position: relative; width: fit-content; }
#preview img { height: 64px; border-radius: 12px; border: 1px solid var(--border); }
#preview .x {
position: absolute; top: -8px; right: -8px; background: #e0245e; color: #fff;
border-radius: 50%; width: 20px; height: 20px; font-size: 11px;
display: flex; align-items: center; justify-content: center; cursor: pointer;
}
</style>
</head>
<body>
<header>
<div class="brand">
<svg width="24" height="16" viewBox="0 0 36 24" fill="none">
<path d="M6 24C2.7 24 0 21.3 0 18c0-1.5.6-3 1.5-4.5C3.5 10.5 6.7 6 9.9 3.2 11.5 1.8 13.2 1 14.7 1c2.4 0 4 1.6 5.1 3.7.4.9.9 2.1 1.4 3.5.5-1.4 1-2.6 1.4-3.5C23.7 2.6 25.4 1 27.8 1c1.5 0 3.2.8 4.8 2.2 3.2 2.8 6.4 7.3 8.4 10.3.9 1.5 1.5 3 1.5 4.5 0 3.3-2.7 6-6 6-2 0-3.7-1-5-2.8-1.3-1.8-2.6-4.4-3.9-7.2-.4-1-.9-2-1.3-3.1-.4 1-.9 2.1-1.3 3.1-1.3 2.8-2.6 5.4-3.9 7.2C19.7 23 18 24 16 24h-10z" fill="#1877F2" transform="scale(0.8)"/>
</svg>
Muse&nbsp;Glimmer
</div>
<nav>
<a href="https://huggingface.co/meta-models/Muse-Glimmer-30B" target="_blank">Model Card</a>
</nav>
</header>
<div class="orb">
<svg viewBox="0 0 100 100">
<defs>
<linearGradient id="g1" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#c084fc"/><stop offset="100%" stop-color="#7c3aed"/>
</linearGradient>
</defs>
<g>
<ellipse cx="50" cy="14" rx="9" ry="14" fill="url(#g1)" transform="rotate(0 50 50)"/>
<ellipse cx="50" cy="14" rx="9" ry="14" fill="url(#g1)" transform="rotate(51 50 50)"/>
<ellipse cx="50" cy="14" rx="9" ry="14" fill="url(#g1)" transform="rotate(103 50 50)"/>
<ellipse cx="50" cy="14" rx="9" ry="14" fill="url(#g1)" transform="rotate(154 50 50)"/>
<ellipse cx="50" cy="14" rx="9" ry="14" fill="url(#g1)" transform="rotate(206 50 50)"/>
<ellipse cx="50" cy="14" rx="9" ry="14" fill="url(#g1)" transform="rotate(257 50 50)"/>
<ellipse cx="50" cy="14" rx="9" ry="14" fill="url(#g1)" transform="rotate(309 50 50)"/>
</g>
</svg>
</div>
<div class="hero" id="hero">
<h1>Do more with Muse Glimmer</h1>
<p>A 30B multimodal agentic model — answering questions, reasoning over images and video,<br>and completing tasks, right here on ZeroGPU.</p>
</div>
<div id="examples">
<button class="chip" data-text="Write a short joke about saving RAM.">😂 Joke about RAM</button>
<button class="chip" data-text="Write a Python function to compute Fibonacci numbers with memoization.">🐍 Fibonacci in Python</button>
<button class="chip" data-text="Explain how speculative decoding works in 3 sentences.">⚡ Speculative decoding</button>
<button class="chip" data-text="Detect the bridge. Return only the detection in the model's native object-detection format, with no explanation." data-needs-image="true">🌉 Detect the bridge (attach an image)</button>
<button class="chip" data-text="Describe what happens in this video." data-needs-image="true">🎬 Describe this video (attach a video)</button>
</div>
<div id="chat"></div>
<div id="controls">
<label>Reasoning
<select id="reasoning">
<option>low</option><option>medium</option>
<option selected>high</option><option>xhigh</option>
</select>
</label>
<label>Queue <span id="queue"></span></label>
</div>
<div id="composer">
<div id="preview"><img id="pimg"><div class="x" onclick="clearImage()"></div></div>
<div class="row">
<button id="attach" title="Attach image">📎</button>
<input type="file" id="file" accept="image/*,video/*" hidden>
<textarea id="input" rows="1" placeholder="Message Muse Glimmer…"></textarea>
<button id="send">Send</button>
<button id="stop" style="display:none">Stop</button>
</div>
</div>
<script type="module">
import { Client, handle_file } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js";
const chatEl = document.getElementById("chat");
const hero = document.getElementById("hero");
const input = document.getElementById("input");
const sendBtn = document.getElementById("send");
const stopBtn = document.getElementById("stop");
const fileInput = document.getElementById("file");
const preview = document.getElementById("preview");
const pimg = document.getElementById("pimg");
const queueEl = document.getElementById("queue");
let attachedFile = null;
let history = [];
let currentJob = null;
const client = await Client.connect(window.location.origin, { events: ["data", "status"] });
document.getElementById("attach").onclick = () => fileInput.click();
fileInput.onchange = () => {
attachedFile = fileInput.files[0] || null;
if (attachedFile) {
const url = URL.createObjectURL(attachedFile);
if (attachedFile.type.startsWith("video/")) {
preview.innerHTML = `<video src="${url}" style="height:64px;border-radius:12px;border:1px solid var(--border)" muted></video><div class="x" onclick="clearImage()">✕</div>`;
} else {
preview.innerHTML = `<img src="${url}" style="height:64px;border-radius:12px;border:1px solid var(--border)"><div class="x" onclick="clearImage()">✕</div>`;
}
preview.style.display = "block";
} else preview.style.display = "none";
};
window.clearImage = () => { attachedFile = null; fileInput.value = ""; preview.style.display = "none"; };
function addMsg(role, text, mediaUrl, isVideo) {
const div = document.createElement("div");
div.className = "msg " + role;
if (mediaUrl) {
const im = document.createElement(isVideo ? "video" : "img");
im.src = mediaUrl;
if (isVideo) { im.controls = true; im.muted = true; }
div.appendChild(im);
}
const t = document.createElement("span"); t.textContent = text; div.appendChild(t);
chatEl.appendChild(div);
chatEl.scrollTop = chatEl.scrollHeight;
return t;
}
// Render a bot message, splitting the model's reasoning preamble
// (everything before the "to=user" marker) into a collapsible block.
function renderBot(div, raw) {
div.innerHTML = "";
let reasoning = "", final = raw;
if (raw.includes("to=user")) {
const idx = raw.lastIndexOf("to=user");
reasoning = raw.slice(0, idx).replace(/\.?assistant\s*$/, "").trim();
final = raw.slice(idx + "to=user".length).trim();
} else {
reasoning = raw.trim();
final = "";
}
if (reasoning) {
const det = document.createElement("details");
det.className = "reasoning";
det.open = !final; // expand while still thinking, collapse once answer starts
const sum = document.createElement("summary");
sum.textContent = final ? "Reasoning" : "Thinking…";
det.appendChild(sum);
const body = document.createElement("div");
body.className = "reasoning-body";
body.textContent = reasoning;
det.appendChild(body);
div.appendChild(det);
}
const t = document.createElement("span");
t.textContent = final || "💭 thinking…";
div.appendChild(t);
}
function setBusy(b) {
sendBtn.style.display = b ? "none" : "";
stopBtn.style.display = b ? "" : "none";
input.disabled = b;
}
async function send() {
const text = input.value.trim();
if (!text && !attachedFile) return;
hero.style.display = "none";
document.getElementById("examples").style.display = "none";
document.querySelector(".orb").style.padding = "12px 0 4px";
const isVideo = attachedFile && attachedFile.type.startsWith("video/");
const mediaUrl = attachedFile ? URL.createObjectURL(attachedFile) : null;
addMsg("user", text || (isVideo ? "[video]" : "[image]"), mediaUrl, isVideo);
input.value = "";
input.style.height = "auto";
setBusy(true);
const botDiv = addMsg("bot", "").parentElement;
botDiv.classList.add("typing");
try {
currentJob = client.submit("/chat", {
message: text,
media: attachedFile ? handle_file(attachedFile) : null,
history: history,
reasoning: document.getElementById("reasoning").value,
});
let finalText = "";
for await (const event of currentJob) {
if (event.type === "status") {
queueEl.textContent = event.stage + (event.position != null ? ` #${event.position}` : "");
}
if (event.type === "data") {
finalText = event.data[0];
renderBot(botDiv, finalText);
chatEl.scrollTop = chatEl.scrollHeight;
}
}
history.push({ role: "user", content: text || (isVideo ? "[video]" : "[image]") });
history.push({ role: "assistant", content: finalText });
} catch (e) {
botDiv.textContent = "⚠️ " + (e.message || e);
} finally {
botDiv.classList.remove("typing");
setBusy(false);
clearImage();
queueEl.textContent = "—";
currentJob = null;
input.focus();
}
}
sendBtn.onclick = send;
stopBtn.onclick = () => { if (currentJob) currentJob.cancel(); };
input.onkeydown = e => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); send(); } };
input.oninput = () => { input.style.height = "auto"; input.style.height = Math.min(input.scrollHeight, 140) + "px"; };
document.querySelectorAll(".chip").forEach(chip => {
chip.onclick = () => {
input.value = chip.dataset.text;
input.dispatchEvent(new Event("input"));
input.focus();
if (chip.dataset.needsImage) fileInput.click();
};
});
</script>
</body>
</html>