Spaces:
Sleeping
Sleeping
| 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); | |
| } | |
| }); | |