feat: thumbnail picker custom per selezione modello/scena

- GET /folder_image/{filename}?w=N: PNG ridotto cache 1h
- Frontend: 2 thumb-picker al posto dei select (thumb + nome + caret)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-24 11:10:35 +02:00
parent 2bca68d700
commit b83e577eab
47 changed files with 115 additions and 27 deletions
+51 -17
View File
@@ -80,18 +80,56 @@ async function fetchImagesList() {
return await r.json();
}
function populateSelect(selectEl, files) {
selectEl.innerHTML = "";
const opt0 = document.createElement("option");
opt0.value = ""; opt0.textContent = "-- seleziona --";
selectEl.appendChild(opt0);
for (const f of files) {
const o = document.createElement("option");
o.value = f; o.textContent = f;
selectEl.appendChild(o);
}
function buildThumbPicker(pickerId, files, onSelect) {
const picker = document.getElementById(pickerId);
const current = picker.querySelector(".picker-current");
const list = picker.querySelector(".picker-list");
const text = current.querySelector(".picker-text");
// Rimuovi eventuale vecchia thumbnail
const oldImg = current.querySelector("img");
if (oldImg) oldImg.remove();
list.innerHTML = "";
files.forEach((f) => {
const item = document.createElement("div");
item.className = "picker-item";
const img = document.createElement("img");
img.src = `/folder_image/${encodeURIComponent(f)}?w=120`;
img.loading = "lazy";
const name = document.createElement("span");
name.className = "name"; name.textContent = f;
item.appendChild(img); item.appendChild(name);
item.addEventListener("click", () => {
// Aggiorna la visual del "current"
let thumb = current.querySelector("img");
if (!thumb) {
thumb = document.createElement("img");
current.insertBefore(thumb, text);
}
thumb.src = `/folder_image/${encodeURIComponent(f)}?w=80`;
text.textContent = f;
picker.classList.remove("open");
onSelect(f);
});
list.appendChild(item);
});
current.onclick = () => {
// Chiudi altri picker aperti
document.querySelectorAll(".thumb-picker.open")
.forEach((p) => { if (p !== picker) p.classList.remove("open"); });
picker.classList.toggle("open");
};
}
// Close picker on outside click
document.addEventListener("click", (e) => {
if (!e.target.closest(".thumb-picker")) {
document.querySelectorAll(".thumb-picker.open")
.forEach((p) => p.classList.remove("open"));
}
});
function loadImage(src) {
return new Promise((res, rej) => {
const img = new Image();
@@ -310,19 +348,15 @@ function setStatus(s) {
window.addEventListener("DOMContentLoaded", async () => {
buildAdvancedForm();
setupROI();
// Popola dropdown immagini da IMAGES_DIR
// Popola picker immagini da IMAGES_DIR (con thumbnail)
const {files, dir} = await fetchImagesList();
const selM = document.getElementById("sel-model");
const selS = document.getElementById("sel-scene");
populateSelect(selM, files);
populateSelect(selS, files);
buildThumbPicker("picker-model", files, onSelectModel);
buildThumbPicker("picker-scene", files, onSelectScene);
if (files.length === 0) {
setStatus(`Nessuna immagine in ${dir} (configura IMAGES_DIR in .env)`);
} else {
setStatus(`${files.length} immagini disponibili in ${dir}`);
}
selM.addEventListener("change", (e) => onSelectModel(e.target.value));
selS.addEventListener("change", (e) => onSelectScene(e.target.value));
document.getElementById("btn-match").addEventListener("click", doMatch);
const slider = document.getElementById("p-min-score");
slider.addEventListener("input", (e) => {