const testerInput = document.getElementById("tester"); const samplerInput = document.getElementById("samplers"); const actionContainer = document.getElementById("action-container"); const resultsSection = document.getElementById("results"); let canvas = document.getElementById("lines-canvas"); let ctx = canvas.getContext("2d"); let generateBtn, loaderDiv; function resetUI() { resultsSection.classList.add("hidden"); resultsSection.innerHTML = `
`; // Re-attach canvas & ctx properly canvas = document.getElementById("lines-canvas"); ctx = canvas.getContext("2d"); canvas.width = 0; canvas.height = 0; // Remove old button & loader if exists if (loaderDiv) loaderDiv.remove(); if (generateBtn) generateBtn.remove(); } testerInput.addEventListener("change", handleFileChange); samplerInput.addEventListener("change", handleFileChange); function handleFileChange() { resetUI(); const testerReady = testerInput.files.length === 1; const samplersReady = samplerInput.files.length > 0 && samplerInput.files.length <= 5; if (samplerInput.files.length > 5) { alert("You can upload a maximum of 5 sampler photos."); samplerInput.value = ""; return; } if (testerReady && samplersReady) { generateBtn = document.createElement("button"); generateBtn.textContent = "Generate Comparison"; generateBtn.className = "generate-btn"; generateBtn.onclick = sendComparison; actionContainer.appendChild(generateBtn); } } function showLoader() { loaderDiv = document.createElement("div"); loaderDiv.className = "loader"; actionContainer.appendChild(loaderDiv); } async function sendComparison() { generateBtn.disabled = true; showLoader(); const formData = new FormData(); formData.append("tester", testerInput.files[0]); for (let file of samplerInput.files) formData.append("samplers", file); try { const res = await fetch("/compare", { method: "POST", body: formData }); if (!res.ok) throw new Error(await res.text()); const data = await res.json(); renderResults(data); } catch (err) { alert("Error: " + err.message); } finally { generateBtn.disabled = false; loaderDiv.remove(); } } function getBorderColor(percent) { if (percent < 30) return "#ef4444"; // red if (percent <= 50) return "#fb923c"; // orange if (percent <= 80) return "#22c55e"; // green return "#a855f7"; // purple } function renderResults({ tester, results }) { resultsSection.classList.remove("hidden"); const testerColumn = document.getElementById("tester-column"); const samplerColumn = document.getElementById("sampler-column"); const testerSquare = createSquare(testerInput.files[0], "#3b82f6"); testerSquare.classList.add("tester-node"); testerColumn.appendChild(testerSquare); // Append results results.forEach((r,idx)=>{ const square = createSquare( samplerInput.files[idx], getBorderColor(r.similarity) ); samplerColumn.appendChild(square); /* Wait for browser calculate layout then draw line */ requestAnimationFrame(()=>drawLineBetween(testerSquare,square,r.similarity)); }); } function createSquare(file, borderColor) { const url = URL.createObjectURL(file); const div = document.createElement("div"); div.className = "square"; div.style.border = `4px solid ${borderColor}`; const img = document.createElement("img"); img.src = url; div.appendChild(img); return div; } function drawLineBetween(el1, el2, similarity) { const rect1 = el1.getBoundingClientRect(); const rect2 = el2.getBoundingClientRect(); const offset = resultsSection.getBoundingClientRect(); // Compute canvas size on first call if (canvas.width === 0) { canvas.width = resultsSection.scrollWidth; canvas.height = resultsSection.getBoundingClientRect().height; } const x1 = rect1.right - offset.left; const y1 = rect1.top + rect1.height / 2 - offset.top; const x2 = rect2.left - offset.left; const y2 = rect2.top + rect2.height / 2 - offset.top; ctx.strokeStyle = getBorderColor(similarity); ctx.lineWidth = 3; ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); ctx.stroke(); // Label const label = document.createElement("div"); label.className = "label"; label.textContent = similarity.toFixed(0) + "%"; label.style.left = (x1 + x2) / 2 + "px"; label.style.top = (y1 + y2) / 2 + "px"; resultsSection.appendChild(label); }