// Store full texts in a module-level array — avoids HTML attribute escaping bugs
let _p2FullTexts = [];
function scoreBar(val, color) {
const pct = (val * 100).toFixed(0);
return `
`;
}
function rankArrow(delta) {
if (delta > 0) return `▲${delta}`;
if (delta < 0) return `▼${Math.abs(delta)}`;
return `—`;
}
function rowColor(rerank, passed) {
if (!passed) return "rgba(239,68,68,0.06)";
if (rerank >= 0.7) return "rgba(16,185,129,0.08)";
if (rerank >= 0.4) return "rgba(245,158,11,0.07)";
return "rgba(99,102,241,0.05)";
}
function borderColor(rerank, passed) {
if (!passed) return "#fca5a5";
if (rerank >= 0.7) return "#6ee7b7";
if (rerank >= 0.4) return "#fcd34d";
return "#c7d2fe";
}
function escHtml(s) {
return String(s)
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
function renderRetrievalTable(rows) {
const container = document.getElementById("retrieval-panel");
if (!rows || !rows.length) { container.innerHTML = ""; return; }
_p2FullTexts = rows.map(r => r.text_full || "");
const topRow = [...rows].sort((a, b) => b.rerank_score - a.rerank_score)[0];
const tableRows = rows.map((r, i) => {
const isTop = r.idx === topRow.idx;
const bg = rowColor(r.rerank_score, r.passed_threshold);
const border = borderColor(r.rerank_score, r.passed_threshold);
const dropped = !r.passed_threshold ? `DROPPED` : "";
const winner = isTop ? `★ top` : "";
const preview = escHtml((r.text_preview || "").trim());
return `
| #${r.pre_rank + 1} |
#${r.post_rank + 1} ${winner}${dropped} |
${preview}
|
${scoreBar(r.vector_score, "#0ea5e9")} |
${scoreBar(r.bm25_score, "#10b981")} |
${scoreBar(r.hybrid_score, "#f59e0b")} |
${scoreBar(r.rerank_score, r.passed_threshold ? "#6366f1" : "#ef4444")} |
${rankArrow(r.rank_delta)} |
`;
}).join("");
container.innerHTML = `
📊 Retrieval Comparison
Vector
BM25
Hybrid
Reranker
|
passed
dropped
| Original Rank |
Rerank |
Chunk |
Vector |
BM25 |
Hybrid |
Reranker |
Δ |
${tableRows}
`;
container.querySelectorAll(".p2-preview-cell").forEach(cell => {
const tooltip = cell.querySelector(".p2-tooltip");
const idx = parseInt(cell.dataset.idx);
tooltip.textContent = _p2FullTexts[idx] || "";
cell.addEventListener("mouseenter", () => tooltip.classList.add("visible"));
cell.addEventListener("mouseleave", () => tooltip.classList.remove("visible"));
cell.addEventListener("mousemove", e => {
const spaceBelow = window.innerHeight - e.clientY;
tooltip.style.left = "0px";
if (spaceBelow < 220) {
tooltip.style.bottom = "100%";
tooltip.style.top = "auto";
} else {
tooltip.style.top = "100%";
tooltip.style.bottom = "auto";
}
});
});
}