matching traces · click to open · matched span outlined
`;
renderHits();
}
document.addEventListener("click", (e) => {
const c = e.target.closest(".chip[data-i]");
if (c) run(SAMPLES[+c.dataset.i].pat);
if (e.target.id === "noisetog") {
HIDE_NOISE = !HIDE_NOISE;
document.body.classList.toggle("hide-noise", HIDE_NOISE);
}
});
$("q").addEventListener("keydown", (e) => { if (e.key === "Enter") { clearTimeout(QTIMER); run($("q").value); } });
// Live-as-you-type: debounce, and only fire on a valid regex so partial patterns
// (e.g. an open paren mid-type) never clobber the last results with an error.
$("q").addEventListener("input", () => {
clearTimeout(QTIMER);
const v = $("q").value.trim();
if (!v) return;
try { new RegExp(v); } catch { return; }
QTIMER = setTimeout(() => run(v), 250);
});
$("ds").addEventListener("change", () => { setSource($("ds").value); run($("q").value || SAMPLES[0].pat); });
// Free-text: query any HF dataset id (live-ingested on the server, slower first load).
$("dsfree").addEventListener("keydown", (e) => {
if (e.key !== "Enter") return;
const v = e.target.value.trim();
if (!v) return;
if (![...$("ds").options].some((o) => o.value === v)) {
const o = document.createElement("option");
o.value = v; o.textContent = v + " · live"; $("ds").appendChild(o);
}
$("ds").value = v; setSource(v); e.target.value = "";
run($("q").value || SAMPLES[0].pat);
});
// Comparator.
// A trail-as-thread: collapse think/other runs into fold lines, render signal
// atoms as labeled colored steps. The same skeleton the spine() bars draw,
// expanded into a readable column. This is how we show a "conversation":
// procgrep keeps the action structure, not the raw text.
function threadHTML(atoms, color) {
const out = [];
let i = 0;
while (i < atoms.length) {
const a = atoms[i];
if (a === "think" || a === "other") {
let n = 0;
while (i < atoms.length && (atoms[i] === "think" || atoms[i] === "other")) { n++; i++; }
out.push(`
⋯ ${n} reasoning ${n > 1 ? "steps" : "step"}
`);
} else {
let n = 1;
while (i + n < atoms.length && atoms[i + n] === a) n++;
out.push(`
${i + 1}${a}${n > 1 ? " ×" + n : ""}
`);
i += n;
}
}
return out.join("");
}
// A contiguous trajectory barcode: one cell per atom, reasoning pale and
// actions vivid, so trail length is proportional and behavior reads as color
// density. Used by the comparator (vs the collapsing spine() the query uses).
// hi (optional) is an [start,end] atom range; cells outside it dim so the
// matched span pops. Noise cells get class nz so "hide think/other" can drop them.
function barcode(atoms, color, hi) {
return '' + atoms.map((a, idx) => {
const noise = a === "think" || a === "other";
const dim = hi && (idx < hi[0] || idx > hi[1]) ? ";opacity:.3" : "";
return ``;
}).join("") + "";
}
// Open any trace as a thread sheet. Shared by the comparator trails and the
// query hits, so "select a trace to inspect" works the same everywhere.
// Present-tense phrase per atom, for the terminal-style replay lines.
const ATOM_PHRASE = {
search_repo: "grepped the repo", read_file: "opened a file", edit: "edited code",
create_file: "created a file", delete_file: "deleted a file", run_test: "ran the tests",
submit: "submitted the patch", localize: "localized the fault", error: "hit an error",
think: "reasoning", other: "other",
};
// Active replay state for the open sheet. One sheet at a time.
let RP = null;
function rpMini(atoms, n, color) {
return '' + atoms.map((a, idx) => {
const noise = a === "think" || a === "other";
return ``;
}).join("") + "";
}
function rpTerminal(atoms, n, color) {
const out = [];
let i = 0;
while (i <= n && i < atoms.length) {
const a = atoms[i];
if (a === "think" || a === "other") {
let k = 0;
while (i <= n && i < atoms.length && (atoms[i] === "think" || atoms[i] === "other")) { k++; i++; }
out.push(`
┄ ${k} reasoning ${k > 1 ? "steps" : "step"}
`);
} else {
const now = i === n ? " rpnow" : "";
out.push(`
${i + 1}${ATOM_PHRASE[a] || a}${now ? ' ▍' : ""}
`);
i++;
}
}
return out.join("");
}
function rpFire() {
if (!RP) return;
const pat = $("rp-q").value.trim(), el = $("rp-fire");
if (!pat) { el.textContent = ""; return; }
let rx; try { rx = new RegExp(pat); } catch { el.innerHTML = '…'; return; }
const sp = RP.atoms.slice(0, RP.n + 1).join(" ") + " ";
const m = rx.exec(sp);
el.innerHTML = m
? `● fired at step ${sp.slice(0, m.index).split(" ").length}`
: 'no match yet';
}
function rpDraw() {
$("rp-mini").innerHTML = rpMini(RP.atoms, RP.n, RP.color);
$("rp-term").innerHTML = rpTerminal(RP.atoms, RP.n, RP.color);
$("rp-seek").value = RP.n;
$("rp-step").textContent = `${RP.n + 1} / ${RP.max + 1}`;
rpFire();
const tm = $("rp-term"); if (tm) tm.scrollTop = tm.scrollHeight;
}
function rpPause() {
if (!RP) return;
RP.playing = false;
if (RP.timer) { clearInterval(RP.timer); RP.timer = null; }
const b = $("rp-play"); if (b) b.textContent = "▶ play";
}
function rpToggle() {
if (!RP) return;
if (RP.playing) { rpPause(); return; }
if (RP.n >= RP.max) RP.n = 0;
RP.playing = true; $("rp-play").textContent = "❚❚ pause";
RP.timer = setInterval(() => { if (RP.n < RP.max) { RP.n++; rpDraw(); } else { rpPause(); } }, RP.speed);
}
function rpSeek(v) { rpPause(); RP.n = +v; rpDraw(); }
function rpSpeed(ms) {
if (ms === 0) { rpPause(); RP.n = RP.max; rpDraw(); return; } // instant: jump to end
const was = RP.playing; RP.speed = ms; rpPause(); if (was) rpToggle();
}
function rpClose() { rpPause(); RP = null; const s = document.querySelector(".sheet"); if (s) s.remove(); }
function openTraceData(t, ds, color) {
rpPause();
const src = `https://huggingface.co/datasets/${ds}`;
const prob = t.task || t.trace_id || "";
const oc = t.outcome ? ` ${t.outcome}` : "";
const max = t.atoms.length - 1;
RP = { atoms: t.atoms, color, n: max, max, playing: false, timer: null, speed: 380 };
const sheet = document.createElement("div");
sheet.className = "sheet";
sheet.onclick = (e) => { if (e.target === sheet) rpClose(); };
sheet.innerHTML =
`
${t.steps ?? t.atoms.length} steps${t.cot_tokens ? ` · ~${fmtTok(t.cot_tokens)} reasoning tokens` : ""} · structural trail, no raw text stored · press play to replay
`;
document.body.appendChild(sheet);
rpDraw();
}
function openTrace(side, i) {
const r = CMP.data; if (!r) return;
const ds = CMP.axis === "eval" ? (side === "left" ? CMP.left : CMP.right) : CMP.ds;
openTraceData(r[side].trails[i], ds, r.atom_color);
}
function openQHit(i) { openTraceData(QHITS[i], $("ds").value, QCOLOR); }
// Render the matched-trace list with a "show more" control so the full match
// set is browsable, not just the first server page.
function renderHits() {
const f = ($("qfilter") && $("qfilter").value || "").trim().toLowerCase();
const s = ($("qsort") && $("qsort").value) || "found";
const rows = QHITS.map((h, i) => [h, i]).filter(([h]) =>
!f || (h.task || h.trace_id || "").toLowerCase().includes(f));
const len = (h) => h.steps ?? h.atoms.length;
const prob = (h) => h.task || h.trace_id || "";
if (s === "len-desc") rows.sort((a, b) => len(b[0]) - len(a[0]));
else if (s === "len-asc") rows.sort((a, b) => len(a[0]) - len(b[0]));
else if (s === "model") rows.sort((a, b) => prettyModel(a[0].model).localeCompare(prettyModel(b[0].model)));
else if (s === "outcome") rows.sort((a, b) => (a[0].outcome || "~").localeCompare(b[0].outcome || "~"));
else if (s === "prob") rows.sort((a, b) => prob(a[0]).localeCompare(prob(b[0])));
$("qhits").innerHTML = rows.map(([h, i]) => {
const prob = h.task || h.trace_id || "";
const oc = h.outcome ? `${h.outcome}` : "";
return `