| |
| |
| |
| |
| import { Client } from "https://cdn.jsdelivr.net/npm/@gradio/client/+esm"; |
|
|
| const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches; |
| const cssv = (v) => getComputedStyle(document.documentElement).getPropertyValue(v).trim(); |
|
|
| |
| const TEACHERS = [ |
| { id: "qwen", label: "qwen", varc: "--t-qwen" }, |
| { id: "smollm", label: "smollm", varc: "--t-smollm" }, |
| { id: "phi", label: "phi", varc: "--t-phi" }, |
| { id: "gemma", label: "gemma", varc: "--t-gemma" }, |
| { id: "minicpm", label: "minicpm", varc: "--t-minicpm" }, |
| { id: "nemotron", label: "nemotron", varc: "--t-nemo" }, |
| ]; |
| const colorFor = (name) => |
| name === "student" ? cssv("--spark") |
| : cssv((TEACHERS.find((t) => t.id === name) || {}).varc || "--text-dim"); |
|
|
| |
| (function buildConverge() { |
| const cx = 300, cy = 300, R = 232; |
| const threads = document.getElementById("threads"); |
| const flows = document.getElementById("flows"); |
| const tnodes = document.getElementById("tnodes"); |
| TEACHERS.forEach((t, i) => { |
| const a = (-90 + i * 60) * Math.PI / 180; |
| const x = cx + R * Math.cos(a), y = cy + R * Math.sin(a); |
| const mx = (x + cx) / 2, my = (y + cy) / 2; |
| const perp = a + Math.PI / 2; |
| const ctrlX = mx + 46 * Math.cos(perp), ctrlY = my + 46 * Math.sin(perp); |
| const d = `M ${x} ${y} Q ${ctrlX} ${ctrlY} ${cx} ${cy}`; |
| const col = cssv(t.varc); |
|
|
| const base = document.createElementNS("http://www.w3.org/2000/svg", "path"); |
| base.setAttribute("d", d); base.setAttribute("class", "thread"); |
| base.setAttribute("stroke", col); |
| threads.appendChild(base); |
|
|
| const flow = document.createElementNS("http://www.w3.org/2000/svg", "path"); |
| flow.setAttribute("d", d); flow.setAttribute("class", "thread-flow"); |
| flow.setAttribute("stroke", col); |
| if (!reduce) flow.style.animation = `flow ${2.4 + i * 0.25}s linear infinite`; |
| flows.appendChild(flow); |
|
|
| const g = document.createElementNS("http://www.w3.org/2000/svg", "g"); |
| const halo = document.createElementNS("http://www.w3.org/2000/svg", "circle"); |
| halo.setAttribute("cx", x); halo.setAttribute("cy", y); halo.setAttribute("r", 13); |
| halo.setAttribute("fill", col); halo.setAttribute("opacity", "0.22"); halo.setAttribute("filter", "url(#soft)"); |
| const node = document.createElementNS("http://www.w3.org/2000/svg", "circle"); |
| node.setAttribute("cx", x); node.setAttribute("cy", y); node.setAttribute("r", 7); |
| node.setAttribute("fill", col); node.setAttribute("class", "tnode"); |
| const lx = cx + (R + 30) * Math.cos(a), ly = cy + (R + 30) * Math.sin(a); |
| const isRight = Math.cos(a) > 0.2, isLeft = Math.cos(a) < -0.2; |
| const lbl = document.createElementNS("http://www.w3.org/2000/svg", "text"); |
| lbl.setAttribute("x", lx); lbl.setAttribute("y", ly + 4); |
| lbl.setAttribute("text-anchor", isRight ? "start" : isLeft ? "end" : "middle"); |
| lbl.setAttribute("class", "tlabel"); lbl.textContent = t.label; |
| g.appendChild(halo); g.appendChild(node); g.appendChild(lbl); |
| tnodes.appendChild(g); |
| }); |
| })(); |
|
|
| |
| const EXAMPLES = [ |
| "Natalia sold clips to 48 friends in April, then half as many in May. How many altogether?", |
| "Which property of a mineral can be determined just by looking at it? (A) luster (B) mass (C) weight (D) hardness", |
| "Write a Python function that checks if a word is a palindrome.", |
| "Explain why the sky is blue in two sentences.", |
| ]; |
| const exWrap = document.getElementById("examples"); |
| EXAMPLES.forEach((ex, i) => { |
| const li = document.createElement("li"); |
| const b = document.createElement("button"); |
| b.className = "chip"; b.type = "button"; |
| b.innerHTML = `<span class="ix">0${i + 1}</span><span class="qt"></span><span class="go">↵</span>`; |
| b.querySelector(".qt").textContent = ex; |
| b.addEventListener("click", () => { |
| document.getElementById("ask").value = ex; |
| document.getElementById("ask").focus(); |
| }); |
| li.appendChild(b); exWrap.appendChild(li); |
| }); |
|
|
| const infRows = document.getElementById("infRows"); |
| const fills = {}; |
| TEACHERS.forEach((t) => { |
| const row = document.createElement("div"); row.className = "inf-row"; |
| row.style.color = cssv(t.varc); |
| row.innerHTML = `<span class="nm">${t.label}</span> |
| <span class="track"><span class="fill"></span></span> |
| <span class="pc">—</span>`; |
| infRows.appendChild(row); |
| fills[t.id] = { fill: row.querySelector(".fill"), pc: row.querySelector(".pc") }; |
| }); |
|
|
| function setInfluence(gates) { |
| if (!gates) return; |
| TEACHERS.forEach((t, i) => { |
| const val = gates[i] || 0; |
| fills[t.id].fill.style.width = (val * 100).toFixed(0) + "%"; |
| fills[t.id].pc.textContent = val > 0 ? val.toFixed(2).slice(1) : "—"; |
| }); |
| } |
| function resetInfluence() { |
| TEACHERS.forEach((t) => { fills[t.id].fill.style.width = "0%"; fills[t.id].pc.textContent = "—"; }); |
| } |
| function topIdx(g) { let m = 0; for (let i = 1; i < g.length; i++) if (g[i] > g[m]) m = i; return m; } |
|
|
| |
| let mode = "ask"; |
| let busy = false; |
| let client = null; |
| const answers = document.getElementById("answers"); |
| const beforeCard = document.getElementById("beforeCard"); |
| const bodyAfter = document.getElementById("bodyAfter"); |
| const bodyBefore = document.getElementById("bodyBefore"); |
| const askBtn = document.getElementById("askBtn"); |
| const infNote = document.getElementById("infNote"); |
|
|
| document.querySelectorAll(".seg button").forEach((b) => { |
| b.addEventListener("click", () => { |
| document.querySelectorAll(".seg button").forEach((x) => x.setAttribute("aria-pressed", "false")); |
| b.setAttribute("aria-pressed", "true"); |
| mode = b.dataset.mode; |
| answers.classList.toggle("compare", mode === "compare"); |
| beforeCard.hidden = (mode !== "compare"); |
| }); |
| }); |
|
|
| function render(el, text, streaming) { |
| el.textContent = text || ""; |
| if (streaming) { |
| const caret = document.createElement("span"); |
| caret.className = "caret"; caret.textContent = "▋"; |
| el.appendChild(caret); |
| } |
| } |
|
|
| async function ask() { |
| const text = document.getElementById("ask").value.trim(); |
| if (!text || busy) return; |
| if (!client) { render(bodyAfter, "⚠ backend not connected", false); return; } |
|
|
| busy = true; |
| askBtn.classList.add("busy"); askBtn.firstChild.textContent = "Thinking "; |
| infNote.textContent = "routing through teachers…"; |
| resetInfluence(); |
| bodyAfter.innerHTML = '<span class="placeholder-note">distilling…</span>'; |
| if (mode === "compare") bodyBefore.textContent = ""; |
|
|
| try { |
| let gates = null; |
| if (mode === "compare") { |
| const job = client.submit("/arena", { text }); |
| for await (const msg of job) { |
| if (msg.type !== "data" || !msg.data?.[0]) continue; |
| const d = msg.data[0]; |
| if (d.error) { render(bodyAfter, "⚠ " + d.error, false); break; } |
| gates = d.gates; setInfluence(gates); |
| render(bodyBefore, d.base, !d.done); |
| render(bodyAfter, d.deku, !d.done); |
| if (d.done && gates) dropProbeByGates(gates); |
| } |
| } else { |
| const job = client.submit("/probe", { text }); |
| for await (const msg of job) { |
| if (msg.type !== "data" || !msg.data?.[0]) continue; |
| const d = msg.data[0]; |
| if (d.error) { render(bodyAfter, "⚠ " + d.error, false); break; } |
| gates = d.gates; setInfluence(gates); |
| render(bodyAfter, d.text, !d.done); |
| if (d.done) { if (d.point) dropProbePoint(d.point); else if (gates) dropProbeByGates(gates); } |
| } |
| } |
| if (gates) { |
| const t = TEACHERS[topIdx(gates)].label; |
| infNote.textContent = `leaning on ${t} · ${(gates[topIdx(gates)] * 100).toFixed(0)}%`; |
| } else { |
| infNote.textContent = "idle — waiting for a prompt"; |
| } |
| } catch (e) { |
| console.error(e); |
| render(bodyAfter, "⚠ " + (e.message || e), false); |
| infNote.textContent = "error"; |
| } finally { |
| busy = false; |
| askBtn.classList.remove("busy"); askBtn.firstChild.textContent = "Ask "; |
| } |
| } |
| askBtn.addEventListener("click", ask); |
| document.getElementById("ask").addEventListener("keydown", (e) => { |
| if ((e.metaKey || e.ctrlKey) && e.key === "Enter") ask(); |
| }); |
|
|
| |
| const stage = document.getElementById("mapStage"); |
| const layers = { |
| far: document.getElementById("mapFar"), |
| mid: document.getElementById("mapMid"), |
| near: document.getElementById("mapNear"), |
| }; |
| const legend = document.getElementById("mapLegend"); |
|
|
| let VIZ = null; |
| let PROJ = null; |
| let CENTROIDS = {}; |
|
|
| |
| const CENTERS = { |
| qwen: [0.30, 0.32], smollm: [0.70, 0.26], phi: [0.80, 0.58], |
| gemma: [0.58, 0.78], minicpm: [0.30, 0.72], nemotron: [0.46, 0.50], |
| }; |
| const rnd = (n) => (Math.random() - 0.5) * n; |
|
|
| function clearLayers() { Object.values(layers).forEach((l) => (l.innerHTML = "")); } |
|
|
| function addDot(layer, leftPct, topPct, size, color, opacity) { |
| const d = document.createElement("div"); d.className = "dot"; |
| d.style.left = leftPct + "%"; d.style.top = topPct + "%"; |
| d.style.width = d.style.height = size + "px"; |
| d.style.color = color; d.style.background = color; d.style.opacity = opacity; |
| layer.appendChild(d); |
| return d; |
| } |
|
|
| function computeProjection(models) { |
| let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity; |
| for (const m of models) for (const p of m.points) { |
| if (p[0] < minX) minX = p[0]; if (p[0] > maxX) maxX = p[0]; |
| if (p[1] < minY) minY = p[1]; if (p[1] > maxY) maxY = p[1]; |
| } |
| return { minX, maxX, minY, maxY }; |
| } |
| function project(p) { |
| const { minX, maxX, minY, maxY } = PROJ; |
| const nx = (p[0] - minX) / ((maxX - minX) || 1); |
| const ny = (p[1] - minY) / ((maxY - minY) || 1); |
| return [ |
| Math.max(0, Math.min(1, 0.08 + nx * 0.84)) * 100, |
| Math.max(0, Math.min(1, 0.08 + ny * 0.84)) * 100, |
| ]; |
| } |
|
|
| function placeReal() { |
| clearLayers(); |
| PROJ = computeProjection(VIZ.models); |
| CENTROIDS = {}; |
| for (const m of VIZ.models) { |
| const col = colorFor(m.name); |
| const isStudent = m.name === "student"; |
| let sx = 0, sy = 0; |
| m.points.forEach((p, k) => { |
| const [lp, tp] = project(p); |
| sx += lp; sy += tp; |
| const depth = isStudent ? "near" : (k % 3 === 0 ? "far" : k % 3 === 1 ? "mid" : "near"); |
| const sz = isStudent ? 7 : depth === "near" ? 6 : depth === "mid" ? 4.5 : 3; |
| const op = isStudent ? 1 : depth === "near" ? 1 : depth === "mid" ? 0.8 : 0.55; |
| addDot(layers[depth], lp, tp, sz, col, op); |
| }); |
| if (!isStudent && m.points.length) CENTROIDS[m.name] = [sx / m.points.length / 100, sy / m.points.length / 100]; |
| } |
| } |
|
|
| function placeFallback() { |
| clearLayers(); |
| PROJ = null; |
| CENTROIDS = {}; |
| TEACHERS.forEach((t) => { |
| const c = CENTERS[t.id]; const col = cssv(t.varc); |
| CENTROIDS[t.id] = c; |
| for (let k = 0; k < 11; k++) { |
| const depth = k % 3 === 0 ? "far" : k % 3 === 1 ? "mid" : "near"; |
| const sz = depth === "near" ? 6 : depth === "mid" ? 4.5 : 3; |
| const op = depth === "near" ? 1 : depth === "mid" ? 0.8 : 0.55; |
| addDot(layers[depth], (c[0] + rnd(0.13)) * 100, (c[1] + rnd(0.13)) * 100, sz, col, op); |
| } |
| }); |
| for (let k = 0; k < 9; k++) { |
| addDot(layers.near, (0.5 + rnd(0.22)) * 100, (0.52 + rnd(0.2)) * 100, 7, cssv("--spark"), 1); |
| } |
| } |
|
|
| function placeMap() { |
| if (VIZ && VIZ.models && VIZ.models.length) placeReal(); |
| else placeFallback(); |
| } |
|
|
| function dropProbePoint(point) { |
| let lp, tp; |
| if (PROJ) { [lp, tp] = project([point.x, point.y, point.z]); } |
| else { lp = 50 + rnd(20) * 2; tp = 52 + rnd(18) * 2; } |
| const d = addDot(layers.near, lp, tp, 9, cssv("--text"), 1); |
| d.classList.add("probe"); |
| } |
| function dropProbeByGates(gates) { |
| const id = TEACHERS[topIdx(gates)].id; |
| const c = CENTROIDS[id] || [0.5, 0.5]; |
| const lp = (c[0] * 0.6 + 0.5 * 0.4 + rnd(0.06)) * 100; |
| const tp = (c[1] * 0.6 + 0.52 * 0.4 + rnd(0.06)) * 100; |
| const d = addDot(layers.near, lp, tp, 9, cssv("--text"), 1); |
| d.classList.add("probe"); |
| } |
|
|
| function buildLegend() { |
| legend.innerHTML = ""; |
| [{ label: "student", c: "--spark" }, ...TEACHERS.map((t) => ({ label: t.label, c: t.varc })), { label: "probe", c: "--text" }] |
| .forEach((it) => { |
| const s = document.createElement("div"); s.className = "lg"; s.style.color = cssv(it.c); |
| s.innerHTML = `<span class="s"></span><span style="color:var(--text-dim)">${it.label}</span>`; |
| legend.appendChild(s); |
| }); |
| } |
| buildLegend(); |
| placeMap(); |
| window.addEventListener("resize", () => { clearTimeout(window.__rt); window.__rt = setTimeout(placeMap, 250); }); |
|
|
| |
| const ambient = document.getElementById("ambient"); |
| const converge = document.getElementById("converge"); |
| let sy = 0, ticking = false; |
| function onScroll() { sy = window.scrollY || 0; if (!ticking) { requestAnimationFrame(applyScroll); ticking = true; } } |
| function applyScroll() { |
| if (!reduce) { |
| ambient.style.transform = `translate3d(0, ${sy * 0.18}px, 0)`; |
| converge.style.transform = `translate3d(0, ${sy * -0.04}px, 0)`; |
| } |
| ticking = false; |
| } |
| window.addEventListener("scroll", onScroll, { passive: true }); |
|
|
| let mx = 0, my = 0, cmx = 0, cmy = 0; |
| document.addEventListener("mousemove", (e) => { |
| mx = (e.clientX / window.innerWidth - 0.5); |
| my = (e.clientY / window.innerHeight - 0.5); |
| }); |
| function rafMouse() { |
| cmx += (mx - cmx) * 0.06; cmy += (my - cmy) * 0.06; |
| if (!reduce) { |
| const tn = document.getElementById("tnodes"); |
| const fl = document.getElementById("flows"); |
| if (tn) tn.style.transform = `translate(${cmx * 22}px, ${cmy * 22}px)`; |
| if (fl) fl.style.transform = `translate(${cmx * 10}px, ${cmy * 10}px)`; |
| const r = stage.getBoundingClientRect(); |
| if (r.top < window.innerHeight && r.bottom > 0) { |
| Object.values(layers).forEach((l) => { |
| const dep = parseFloat(l.dataset.depth) * 1; |
| l.style.transform = `translate(${cmx * dep * 900}px, ${cmy * dep * 700}px)`; |
| }); |
| } |
| } |
| requestAnimationFrame(rafMouse); |
| } |
| requestAnimationFrame(rafMouse); |
|
|
| |
| const io = new IntersectionObserver((ents) => { |
| ents.forEach((en) => { if (en.isIntersecting) { en.target.classList.add("in"); io.unobserve(en.target); } }); |
| }, { threshold: 0.18 }); |
| document.querySelectorAll(".reveal").forEach((el) => io.observe(el)); |
|
|
| |
| document.querySelectorAll("[data-scroll]").forEach((b) => { |
| b.addEventListener("click", () => { |
| const el = document.querySelector(b.dataset.scroll); |
| if (el) el.scrollIntoView({ behavior: reduce ? "auto" : "smooth", block: "start" }); |
| }); |
| }); |
|
|
| |
| (async () => { |
| try { |
| client = await Client.connect(location.origin); |
| const res = await client.predict("/viz", {}); |
| VIZ = res.data[0]; |
| const n = (VIZ.teachers && VIZ.teachers.length) || 6; |
| document.getElementById("statTeachers").innerHTML = |
| `<span class="d t"></span>${n} Teachers <b>· 1.5–4B</b>`; |
| if (!VIZ.model_ready) infNote.textContent = "model offline — UI preview only"; |
| placeMap(); |
| } catch (e) { |
| console.error("boot failed", e); |
| infNote.textContent = "backend unreachable — UI preview only"; |
| } |
| })(); |
|
|