TaskCLIP / webui /static /main.js
HanningChen
update front page
af40191
raw
history blame
8.26 kB
window.addEventListener("error", (e) => {
const log = document.querySelector("#statusLog");
if (log) log.textContent = "JS error: " + (e.message || e.error) + "\n" + log.textContent;
});
const $ = (sel) => document.querySelector(sel);
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");
function setStatus(kind, text, logLine) {
// kind: idle | run | ok | bad
statusDot.className = `dot ${kind}`;
statusText.textContent = text;
if (logLine) statusLog.textContent = logLine + "\n" + statusLog.textContent;
}
function setRunning(isRunning) {
runBtn.disabled = isRunning;
spinner.classList.toggle("hidden", !isRunning);
runBtnText.textContent = isRunning ? "Running…" : "Run";
}
function clearResults() {
imgInput.removeAttribute("src");
imgYolo.removeAttribute("src");
imgSelected.removeAttribute("src");
meta.textContent = "";
jobPill.textContent = "job: —";
[dlInput, dlYolo, dlSelected].forEach(a => {
a.classList.add("hidden");
a.removeAttribute("href");
});
}
function setPreview(file) {
if (!file) {
previewWrap.classList.add("hidden");
previewImg.removeAttribute("src");
fileMeta.textContent = "No file selected";
return;
}
fileMeta.textContent = `${file.name}${(file.size / 1024).toFixed(1)} KB`;
const url = URL.createObjectURL(file);
previewImg.src = url;
previewWrap.classList.remove("hidden");
}
(function () {
const noiseType = document.getElementById("noiseType");
if (!noiseType) return;
const panels = {
none: document.getElementById("noisePanelNone"),
gaussian: document.getElementById("noisePanelGaussian"),
linear: document.getElementById("noisePanelLinear"),
adv: document.getElementById("noisePanelAdv"),
};
const sliders = {
gaussian: document.getElementById("noiseGaussian"),
linear: document.getElementById("noiseLinear"),
adv: document.getElementById("noiseAdv"),
};
const labels = {
gaussian: document.getElementById("noiseValGaussian"),
linear: document.getElementById("noiseValLinear"),
adv: document.getElementById("noiseValAdv"),
};
const hiddenStrength = document.getElementById("noiseStrength");
// Map dropdown values to ONE of the existing panel keys.
function noiseFamily(val) {
const t = String(val || "none").toLowerCase();
if (t === "none" || t === "off") return "none";
// gaussian variants
if (t === "gaussian" || t.includes("gauss")) return "gaussian";
// linear variants
if (t === "linear" || t.includes("linear")) return "linear";
// adversarial variants (covers: "simple_adversarial_noise", "fgsm", "pgd", etc.)
if (
t === "adv" ||
t.startsWith("adv") ||
t.includes("adversarial") ||
t.includes("attack") ||
t.includes("fgsm") ||
t.includes("pgd")
) return "adv";
return "none";
}
function updateHiddenStrength() {
const fam = noiseFamily(noiseType.value);
if (fam === "none") {
hiddenStrength.value = "0";
return;
}
hiddenStrength.value = String(sliders[fam]?.value ?? "0");
}
function showPanelForCurrentSelection() {
const fam = noiseFamily(noiseType.value);
Object.values(panels).forEach((p) => p && p.classList.add("hidden"));
(panels[fam] || panels.none).classList.remove("hidden");
updateHiddenStrength();
}
// Slider updates
Object.keys(sliders).forEach((k) => {
sliders[k]?.addEventListener("input", () => {
if (labels[k]) labels[k].textContent = sliders[k].value;
if (noiseFamily(noiseType.value) === k) updateHiddenStrength();
});
});
// Dropdown updates
noiseType.addEventListener("change", showPanelForCurrentSelection);
// Initialize from current dropdown value (NOT hard-coded none)
showPanelForCurrentSelection();
})();
(function () {
// Tabs
const tabBtns = document.querySelectorAll(".tabBtn");
const panels = document.querySelectorAll(".tabPanel");
tabBtns.forEach((b) => {
b.addEventListener("click", () => {
tabBtns.forEach((x) => x.classList.remove("active"));
panels.forEach((p) => p.classList.add("hidden"));
b.classList.add("active");
const id = b.getAttribute("data-tab");
document.getElementById(id)?.classList.remove("hidden");
});
});
// Hardware noise sliders -> label text
const w = document.getElementById("hwNoiseWidth");
const s = document.getElementById("hwNoiseStrength");
const wv = document.getElementById("hwNoiseWidthVal");
const sv = document.getElementById("hwNoiseStrengthVal");
if (w && wv) w.addEventListener("input", () => (wv.textContent = w.value));
if (s && sv) s.addEventListener("input", () => (sv.textContent = s.value));
})();
dropzone.addEventListener("click", () => fileInput.click());
dropzone.addEventListener("keydown", (e) => {
if (e.key === "Enter" || e.key === " ") fileInput.click();
});
fileInput.addEventListener("change", () => {
const file = fileInput.files && fileInput.files[0];
setPreview(file);
});
["dragenter", "dragover"].forEach(evt => {
dropzone.addEventListener(evt, (e) => {
e.preventDefault();
e.stopPropagation();
dropzone.classList.add("drag");
});
});
["dragleave", "drop"].forEach(evt => {
dropzone.addEventListener(evt, (e) => {
e.preventDefault();
e.stopPropagation();
dropzone.classList.remove("drag");
});
});
dropzone.addEventListener("drop", (e) => {
const file = e.dataTransfer.files && e.dataTransfer.files[0];
if (!file) return;
// Set preview
setPreview(file);
// Safely assign to input (works across browsers)
const dt = new DataTransfer();
dt.items.add(file);
fileInput.files = dt.files;
});
resetBtn.addEventListener("click", () => {
form.reset();
fileInput.value = "";
setPreview(null);
clearResults();
statusLog.textContent = "Waiting for input…";
setStatus("idle", "Idle", "Reset UI.");
});
copyMetaBtn.addEventListener("click", async () => {
const text = meta.textContent || "";
if (!text) return;
await navigator.clipboard.writeText(text);
setStatus("ok", "Done", "Copied metadata to clipboard.");
});
form.addEventListener("submit", async (e) => {
e.preventDefault();
const file = fileInput.files && fileInput.files[0];
if (!file) {
setStatus("bad", "Error", "No file selected.");
return;
}
setRunning(true);
setStatus("run", "Running", "Submitting request to /api/run …");
const fd = new FormData(form);
try {
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}`);
}
jobPill.textContent = `job: ${data.job_id}`;
setStatus("ok", "Done", `Inference finished. job_id=${data.job_id}`);
// Update images (cache-bust)
const bust = `t=${Date.now()}`;
imgInput.src = `${data.image_urls.input}?${bust}`;
imgYolo.src = `${data.image_urls.yolo}?${bust}`;
imgSelected.src = `${data.image_urls.selected}?${bust}`;
// Open links
dlInput.href = data.image_urls.input;
dlYolo.href = data.image_urls.yolo;
dlSelected.href = data.image_urls.selected;
[dlInput, dlYolo, dlSelected].forEach(a => a.classList.remove("hidden"));
// Meta
meta.textContent = JSON.stringify({
task_id: data.task_id,
task_name: data.task_name,
selected_indices: data.selected_indices,
job_id: data.job_id,
}, null, 2);
} catch (err) {
setStatus("bad", "Error", String(err));
} finally {
setRunning(false);
}
});