// Constants & Configuration
const MODEL_COLORS = {
"XGBoost": "#f59e0b",
"LightGBM": "#10b981",
"CatBoost": "#6366f1",
"TabPFN": "#3b82f6",
"SAP RPT-1 OSS": "#ec4899",
"Voting Ensemble": "#fbbf24",
"Stacking Ensemble": "#a78bfa",
};
const MODEL_EMOJIS = {
"XGBoost": "๐ก",
"LightGBM": "๐ข",
"CatBoost": "๐ฃ",
"TabPFN": "๐ฆ",
"SAP RPT-1 OSS": "๐ฉท",
"Voting Ensemble": "๐",
"Stacking Ensemble": "โจ",
};
const ENSEMBLE_NAMES = ["Voting Ensemble", "Stacking Ensemble"];
// DOM Elements
const dropZone = document.getElementById("drop-zone");
const fileInput = document.getElementById("file-input");
const uploadError = document.getElementById("upload-error");
const uploadSection = document.getElementById("upload-section");
const previewSection = document.getElementById("preview-section");
const previewMeta = document.getElementById("preview-meta");
const targetSelect = document.getElementById("target-select");
const previewTable = document.getElementById("preview-table");
const changeFileBtn = document.getElementById("change-file-btn");
const runBtn = document.getElementById("run-btn");
const loadingSection = document.getElementById("loading-section");
const resultsSection = document.getElementById("results-section");
const resetBtn = document.getElementById("reset-btn");
const exportCsvBtn = document.getElementById("export-csv-btn");
const exportJsonBtn = document.getElementById("export-json-btn");
const resumeSection = document.getElementById("resume-section");
const resumeFilename = document.getElementById("resume-filename");
const resumeClearBtn = document.getElementById("resume-clear-btn");
const resumeGoBtn = document.getElementById("resume-go-btn");
let currentFile = null;
let chartInstances = [];
// Drag & Drop Handling
if (dropZone) {
dropZone.addEventListener("click", () => fileInput.click());
dropZone.addEventListener("keydown", e => { if (e.key === "Enter" || e.key === " ") fileInput.click(); });
dropZone.addEventListener("dragover", e => { e.preventDefault(); dropZone.classList.add("drag-over"); });
dropZone.addEventListener("dragleave", () => dropZone.classList.remove("drag-over"));
dropZone.addEventListener("drop", e => {
e.preventDefault();
dropZone.classList.remove("drag-over");
const f = e.dataTransfer.files[0];
if (f) handleFile(f);
});
}
if (fileInput) {
fileInput.addEventListener("change", () => {
if (fileInput.files[0]) handleFile(fileInput.files[0]);
});
}
if (changeFileBtn) changeFileBtn.addEventListener("click", resetToUpload);
if (resetBtn) resetBtn.addEventListener("click", resetToUpload);
if (exportCsvBtn) exportCsvBtn.addEventListener("click", () => {
const data = JSON.parse(sessionStorage.getItem("lastResults"));
if (data) exportToCSV(data);
});
if (exportJsonBtn) exportJsonBtn.addEventListener("click", () => {
const data = JSON.parse(sessionStorage.getItem("lastResults"));
if (data) exportToJSON(data);
});
if (resumeClearBtn) resumeClearBtn.addEventListener("click", () => {
sessionStorage.removeItem("lastResults");
sessionStorage.removeItem("lastFileName");
window.location.reload();
});
if (resumeGoBtn) resumeGoBtn.addEventListener("click", () => {
window.location.href = "/static/arena.html";
});
// File selection and preview initialization
async function handleFile(file) {
uploadError.hidden = true;
if (!file.name.endsWith(".csv")) {
showError("Please upload a .csv file.");
return;
}
const MAX_MB = 5;
if (file.size > MAX_MB * 1024 * 1024) {
showError(`File is too large (${(file.size / 1048576).toFixed(1)} MB). Maximum is ${MAX_MB} MB.`);
return;
}
currentFile = file;
const fd = new FormData();
fd.append("file", file);
try {
const res = await fetch("/preview", { method: "POST", body: fd });
if (!res.ok) {
const err = await res.json();
showError(err.detail || "Failed to read CSV.");
return;
}
const data = await res.json();
renderPreview(data, file);
} catch (e) {
showError("Network error: " + e.message);
}
}
function renderPreview(data, file) {
// Meta badges
previewMeta.innerHTML = `
๐ ${file.name}
${data.n_rows.toLocaleString()} rows
${data.n_cols} columns
`;
// Target column selector
targetSelect.innerHTML = "";
data.columns.forEach(col => {
const opt = document.createElement("option");
opt.value = col;
opt.textContent = col;
if (col === data.default_target) opt.selected = true;
targetSelect.appendChild(opt);
});
// Preview table
const cols = data.columns;
let thead = "" + cols.map(c => `| ${esc(c)} | `).join("") + "
";
let tbody = "
" + data.preview.map(row =>
"" + cols.map(c => `| ${esc(String(row[c] ?? ""))} | `).join("") + "
"
).join("") + "";
previewTable.innerHTML = thead + tbody;
// Highlight target column on select change
targetSelect.addEventListener("change", () => highlightTarget(targetSelect.value, cols));
uploadSection.hidden = true;
previewSection.hidden = false;
}
function highlightTarget(targetCol, cols) {
previewTable.querySelectorAll("th, td").forEach(el => el.classList.remove("target-col"));
const idx = cols.indexOf(targetCol);
if (idx < 0) return;
previewTable.querySelectorAll("tr").forEach(row => {
const cells = row.querySelectorAll("th, td");
if (cells[idx]) cells[idx].classList.add("target-col");
});
}
// Execute benchmarking suite
if (runBtn) {
runBtn.addEventListener("click", async () => {
if (!currentFile) return;
previewSection.hidden = true;
loadingSection.hidden = false;
// Animate loader steps
const steps = ["step-xgb", "step-lgb", "step-cat", "step-tabpfn", "step-sap", "step-vote", "step-stack"];
const delays = [0, 150, 300, 450, 600, 750, 900];
let stepIdx = 0;
const stepTimer = setInterval(() => {
if (stepIdx > 0) {
document.getElementById(steps[stepIdx - 1])?.classList.remove("active");
document.getElementById(steps[stepIdx - 1])?.classList.add("done");
}
if (stepIdx < steps.length) {
document.getElementById(steps[stepIdx])?.classList.add("active");
stepIdx++;
} else {
clearInterval(stepTimer);
}
}, 1400);
const fd = new FormData();
fd.append("file", currentFile);
fd.append("target_col", targetSelect.value);
try {
const res = await fetch("/benchmark", { method: "POST", body: fd });
if (!res.ok) {
const err = await res.json();
clearInterval(stepTimer);
loadingSection.hidden = true;
previewSection.hidden = false;
showError(err.detail || "Benchmarking failed.");
return;
}
const data = await res.json();
clearInterval(stepTimer);
loadingSection.hidden = true;
sessionStorage.setItem("lastResults", JSON.stringify(data));
sessionStorage.setItem("lastFileName", currentFile.name);
window.location.href = "/static/arena.html";
} catch (e) {
clearInterval(stepTimer);
loadingSection.hidden = true;
previewSection.hidden = false;
showError("Network error: " + e.message);
}
});
}
// Visualization of benchmarking results
function renderResults(data) {
const { dataset_info, task, results, recommendation, n_folds } = data;
const isCLF = task === "classification";
const primaryKey = isCLF ? "roc_auc" : "r2";
const primaryLabel = isCLF ? "ROC-AUC" : "Rยฒ";
const fileName = sessionStorage.getItem("lastFileName") || "Dataset";
// โโ Info bar
const taskBadge = isCLF
? `๐ท Classification`
: `๐ Regression`;
document.getElementById("info-bar").innerHTML = `
๐ ${esc(fileName)}
${taskBadge}
${dataset_info.n_samples.toLocaleString()} samples
${dataset_info.n_features} features
Target: ${esc(dataset_info.target_col)}
${isCLF ? `${dataset_info.n_classes} classes` : ""}
${n_folds}-Fold CV
`;
// โโ KPI cards
const kpiGrid = document.getElementById("kpi-grid");
kpiGrid.innerHTML = "";
const validModels = Object.entries(results).filter(([, v]) => !v.error);
const bestEntry = validModels.reduce((best, [name, v]) =>
(v.mean[primaryKey] || 0) > (best[1].mean[primaryKey] || 0) ? [name, v] : best
, validModels[0]);
const kpis = [
{
label: "Best Model",
value: bestEntry[0],
sub: `${primaryLabel}: ${fmt(bestEntry[1].mean[primaryKey])}`,
color: MODEL_COLORS[bestEntry[0]],
},
{
label: `Best ${primaryLabel}`,
value: fmt(bestEntry[1].mean[primaryKey]),
sub: `ยฑ ${fmt(bestEntry[1].std[primaryKey])} std`,
color: "#818cf8",
},
{
label: "Models Evaluated",
value: validModels.length,
sub: `${n_folds}-fold cross-validation`,
color: "#10b981",
},
{
label: "Dataset Size",
value: dataset_info.n_samples.toLocaleString(),
sub: `${dataset_info.n_features} features ยท ${isCLF ? dataset_info.n_classes + " classes" : "regression"}`,
color: "#f59e0b",
},
];
kpis.forEach(k => {
const card = document.createElement("div");
card.className = "kpi-card";
card.style.setProperty("--accent-bar", k.color);
card.innerHTML = `
${k.label}
${esc(String(k.value))}
${k.sub}
`;
kpiGrid.appendChild(card);
});
// โโ Legend
const legendEl = document.getElementById("legend");
legendEl.innerHTML = Object.entries(MODEL_COLORS).map(([name, color]) =>
``
).join("");
// โโ Charts
chartInstances.forEach(c => c.destroy());
chartInstances = [];
const chartsGrid = document.getElementById("charts-grid");
chartsGrid.innerHTML = "";
const metricsToChart = isCLF
? [["roc_auc", "ROC-AUC"], ["accuracy", "Accuracy"], ["f1_macro", "F1-Macro"]]
: [["r2", "Rยฒ"], ["mae", "MAE"], ["rmse", "RMSE"]];
metricsToChart.forEach(([key, label]) => {
const modelNames = Object.keys(results).filter(n => !results[n].error && results[n].mean[key] != null);
if (!modelNames.length) return;
const vals = modelNames.map(n => roundN(results[n].mean[key], 4));
const errs = modelNames.map(n => roundN(results[n].std[key] || 0, 4));
const bgs = modelNames.map(n => (MODEL_COLORS[n] || "#888") + "cc");
const bords = modelNames.map(n => MODEL_COLORS[n] || "#888");
const isErrorMetric = ["mae", "rmse", "log_loss"].includes(key.toLowerCase());
const highQual = isErrorMetric ? "poor" : "excellent";
const lowQual = isErrorMetric ? "excellent" : "poor";
const card = document.createElement("div");
card.className = "chart-card";
const canvasId = `chart-${key}`;
card.innerHTML = `
${label}
${label} (mean ยฑ std over ${n_folds} folds)
High ${label} = ${highQual}
Low ${label} = ${lowQual}
`;
chartsGrid.appendChild(card);
const minVal = Math.min(...vals);
const maxVal = Math.max(...vals);
const pad = Math.max((maxVal - minVal) * 0.15, 0.02);
const inst = new Chart(document.getElementById(canvasId), {
type: "bar",
data: {
labels: modelNames,
datasets: [{
label,
data: vals,
backgroundColor: bgs,
borderColor: bords,
borderWidth: 2,
borderRadius: 8,
}],
},
options: {
responsive: true,
plugins: {
legend: { display: false },
tooltip: {
callbacks: {
label: ctx => `${label}: ${ctx.parsed.y.toFixed(4)} ยฑ ${errs[ctx.dataIndex].toFixed(4)}`,
},
},
},
scales: {
y: {
min: Math.max(key === "roc_auc" || key === "accuracy" ? 0 : -Infinity, minVal - pad),
max: key === "roc_auc" || key === "accuracy" ? Math.min(1, maxVal + pad) : maxVal + pad,
grid: { color: "rgba(100, 116, 139, 0.1)" },
ticks: { color: "rgba(100, 116, 139, 0.8)", font: { size: 11 } },
},
x: {
grid: { display: false },
ticks: { color: "rgba(100, 116, 139, 0.8)", font: { size: 12 } },
},
},
},
});
chartInstances.push(inst);
});
// โโ Full table
const thead = document.getElementById("results-thead");
const tbody = document.getElementById("results-tbody");
const allMetrics = isCLF
? ["accuracy", "f1_macro", "roc_auc", "log_loss", "fit_time"]
: ["r2", "mae", "rmse", "fit_time"];
const metricLabels = isCLF
? ["Accuracy", "F1-Macro", "ROC-AUC", "Log Loss", "Fit Time"]
: ["Rยฒ", "MAE", "RMSE", "Fit Time"];
thead.innerHTML = "| Model | " + metricLabels.map(l => `${l} | `).join("") + "
";
tbody.innerHTML = Object.entries(results).map(([name, d]) => {
if (d.error) {
const errText = d.error.startsWith("Error:") ? d.error : `Error: ${d.error}`;
return `| ${name} | ${esc(errText)} |
`;
}
const cells = allMetrics.map(k => {
const v = d.mean[k];
if (v == null) return `โ | `;
const isTime = k === "fit_time";
if (isTime) return `${v.toFixed(3)}s | `;
const cls = scoreClass(v, k, task);
return `${v.toFixed(4)} ยฑ${(d.std[k]||0).toFixed(3)} | `;
}).join("");
return `| ${name} | ${cells}
`;
}).join("");
// โโ Recommendations
const recGrid = document.getElementById("recommendation-grid");
recGrid.innerHTML = "";
const recs = recommendation.recommendations || {};
const recDefs = [
{ key: "best_overall", label: "๐ Best Overall", winner: true },
{ key: "production", label: "๐ Production Ready", winner: false },
{ key: "best_accuracy", label: "๐ฏ Highest Accuracy", winner: false },
{ key: "best_speed", label: "โก Fastest Training", winner: false },
{ key: "best_consistency", label: "๐ก Most Consistent", winner: false },
];
recDefs.forEach(({ key, label, winner }) => {
const rec = recs[key];
if (!rec) return;
const color = MODEL_COLORS[rec.model] || "#888";
const score = rec.score != null
? `${recommendation.primary_metric}: ${typeof rec.score === "number" ? rec.score.toFixed(4) : rec.score}
`
: "";
const card = document.createElement("div");
card.className = `rec-card ${key}${winner ? " winner" : ""}`;
card.innerHTML = `
${label}
${winner ? '๐' : ""}
${rec.model}
${score}
${esc(rec.reason)}
`;
recGrid.appendChild(card);
});
// โโ Ensemble Analysis section
renderEnsembleSection(data.ensemble_info || {}, results, recommendation, task);
// โโ Interactive Playground
renderPlayground(data.dataset_info, recommendation.recommendations?.best_overall, task);
// โโ Statistical Rigor
renderStatisticalSection(data.stats || {});
resultsSection.hidden = false;
resultsSection.scrollIntoView({ behavior: "smooth", block: "start" });
}
function renderStatisticalSection(stats) {
const tbody = document.getElementById("rigor-tbody");
const badge = document.getElementById("friedman-badge");
if (!tbody || !stats.ranking) return;
const isSig = stats.significant;
badge.className = `p-value-badge ${isSig ? 'significant' : 'not-significant'}`;
badge.textContent = isSig
? `Significant (p=${stats.friedman_p})`
: `Not Significant (p=${stats.friedman_p})`;
tbody.innerHTML = stats.ranking.map(r => {
const stability = r.win_rate;
return `
|
${r.avg_rank <= 1.5 ? '๐' : ''}
${r.model}
|
${r.avg_rank} |
${stability}%
|
${stability > 50 ? 'Dominant' : (stability > 20 ? 'Competitive' : 'Volatile')}
|
`;
}).join("");
}
// โโ Playground Logic โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
function renderPlayground(datasetInfo, bestOverall, task) {
const form = document.getElementById("playground-form");
const valueEl = document.getElementById("prediction-value");
const subEl = document.getElementById("prediction-sub");
const probEl = document.getElementById("probability-bars");
if (!form || !bestOverall) return;
form.innerHTML = "";
const features = datasetInfo.columns || [];
const preview = datasetInfo.preview ? datasetInfo.preview[0] : {};
features.forEach(f => {
const div = document.createElement("div");
div.className = "playground-field";
const types = datasetInfo.feature_types || {};
const isNumeric = types[f] === "numeric";
const sampleVal = preview[f];
const val = sampleVal != null ? sampleVal : (isNumeric ? 0 : "");
const placeholder = isNumeric ? "Enter value..." : "Enter text...";
div.innerHTML = `
`;
form.appendChild(div);
});
const updatePrediction = async () => {
const inputs = form.querySelectorAll("input");
const data = {};
inputs.forEach(i => {
const v = i.value;
data[i.dataset.feature] = isNaN(parseFloat(v)) ? v : parseFloat(v);
});
valueEl.style.opacity = "0.5";
try {
const resp = await fetch("/predict", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data)
});
const res = await resp.json();
valueEl.style.opacity = "1";
if (res.error) {
valueEl.textContent = "Error";
subEl.textContent = res.error;
return;
}
if (task === "classification") {
valueEl.textContent = res.prediction || "โ";
subEl.textContent = `Most likely class (via ${bestOverall.model})`;
if (res.probabilities && res.labels) {
probEl.innerHTML = res.probabilities.map((p, i) => `
${res.labels[i] || 'Class '+i}${(p*100).toFixed(1)}%
`).join("");
}
} else {
const val = Number(res.prediction);
valueEl.textContent = isNaN(val) ? "โ" : val.toFixed(4);
subEl.textContent = `Regression output (via ${bestOverall.model})`;
probEl.innerHTML = "";
}
} catch (e) {
valueEl.style.opacity = "1";
valueEl.textContent = "Error";
subEl.textContent = "Service unavailable";
}
};
form.addEventListener("input", debounce(updatePrediction, 300));
updatePrediction(); // Initial prediction
}
function debounce(fn, ms) {
let timeout;
return (...args) => {
clearTimeout(timeout);
timeout = setTimeout(() => fn.apply(this, args), ms);
};
}
// โโ Ensemble Analysis renderer โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
function renderEnsembleSection(ensembleInfo, results, recommendation, task) {
const grid = document.getElementById("ensemble-grid");
const title = document.getElementById("ensemble-section-title");
grid.innerHTML = "";
const entries = Object.entries(ensembleInfo).filter(([name]) => results[name] && !results[name].error);
if (!entries.length) {
title.hidden = true;
grid.hidden = true;
return;
}
title.hidden = false;
grid.hidden = false;
const primaryKey = task === "classification" ? "roc_auc" : "r2";
const primaryLabel = task === "classification" ? "ROC-AUC" : "Rยฒ";
// Find the best individual model score (excluding ensembles) for gain %
const indivScores = Object.entries(results)
.filter(([n, v]) => !ENSEMBLE_NAMES.includes(n) && !v.error && v.mean[primaryKey] != null)
.map(([, v]) => v.mean[primaryKey]);
const bestIndivScore = indivScores.length ? Math.max(...indivScores) : 0;
entries.forEach(([name, info]) => {
const cv = results[name];
const score = cv.mean[primaryKey] ?? 0;
const std = cv.std[primaryKey] ?? 0;
const ft = cv.mean.fit_time ?? 0;
const color = MODEL_COLORS[name] || "#888";
const gain = bestIndivScore > 0 ? ((score - bestIndivScore) / bestIndivScore * 100) : 0;
const gainStr = gain >= 0
? `โฒ +${gain.toFixed(2)}% vs best individual`
: `โผ ${gain.toFixed(2)}% vs best individual`;
const componentPills = (info.components || []).map(c =>
`${c}`
).join("");
const metaTag = info.meta_learner
? `Meta-learner: ${esc(info.meta_learner)}
` : "";
const card = document.createElement("div");
card.className = "ens-card";
card.style.setProperty("--ens-color", color);
card.innerHTML = `
${score.toFixed(4)}
${primaryLabel} ยฑ ${std.toFixed(3)}
${gainStr}
${metaTag}
${esc(info.description || "")}
Component Models
${componentPills}
`;
grid.appendChild(card);
});
}
// โโ Helpers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
function resetToUpload() {
currentFile = null;
if (fileInput) fileInput.value = "";
if (uploadError) uploadError.hidden = true;
if (previewSection) previewSection.hidden = true;
if (loadingSection) loadingSection.hidden = true;
if (resultsSection) resultsSection.hidden = true;
if (uploadSection) uploadSection.hidden = false;
chartInstances.forEach(c => c.destroy());
chartInstances = [];
sessionStorage.removeItem("lastResults");
sessionStorage.removeItem("lastFileName");
if (window.location.pathname.includes("arena.html")) {
window.location.href = "/static/uploader.html";
} else {
window.scrollTo({ top: 0, behavior: "smooth" });
}
}
function showError(msg) {
if (!uploadError) return;
uploadError.textContent = msg;
uploadError.hidden = false;
window.scrollTo({ top: 0, behavior: "smooth" });
}
function exportToCSV(data) {
const results = data.results;
const models = Object.keys(results);
if (models.length === 0) return;
const metricKeys = new Set();
models.forEach(m => {
if (results[m].mean) {
Object.keys(results[m].mean).forEach(k => metricKeys.add(k));
}
});
const metrics = Array.from(metricKeys).sort();
let csv = "Model," + metrics.map(m => m + " (mean)").join(",") + "\n";
models.forEach(m => {
if (results[m].error) {
const errText = results[m].error.startsWith("Error:") ? results[m].error : `Error: ${results[m].error}`;
csv += `${m.replace(/,/g, "")},${errText.replace(/,/g, " ")}\n`;
return;
}
let row = [m.replace(/,/g, "")];
metrics.forEach(met => {
let val = results[m].mean ? results[m].mean[met] : "";
row.push(val !== undefined && val !== null ? val : "");
});
csv += row.join(",") + "\n";
});
downloadFile(csv, "benchmark_results.csv", "text/csv");
}
function exportToJSON(data) {
const json = JSON.stringify(data, null, 2);
downloadFile(json, "benchmark_results.json", "application/json");
}
function downloadFile(content, fileName, contentType) {
const blob = new Blob([content], { type: contentType });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = fileName;
a.click();
setTimeout(() => URL.revokeObjectURL(url), 100);
}
function fmt(v) {
if (v == null || isNaN(v)) return "โ";
return Number(v).toFixed(4);
}
function roundN(v, n) {
return Math.round(v * Math.pow(10, n)) / Math.pow(10, n);
}
function esc(str) {
return String(str)
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """);
}
function scoreClass(v, metric, task) {
if (metric === "fit_time") return "";
const higherBetter = !["mae", "rmse", "mse", "log_loss"].includes(metric);
if (!higherBetter) {
if (v < 0.1) return "col-excellent";
if (v < 0.3) return "col-good";
if (v < 0.5) return "col-fair";
return "col-poor";
}
if (metric === "roc_auc" || metric === "accuracy") {
if (v >= 0.95) return "col-excellent";
if (v >= 0.88) return "col-good";
if (v >= 0.75) return "col-fair";
return "col-poor";
}
if (metric === "r2") {
if (v >= 0.75) return "col-excellent";
if (v >= 0.5) return "col-good";
if (v >= 0.25) return "col-fair";
return "col-poor";
}
if (v >= 0.85) return "col-excellent";
if (v >= 0.70) return "col-good";
if (v >= 0.55) return "col-fair";
return "col-poor";
}
// โโ Restore state on load โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
window.addEventListener("DOMContentLoaded", () => {
checkResumeState();
});
// โโ Handle Back Button (BFCache) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
window.addEventListener("pageshow", function(e) {
checkResumeState();
});
// Theme Toggle Logic
const themeToggle = document.getElementById("theme-toggle");
const themeIconDark = document.getElementById("theme-icon-dark");
const themeIconLight = document.getElementById("theme-icon-light");
function setTheme(theme) {
document.documentElement.setAttribute("data-theme", theme);
localStorage.setItem("theme", theme);
if (theme === "light") {
if (themeIconDark) themeIconDark.style.display = "block";
if (themeIconLight) themeIconLight.style.display = "none";
} else {
if (themeIconDark) themeIconDark.style.display = "none";
if (themeIconLight) themeIconLight.style.display = "block";
}
}
if (themeToggle) {
themeToggle.addEventListener("click", () => {
const current = document.documentElement.getAttribute("data-theme") || "dark";
setTheme(current === "dark" ? "light" : "dark");
});
}
// Initial theme load
const savedTheme = localStorage.getItem("theme") || "dark";
setTheme(savedTheme);
function checkResumeState() {
const savedResults = sessionStorage.getItem("lastResults");
const savedFile = sessionStorage.getItem("lastFileName");
const isUploader = window.location.pathname.includes("uploader.html") || window.location.pathname === "/";
const isArena = window.location.pathname.includes("arena.html");
// Handle MBench logo link privilege
const navLogo = document.getElementById("nav-logo");
if (navLogo) {
// Privilege: Only uploader page in fresh mode (no results) can go to landing
if (isUploader && !savedResults) {
navLogo.classList.add("active-link");
navLogo.style.pointerEvents = "auto";
} else {
navLogo.classList.remove("active-link");
navLogo.style.pointerEvents = "none";
}
}
if (savedResults && savedFile) {
if (isUploader) {
// Always show resume card if data exists, until cleared
if (uploadSection) uploadSection.hidden = true;
if (previewSection) previewSection.hidden = true;
if (loadingSection) loadingSection.hidden = true;
if (resumeSection) {
resumeSection.hidden = false;
resumeFilename.textContent = savedFile;
}
} else if (isArena) {
// Auto-render on results page if data exists
try {
const data = JSON.parse(savedResults);
renderResults(data);
} catch (e) {
window.location.href = "/static/uploader.html";
}
}
} else {
// No saved data: reset to default
if (isUploader) {
if (resumeSection) resumeSection.hidden = true;
if (uploadSection) uploadSection.hidden = false;
} else if (isArena) {
window.location.href = "/static/uploader.html";
}
}
}