mudabench-viewer / viewer.js
mudabench-viewer bot
MuDABench viewer: Corpus (589 PDFs) + Eval (332 Q) static Space
21131b5
Raw
History Blame Contribute Delete
14 kB
/* MuDABench viewer — vanilla JS, two tabs: Corpus + Eval. */
(function () {
"use strict";
var $ = function (id) { return document.getElementById(id); };
function esc(s) {
if (s === null || s === undefined) return "";
return String(s)
.replace(/&/g, "&")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
function fmtVal(v) {
if (v === null || v === undefined) return '<span class="fval-null">null</span>';
if (v === true) return '<span class="fval-bool-true">true</span>';
if (v === false) return '<span class="fval-bool-false">false</span>';
return esc(String(v));
}
function shortId(id) { return id ? String(id).slice(0, 8) : ""; }
var state = {
mode: "corpus",
corpus: [], // all docs
corpusView: [], // filtered docs
corpusIdx: 0,
byId: {}, // id -> corpus doc
questions: [], // all questions
evalView: [], // filtered questions
evalIdx: 0,
};
/* ---------------- data loading ---------------- */
function loadAll() {
return Promise.all([
fetch("corpus_index.json").then(function (r) { return r.json(); }),
fetch("eval.json").then(function (r) { return r.json(); }),
]).then(function (res) {
state.corpus = res[0] || [];
state.questions = res[1] || [];
state.corpus.forEach(function (d) { state.byId[d.id] = d; });
});
}
/* ---------------- corpus tab ---------------- */
function populateCorpusFilters() {
var types = {}, years = {};
state.corpus.forEach(function (d) {
if (d.doctype) types[d.doctype] = true;
if (d.year !== null && d.year !== undefined && d.year !== "") years[d.year] = true;
});
var tf = $("docTypeFilter"), yf = $("yearFilter");
Object.keys(types).sort().forEach(function (t) {
var o = document.createElement("option"); o.value = t; o.textContent = t; tf.appendChild(o);
});
Object.keys(years).sort().forEach(function (y) {
var o = document.createElement("option"); o.value = y; o.textContent = y; yf.appendChild(o);
});
}
function filterCorpus() {
var q = $("corpusFilter").value.trim().toLowerCase();
var typ = $("docTypeFilter").value;
var yr = $("yearFilter").value;
state.corpusView = state.corpus.filter(function (d) {
if (typ && d.doctype !== typ) return false;
if (yr && String(d.year) !== yr) return false;
if (q) {
var hay = (d.title + " " + (d.symbol || "") + " " + (d.id || "")).toLowerCase();
if (hay.indexOf(q) === -1) return false;
}
return true;
});
if (state.corpusIdx >= state.corpusView.length) state.corpusIdx = 0;
rebuildDocSelect();
renderCorpus();
}
function rebuildDocSelect() {
var sel = $("docSelect");
sel.innerHTML = "";
state.corpusView.forEach(function (d, i) {
var o = document.createElement("option");
o.value = String(i);
o.textContent = d.title + " · " + shortId(d.id);
sel.appendChild(o);
});
sel.value = String(state.corpusIdx);
}
function renderCorpus() {
var meta = $("docMetaCard");
var frame = $("pdfFrame");
if (!state.corpusView.length) {
meta.innerHTML = '<div class="empty">No documents match the filter.</div>';
frame.removeAttribute("src");
$("corpusCounter").textContent = "0 / 0";
return;
}
var d = state.corpusView[state.corpusIdx];
$("docSelect").value = String(state.corpusIdx);
$("corpusCounter").textContent = (state.corpusIdx + 1) + " / " + state.corpusView.length;
var pills = [];
if (d.symbol) pills.push('<span class="meta-pill">Symbol: <b>' + esc(d.symbol) + "</b></span>");
if (d.year !== null && d.year !== undefined && d.year !== "") pills.push('<span class="meta-pill">Year: <b>' + esc(d.year) + "</b></span>");
if (d.doctype) pills.push('<span class="meta-pill">Type: <b>' + esc(d.doctype) + "</b></span>");
pills.push('<span class="meta-pill">Cited by <b>' + (d.n_refs || 0) + "</b> question(s)</span>");
pills.push('<span class="meta-pill mono">' + esc(d.id) + "</span>");
var link = '<a class="doc-link" href="' + esc(d.pdf) + '" target="_blank" rel="noopener">open PDF ↗</a>';
var fieldsHtml = "";
if (d.fields && d.fields.length) {
var rows = d.fields.map(function (f) {
var vals = (f.values || []).map(fmtVal).join(", ");
return "<tr><td class=\"fname\">" + esc(f.name) + "</td>" +
"<td class=\"fval\">" + (vals || "") + "</td>" +
"<td class=\"fdesc\">" + esc(f.desc || "") + "</td></tr>";
}).join("");
fieldsHtml =
'<table class="fields-table"><thead><tr>' +
"<th>Field</th><th>Value(s)</th><th>Description</th>" +
"</tr></thead><tbody>" + rows + "</tbody></table>";
}
var refsHtml = "";
if (d.referenced_by && d.referenced_by.length) {
var refPills = d.referenced_by.map(function (r) {
return '<span class="ref-pill" data-qid="' + esc(r.qid) + '" data-ds="' + esc(r.dataset) +
'">' + esc(shortId(r.qid)) + ' <span class="ds">' + esc(r.dataset) + "</span></span>";
}).join("");
refsHtml =
'<details class="refs-wrap"><summary>Referenced by ' + d.referenced_by.length +
" question(s)</summary><div class=\"ref-pills\">" + refPills + "</div></details>";
}
meta.innerHTML =
"<h2>" + esc(d.title) + "</h2>" +
'<div class="meta-grid">' + pills.join("") + link + "</div>" +
fieldsHtml + refsHtml;
// wire ref pills -> jump to that question in the Eval tab
Array.prototype.forEach.call(meta.querySelectorAll(".ref-pill"), function (el) {
el.addEventListener("click", function () {
setMode("eval");
selectQuestion(this.getAttribute("data-ds"), this.getAttribute("data-qid"));
});
});
frame.src = d.pdf;
}
function selectDocById(id) {
// reset corpus filters so the target doc is guaranteed visible
$("corpusFilter").value = "";
$("docTypeFilter").value = "";
$("yearFilter").value = "";
state.corpusView = state.corpus.slice();
var idx = 0;
for (var i = 0; i < state.corpusView.length; i++) {
if (state.corpusView[i].id === id) { idx = i; break; }
}
state.corpusIdx = idx;
rebuildDocSelect();
renderCorpus();
}
/* ---------------- eval tab ---------------- */
function filterEval() {
var q = $("evalSearch").value.trim().toLowerCase();
var ds = $("datasetFilter").value;
state.evalView = state.questions.filter(function (x) {
if (ds && x.dataset !== ds) return false;
if (q) {
var docTitles = (x.docs || []).map(function (d) { return d.title + " " + (d.symbol || ""); }).join(" ");
var hay = [x.question, x.final_answer, x.qid, docTitles].join(" ").toLowerCase();
if (hay.indexOf(q) === -1) return false;
}
return true;
});
if (state.evalIdx >= state.evalView.length) state.evalIdx = 0;
rebuildQSelect();
renderEval();
}
function rebuildQSelect() {
var sel = $("qSelect");
sel.innerHTML = "";
state.evalView.forEach(function (x, i) {
var o = document.createElement("option");
o.value = String(i);
var q = (x.question || "").replace(/\s+/g, " ");
if (q.length > 70) q = q.slice(0, 70) + "…";
o.textContent = "[" + x.dataset + "] " + q;
sel.appendChild(o);
});
sel.value = String(state.evalIdx);
}
function field(lbl, valHtml) {
return '<div class="field"><div class="lbl">' + esc(lbl) + '</div><div class="val">' + valHtml + "</div></div>";
}
function renderEval() {
var card = $("evalCard");
if (!state.evalView.length) {
card.innerHTML = '<div class="empty">No questions match the search.</div>';
$("evalCounter").textContent = "0 / 0";
return;
}
var x = state.evalView[state.evalIdx];
$("qSelect").value = String(state.evalIdx);
$("evalCounter").textContent = (state.evalIdx + 1) + " / " + state.evalView.length;
var badges =
'<span class="badge id">' + esc(x.qid) + "</span>" +
'<span class="badge ds-' + esc(x.dataset) + '">' + esc(x.dataset) + "</span>";
// supporting facts (source_answer)
var facts = x.source_answer || [];
var factsHtml = facts.length
? '<ol class="facts-list">' + facts.map(function (f) { return "<li>" + esc(f) + "</li>"; }).join("") + "</ol>"
: '<span style="color:var(--muted)">none</span>';
// supporting documents
var docs = x.docs || [];
var docsHtml = docs.map(function (d) {
var fields = (d.fields || []).map(function (f) {
return '<div class="doc-field"><span class="fn">' + esc(f.name) + '</span>' +
'<span class="fv">' + fmtVal(f.value) + "</span>" +
(f.desc ? '<span class="fd">' + esc(f.desc) + "</span>" : "") + "</div>";
}).join("");
return '<div class="doc-item">' +
'<div class="doc-item-head">' +
'<a class="doclink" data-doc-id="' + esc(d.id) + '">' + esc(d.title) + " ↗</a>" +
'<span class="doc-id">' + esc(d.id) + "</span>" +
"</div>" +
(fields ? '<div class="doc-fields">' + fields + "</div>" : "") +
"</div>";
}).join("");
card.innerHTML =
'<div class="badges">' + badges + "</div>" +
"<h2>" + esc(x.question) + "</h2>" +
'<div class="answer-banner"><div class="lbl">Final answer (gold)</div><div class="val">' +
esc(x.final_answer) + "</div></div>" +
field("Supporting facts (source_answer)", factsHtml) +
field("Supporting documents (" + docs.length + ")", docsHtml ||
'<span style="color:var(--muted)">none</span>');
// wire doc links -> jump to Corpus tab
Array.prototype.forEach.call(card.querySelectorAll("a.doclink[data-doc-id]"), function (el) {
el.addEventListener("click", function () {
setMode("corpus");
selectDocById(this.getAttribute("data-doc-id"));
});
});
}
function selectQuestion(dataset, qid) {
$("evalSearch").value = "";
$("datasetFilter").value = "";
state.evalView = state.questions.slice();
var idx = 0;
for (var i = 0; i < state.evalView.length; i++) {
if (state.evalView[i].qid === qid && state.evalView[i].dataset === dataset) { idx = i; break; }
}
state.evalIdx = idx;
rebuildQSelect();
renderEval();
}
/* ---------------- mode switching ---------------- */
function setMode(mode) {
state.mode = mode;
$("modeCorpusBtn").classList.toggle("active", mode === "corpus");
$("modeEvalBtn").classList.toggle("active", mode === "eval");
$("corpusControls").style.display = mode === "corpus" ? "" : "none";
$("evalControls").style.display = mode === "eval" ? "" : "none";
$("corpusView").style.display = mode === "corpus" ? "flex" : "none";
$("evalView").style.display = mode === "eval" ? "block" : "none";
}
/* ---------------- wiring ---------------- */
function wire() {
$("modeCorpusBtn").addEventListener("click", function () { setMode("corpus"); });
$("modeEvalBtn").addEventListener("click", function () { setMode("eval"); });
// corpus
$("corpusFilter").addEventListener("input", filterCorpus);
$("docTypeFilter").addEventListener("change", filterCorpus);
$("yearFilter").addEventListener("change", filterCorpus);
$("docSelect").addEventListener("change", function () {
state.corpusIdx = parseInt(this.value, 10) || 0;
renderCorpus();
});
$("corpusPrevBtn").addEventListener("click", function () {
if (state.corpusIdx > 0) { state.corpusIdx--; renderCorpus(); }
});
$("corpusNextBtn").addEventListener("click", function () {
if (state.corpusIdx < state.corpusView.length - 1) { state.corpusIdx++; renderCorpus(); }
});
// eval
$("evalSearchBtn").addEventListener("click", filterEval);
$("evalSearch").addEventListener("keydown", function (e) { if (e.key === "Enter") filterEval(); });
$("evalClearBtn").addEventListener("click", function () {
$("evalSearch").value = ""; $("datasetFilter").value = ""; filterEval();
});
$("datasetFilter").addEventListener("change", filterEval);
$("qSelect").addEventListener("change", function () {
state.evalIdx = parseInt(this.value, 10) || 0;
renderEval();
});
$("evalPrevBtn").addEventListener("click", function () {
if (state.evalIdx > 0) { state.evalIdx--; renderEval(); }
});
$("evalNextBtn").addEventListener("click", function () {
if (state.evalIdx < state.evalView.length - 1) { state.evalIdx++; renderEval(); }
});
// keyboard arrows
document.addEventListener("keydown", function (e) {
var tag = (e.target.tagName || "").toLowerCase();
if (tag === "input" || tag === "select" || tag === "textarea") return;
if (e.key === "ArrowLeft") {
if (state.mode === "corpus") $("corpusPrevBtn").click();
else $("evalPrevBtn").click();
} else if (e.key === "ArrowRight") {
if (state.mode === "corpus") $("corpusNextBtn").click();
else $("evalNextBtn").click();
}
});
}
/* ---------------- boot ---------------- */
loadAll().then(function () {
$("loading").style.display = "none";
$("sidebarFooter").innerHTML =
state.corpus.length + " documents · " + state.questions.length + " questions<br>" +
'<a href="https://github.com/Zhanli-Li/MuDABench" target="_blank" rel="noopener">GitHub</a> · ' +
'<a href="https://huggingface.co/datasets/Zhanli-Li/MuDABench" target="_blank" rel="noopener">HF dataset</a> · ' +
'<a href="https://arxiv.org/abs/2604.22239" target="_blank" rel="noopener">arXiv</a>';
populateCorpusFilters();
wire();
setMode("corpus");
filterCorpus();
filterEval();
}).catch(function (err) {
$("loading").textContent = "Failed to load data: " + err;
});
})();