CarDentIQ / static /js /workspace.js
parthmax24's picture
added the latest updates
81b4246
Raw
History Blame Contribute Delete
6.29 kB
// Developer: Saksham Pathak (github.com/parthmax2)
let classes = [];
let selectedFile = null;
const dropzone = document.getElementById("dropzone");
const dropzoneEmpty = document.getElementById("dropzoneEmpty");
const dropzonePreview = document.getElementById("dropzonePreview");
const previewImage = document.getElementById("previewImage");
const previewFileName = document.getElementById("previewFileName");
const previewFileInfo = document.getElementById("previewFileInfo");
const changeImageBtn = document.getElementById("changeImageBtn");
const imageInput = document.getElementById("imageInput");
const resizeCb = document.getElementById("resizeCb");
const slidersEl = document.getElementById("sliders");
const legendEl = document.getElementById("legend");
const runBtn = document.getElementById("runBtn");
const scanOverlay = document.getElementById("scanOverlay");
const step1 = document.querySelector('.step[data-step="1"]');
const step2 = document.querySelector('.step[data-step="2"]');
const line1 = document.getElementById("line1");
const stepEyebrow = document.getElementById("stepEyebrow");
const toastStack = document.getElementById("toastStack");
function showToast(message, type = "info") {
const el = document.createElement("div");
el.className = `toast ${type}`;
el.textContent = message;
toastStack.appendChild(el);
setTimeout(() => el.remove(), 4000);
}
function formatBytes(bytes) {
if (bytes < 1024) return `${bytes} B`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}
function readImageDimensions(file) {
return new Promise((resolve) => {
const img = new Image();
img.onload = () => {
resolve({ width: img.naturalWidth, height: img.naturalHeight });
URL.revokeObjectURL(img.src);
};
img.src = URL.createObjectURL(file);
});
}
async function init() {
const res = await fetch("/api/classes");
const data = await res.json();
classes = data.classes;
renderSliders();
renderLegend();
}
function renderSliders() {
slidersEl.innerHTML = classes.map((c) => `
<div class="slider-item">
<label for="slider-${c.id}">${c.name}</label>
<input type="range" id="slider-${c.id}" min="0" max="1" step="0.01" value="${c.default_threshold}">
<span id="slider-${c.id}-val" class="mono">${c.default_threshold}</span>
</div>
`).join("");
classes.forEach((c) => {
const input = document.getElementById(`slider-${c.id}`);
const val = document.getElementById(`slider-${c.id}-val`);
input.addEventListener("input", () => { val.textContent = input.value; });
});
}
function renderLegend() {
legendEl.innerHTML = classes.map((c) => `
<div class="legend-item">
<span class="swatch" style="background: rgb(${c.color.join(",")})"></span>
<span>${c.name}</span>
</div>
`).join("");
}
function getThresholds() {
const thresholds = {};
classes.forEach((c) => {
thresholds[c.id] = parseFloat(document.getElementById(`slider-${c.id}`).value);
});
return thresholds;
}
// ── upload / dropzone ──────────────────────────────────────
async function setSelectedFile(file) {
if (!file || !file.type.startsWith("image/")) {
showToast("Please choose a valid image file.", "error");
return;
}
selectedFile = file;
previewImage.src = URL.createObjectURL(file);
previewFileName.textContent = file.name;
previewFileInfo.textContent = formatBytes(file.size);
dropzoneEmpty.hidden = true;
dropzonePreview.hidden = false;
runBtn.disabled = false;
const { width, height } = await readImageDimensions(file);
previewFileInfo.textContent = `${width} Γ— ${height} px Β· ${formatBytes(file.size)}`;
}
dropzone.addEventListener("click", (e) => {
if (e.target.closest(".dropzone-preview")) return;
imageInput.click();
});
dropzone.addEventListener("keydown", (e) => {
if (e.key === "Enter" || e.key === " ") { e.preventDefault(); imageInput.click(); }
});
imageInput.addEventListener("change", () => setSelectedFile(imageInput.files[0]));
changeImageBtn.addEventListener("click", (e) => {
e.stopPropagation();
imageInput.click();
});
["dragenter", "dragover"].forEach((evt) =>
dropzone.addEventListener(evt, (e) => { e.preventDefault(); dropzone.classList.add("dragover"); })
);
["dragleave", "drop"].forEach((evt) =>
dropzone.addEventListener(evt, (e) => { e.preventDefault(); dropzone.classList.remove("dragover"); })
);
dropzone.addEventListener("drop", (e) => {
const file = e.dataTransfer.files[0];
if (file) setSelectedFile(file);
});
// ── run detection ──────────────────────────────────────────
runBtn.addEventListener("click", async () => {
if (!selectedFile) {
showToast("Please choose an image first.", "error");
return;
}
runBtn.disabled = true;
runBtn.textContent = "Analyzing…";
scanOverlay.classList.add("active");
step1.classList.add("completed");
step1.classList.remove("active");
step2.classList.add("active");
line1.classList.add("filled");
stepEyebrow.innerHTML = `<span class="dot"></span> Step 2 of 3`;
try {
const formData = new FormData();
formData.append("image", selectedFile);
formData.append("resize", resizeCb.checked);
formData.append("thresholds", JSON.stringify(getThresholds()));
const res = await fetch("/api/detect", { method: "POST", body: formData });
if (!res.ok) throw new Error(`Request failed: ${res.status}`);
const data = await res.json();
sessionStorage.setItem("cardentiq_result", JSON.stringify(data));
window.location.href = "report.html";
} catch (err) {
showToast(`Detection failed: ${err.message}`, "error");
scanOverlay.classList.remove("active");
step1.classList.remove("completed");
step1.classList.add("active");
step2.classList.remove("active");
line1.classList.remove("filled");
stepEyebrow.innerHTML = `<span class="dot"></span> Step 1 of 3`;
runBtn.disabled = false;
runBtn.textContent = "Analyze Image";
}
});
init();