HanningChen
Fix bug
02bed5c
// static/main.js
(() => {
// Helpers
const $ = (id) => document.getElementById(id);
const qsa = (sel) => Array.from(document.querySelectorAll(sel));
// Elements
const form = $("runForm");
const runBtn = $("runBtn");
const runBtnText = $("runBtnText");
const spinner = $("spinner");
const resetBtn = $("resetBtn");
const fileInput = $("fileInput");
const dropzone = $("dropzone");
const fileMeta = $("fileMeta");
const previewWrap = $("previewWrap");
const previewImg = $("previewImg");
const statusDot = $("statusDot");
const statusText = $("statusText");
const statusLog = $("statusLog");
const jobPill = $("jobPill");
const imgInput = $("imgInput");
const imgYolo = $("imgYolo");
const imgSelected = $("imgSelected");
const meta = $("meta");
const dlInput = $("dlInput");
const dlYolo = $("dlYolo");
const dlSelected = $("dlSelected");
const copyMetaBtn = $("copyMetaBtn");
// Noise elements
const noiseType = $("noiseType");
const noiseStrengthHidden = $("noiseStrength");
const noisePanelNone = $("noisePanelNone");
const noisePanelGaussian = $("noisePanelGaussian");
const noisePanelLinear = $("noisePanelLinear");
const noisePanelAdv = $("noisePanelAdv");
const noiseGaussian = $("noiseGaussian");
const noiseLinear = $("noiseLinear");
const noiseAdv = $("noiseAdv");
const noiseValGaussian = $("noiseValGaussian");
const noiseValLinear = $("noiseValLinear");
const noiseValAdv = $("noiseValAdv");
// HW noise elements (UI labels only unless backend supports)
const hwNoiseWidth = $("hwNoiseWidth");
const hwNoiseStrength = $("hwNoiseStrength");
const hwNoiseWidthVal = $("hwNoiseWidthVal");
const hwNoiseStrengthVal = $("hwNoiseStrengthVal");
function setStatus(kind, text, logLine) {
// kind: idle | run | ok | bad
if (statusDot) statusDot.className = `dot ${kind}`;
if (statusText) statusText.textContent = text;
if (statusLog && logLine != null) statusLog.textContent = logLine;
}
function setRunning(isRunning) {
if (!runBtn) return;
runBtn.disabled = isRunning;
if (spinner) spinner.classList.toggle("hidden", !isRunning);
if (runBtnText) runBtnText.textContent = isRunning ? "Running…" : "Run";
}
function setPreview(file) {
if (!fileMeta || !previewWrap || !previewImg) return;
if (!file) {
fileMeta.textContent = "No file selected";
previewWrap.classList.add("hidden");
previewImg.removeAttribute("src");
return;
}
fileMeta.textContent = `${file.name}${(file.size / 1024).toFixed(1)} KB`;
const url = URL.createObjectURL(file);
previewImg.src = url;
previewWrap.classList.remove("hidden");
}
function setFileToInput(file) {
const dt = new DataTransfer();
dt.items.add(file);
fileInput.files = dt.files;
}
// ---------------- Tabs ----------------
function initTabs() {
const tabBtns = qsa(".tabBtn");
const tabPanels = qsa(".tabPanel");
tabBtns.forEach((btn) => {
btn.addEventListener("click", () => {
tabBtns.forEach((b) => b.classList.remove("active"));
tabPanels.forEach((p) => p.classList.add("hidden"));
btn.classList.add("active");
const id = btn.getAttribute("data-tab");
const panel = $(id);
if (panel) panel.classList.remove("hidden");
});
});
}
// ------------- HDC bits toggle -------------
function initHdcBitsToggle() {
// score function select has NAME but no ID in your HTML
const scoreSel = document.querySelector('select[name="score_function"]');
const bitsField = $("hdcBitsField");
const bitsSel = $("hdcBits");
if (!scoreSel || !bitsField || !bitsSel) {
console.warn("HDC bits toggle: missing DOM elements");
return;
}
const update = () => {
const isHDC = scoreSel.value === "HDC";
bitsField.style.display = isHDC ? "" : "none";
bitsSel.disabled = !isHDC;
};
scoreSel.addEventListener("change", update);
update();
}
// ------------- Noise panel switching -------------
function noiseFamily(val) {
const t = String(val || "none").toLowerCase();
if (t === "none" || t === "default" || t === "off") return "none";
if (t === "gaussian") return "gaussian";
if (t === "linear") return "linear";
if (t.startsWith("adv")) return "adv";
return "none";
}
function updateNoiseHidden() {
if (!noiseType || !noiseStrengthHidden) return;
const fam = noiseFamily(noiseType.value);
let v = 0;
if (fam === "gaussian" && noiseGaussian) v = Number(noiseGaussian.value || 0);
if (fam === "linear" && noiseLinear) v = Number(noiseLinear.value || 0);
if (fam === "adv" && noiseAdv) v = Number(noiseAdv.value || 0);
noiseStrengthHidden.value = String(v);
}
function showNoisePanel() {
if (!noiseType) return;
const fam = noiseFamily(noiseType.value);
[noisePanelNone, noisePanelGaussian, noisePanelLinear, noisePanelAdv].forEach((p) => {
if (p) p.classList.add("hidden");
});
if (fam === "none" && noisePanelNone) noisePanelNone.classList.remove("hidden");
if (fam === "gaussian" && noisePanelGaussian) noisePanelGaussian.classList.remove("hidden");
if (fam === "linear" && noisePanelLinear) noisePanelLinear.classList.remove("hidden");
if (fam === "adv" && noisePanelAdv) noisePanelAdv.classList.remove("hidden");
updateNoiseHidden();
}
function initNoise() {
if (!noiseType) return;
noiseType.addEventListener("change", showNoisePanel);
if (noiseGaussian) {
noiseGaussian.addEventListener("input", () => {
if (noiseValGaussian) noiseValGaussian.textContent = noiseGaussian.value;
if (noiseFamily(noiseType.value) === "gaussian") updateNoiseHidden();
});
}
if (noiseLinear) {
noiseLinear.addEventListener("input", () => {
if (noiseValLinear) noiseValLinear.textContent = noiseLinear.value;
if (noiseFamily(noiseType.value) === "linear") updateNoiseHidden();
});
}
if (noiseAdv) {
noiseAdv.addEventListener("input", () => {
if (noiseValAdv) noiseValAdv.textContent = noiseAdv.value;
if (noiseFamily(noiseType.value) === "adv") updateNoiseHidden();
});
}
showNoisePanel();
}
// ------------- HW noise label updates -------------
function initHwNoiseLabels() {
if (hwNoiseWidth && hwNoiseWidthVal) {
hwNoiseWidthVal.textContent = hwNoiseWidth.value;
hwNoiseWidth.addEventListener("input", () => {
hwNoiseWidthVal.textContent = hwNoiseWidth.value;
});
}
if (hwNoiseStrength && hwNoiseStrengthVal) {
hwNoiseStrengthVal.textContent = hwNoiseStrength.value;
hwNoiseStrength.addEventListener("input", () => {
hwNoiseStrengthVal.textContent = hwNoiseStrength.value;
});
}
}
// ------------- Upload / Dropzone -------------
function initUpload() {
if (!fileInput || !dropzone) return;
dropzone.addEventListener("click", () => fileInput.click());
dropzone.addEventListener("keydown", (e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
fileInput.click();
}
});
fileInput.addEventListener("change", () => {
const f = fileInput.files && fileInput.files[0];
setPreview(f);
});
dropzone.addEventListener("dragover", (e) => {
e.preventDefault();
dropzone.classList.add("drag");
});
dropzone.addEventListener("dragleave", () => {
dropzone.classList.remove("drag");
});
dropzone.addEventListener("drop", (e) => {
e.preventDefault();
dropzone.classList.remove("drag");
const f = e.dataTransfer?.files?.[0];
if (!f) return;
setFileToInput(f);
setPreview(f);
});
}
// ------------- Reset -------------
function initReset() {
if (!resetBtn || !form) return;
resetBtn.addEventListener("click", () => {
form.reset();
if (fileInput) fileInput.value = "";
setPreview(null);
if (jobPill) jobPill.textContent = "job: —";
if (imgInput) imgInput.removeAttribute("src");
if (imgYolo) imgYolo.removeAttribute("src");
if (imgSelected) imgSelected.removeAttribute("src");
if (meta) meta.textContent = "";
// reset labels
if (noiseValGaussian && noiseGaussian) noiseValGaussian.textContent = noiseGaussian.value;
if (noiseValLinear && noiseLinear) noiseValLinear.textContent = noiseLinear.value;
if (noiseValAdv && noiseAdv) noiseValAdv.textContent = noiseAdv.value;
initHdcBitsToggle();
showNoisePanel();
setStatus("idle", "Idle", "Waiting for input…");
setRunning(false);
});
}
// ------------- Copy meta -------------
function initCopyMeta() {
if (!copyMetaBtn || !meta) return;
copyMetaBtn.addEventListener("click", async () => {
const text = meta.textContent || "";
if (!text) return;
await navigator.clipboard.writeText(text);
setStatus("ok", "Done", "Copied metadata to clipboard.");
});
}
// ------------- Submit -------------
function initSubmit() {
if (!form) return;
form.addEventListener("submit", async (e) => {
e.preventDefault();
const file = fileInput?.files?.[0];
if (!file) {
setStatus("bad", "Error", "No file selected.");
return;
}
updateNoiseHidden();
setRunning(true);
setStatus("run", "Running", "Submitting request…");
try {
const fd = new FormData(form);
const resp = await fetch("/api/run", { method: "POST", body: fd });
const data = await resp.json();
if (!resp.ok || !data.ok) throw new Error(data?.error || `HTTP ${resp.status}`);
if (jobPill) jobPill.textContent = `job: ${data.job_id}`;
const bust = `t=${Date.now()}`;
if (imgInput) imgInput.src = `${data.image_urls.input}?${bust}`;
if (imgYolo) imgYolo.src = `${data.image_urls.yolo}?${bust}`;
if (imgSelected) imgSelected.src = `${data.image_urls.selected}?${bust}`;
if (dlInput) { dlInput.href = data.image_urls.input; dlInput.classList.remove("hidden"); }
if (dlYolo) { dlYolo.href = data.image_urls.yolo; dlYolo.classList.remove("hidden"); }
if (dlSelected) { dlSelected.href = data.image_urls.selected; dlSelected.classList.remove("hidden"); }
if (meta) {
meta.textContent = JSON.stringify(
{
job_id: data.job_id,
task_id: data.task_id,
task_name: data.task_name,
selected_indices: data.selected_indices,
},
null,
2
);
}
setStatus("ok", "Done", "Finished.");
} catch (err) {
setStatus("bad", "Error", String(err));
} finally {
setRunning(false);
}
});
}
// Boot
document.addEventListener("DOMContentLoaded", () => {
if (!form || !fileInput || !dropzone) {
console.error("Missing DOM elements: check ids in HTML.");
if (statusLog) statusLog.textContent = "JS init failed: missing DOM elements (check ids).";
return;
}
initTabs();
initHdcBitsToggle();
initNoise();
initHwNoiseLabels();
initUpload();
initReset();
initCopyMeta();
initSubmit();
setStatus("idle", "Idle", "Waiting for input…");
});
})();