Team Ai
Apppublic

JamesNR/patchnet

sourceHugging Faceupdated 8mo agoView on Hugging Face
0likes
app.js130 linesDownload Raw Back to static
1// PatchNet Frontend2 3const form = document.getElementById('predict-form');4const fileInput = document.getElementById('file-input');5const dropZone = document.getElementById('drop-zone');6const placeholder = document.getElementById('upload-placeholder');7const previewImg = document.getElementById('preview-img');8const promptInput = document.getElementById('prompt-input');9const submitBtn = document.getElementById('submit-btn');10const btnText = submitBtn.querySelector('.btn-text');11const btnLoading = submitBtn.querySelector('.btn-loading');12const resultsEl = document.getElementById('results-section');13 14let selectedFile = null;15 16// ──────────── Drag & Drop / File Select ──────────────────────────────17 18dropZone.addEventListener('click', () => fileInput.click());19 20dropZone.addEventListener('dragover', e => {21    e.preventDefault();22    dropZone.classList.add('dragover');23});24dropZone.addEventListener('dragleave', () => dropZone.classList.remove('dragover'));25dropZone.addEventListener('drop', e => {26    e.preventDefault();27    dropZone.classList.remove('dragover');28    if (e.dataTransfer.files.length) handleFile(e.dataTransfer.files[0]);29});30 31fileInput.addEventListener('change', () => {32    if (fileInput.files.length) handleFile(fileInput.files[0]);33});34 35function handleFile(file) {36    if (!file.type.startsWith('image/')) return;37    selectedFile = file;38    const reader = new FileReader();39    reader.onload = e => {40        previewImg.src = e.target.result;41        previewImg.classList.remove('hidden');42        placeholder.classList.add('hidden');43    };44    reader.readAsDataURL(file);45    checkReady();46}47 48// ──────────── Enable button when both fields filled ──────────────────49 50promptInput.addEventListener('input', checkReady);51function checkReady() {52    submitBtn.disabled = !(selectedFile && promptInput.value.trim());53}54 55// ──────────── Submit ─────────────────────────────────────────────────56 57form.addEventListener('submit', async e => {58    e.preventDefault();59    if (!selectedFile || !promptInput.value.trim()) return;60 61    // UI → loading state62    submitBtn.disabled = true;63    btnText.classList.add('hidden');64    btnLoading.classList.remove('hidden');65    resultsEl.classList.add('hidden');66 67    const formData = new FormData();68    formData.append('image', selectedFile);69    formData.append('prompt', promptInput.value.trim());70 71    try {72        const res = await fetch('/api/predict', { method: 'POST', body: formData });73        if (!res.ok) throw new Error(`Server error: ${res.status}`);74        const data = await res.json();75        renderResults(data);76    } catch (err) {77        alert('Error: ' + err.message);78    } finally {79        btnText.classList.remove('hidden');80        btnLoading.classList.add('hidden');81        submitBtn.disabled = false;82    }83});84 85// ──────────── Render Results ─────────────────────────────────────────86 87function renderResults(data) {88    const maxScore = data.max_score;89 90    setScore('consistency', data.consistency_score, maxScore);91    setScore('quality', data.quality_score, maxScore);92    setScore('authenticity', data.authenticity_score, maxScore);93 94    // Annotated image95    document.getElementById('annotated-img').src = 'data:image/jpeg;base64,' + data.annotated_image;96    document.getElementById('info-grid').textContent = `${data.num_grid_patches} grid patches`;97    document.getElementById('info-surf').textContent = `${data.num_surf_keypoints} SURF keypoints`;98 99    // SURF patches gallery100    const gallery = document.getElementById('surf-gallery');101    gallery.innerHTML = '';102    data.surf_patches.forEach((b64, i) => {103        const img = document.createElement('img');104        img.src = 'data:image/jpeg;base64,' + b64;105        img.alt = `SURF Patch ${i + 1}`;106        gallery.appendChild(img);107    });108 109    resultsEl.classList.remove('hidden');110    resultsEl.scrollIntoView({ behavior: 'smooth', block: 'start' });111}112 113function setScore(name, score, max) {114    const pct = Math.min(score / max, 1);115    const circumference = 2 * Math.PI * 52;  // r=52116 117    // Value text118    document.getElementById(`val-${name}`).textContent = score.toFixed(2);119 120    // Ring animation121    const ring = document.getElementById(`ring-${name}`);122    ring.style.strokeDashoffset = circumference * (1 - pct);123 124    // Colour based on score ratio125    ring.classList.remove('ring-good', 'ring-mid', 'ring-low');126    if (pct >= 0.65) ring.classList.add('ring-good');127    else if (pct >= 0.35) ring.classList.add('ring-mid');128    else ring.classList.add('ring-low');129}130