FineVision / app /src /content /embeds /comparison.html
thibaud frere
add responsiveness on pie and comparison
14a3a46
raw
history blame
8.13 kB
<div class="image-comparison" style="width:100%;margin:10px 0;"></div>
<style>
.image-comparison { position: relative; }
.image-comparison .controls { display:flex; align-items:center; gap:16px; justify-content:center; flex-wrap:wrap; margin:14px 0; }
.image-comparison .controls label { font-size:14px; color: var(--text-color); display:flex; align-items:center; justify-content:center; gap:10px; font-weight:600; }
.image-comparison .controls select {
font-size: 14px;
padding: 8px 32px 8px 12px;
border: 1px solid var(--border-color);
border-radius: 10px;
background-color: var(--surface-bg);
color: var(--text-color);
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%230f1115' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 8px center;
background-size: 12px;
-webkit-appearance: none; appearance: none; cursor: pointer;
transition: border-color .15s ease, box-shadow .15s ease;
box-shadow: 0 1px 2px rgba(0,0,0,.04);
}
[data-theme="dark"] .image-comparison .controls select {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
}
.image-comparison .controls select:hover { border-color: var(--primary-color); }
.image-comparison .controls select:focus { border-color: var(--primary-color); box-shadow: 0 0 0 3px rgba(232,137,171,.25); outline: none; }
/* Responsive: empiler label et select proprement sur petits écrans */
@media (max-width: 520px) {
.image-comparison .controls label { flex-direction: column; gap: 6px; }
}
.image-comparison .grid { display:grid; grid-template-columns: repeat(4, 1fr); gap: 12px; width:100%; align-items: start; }
/* Large → 4 colonnes; Medium → 2 colonnes; Mobile → 1 colonne */
@media (max-width: 1100px) { .image-comparison .grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 680px) { .image-comparison .grid { grid-template-columns: 1fr; } }
.image-comparison .card { position: relative; border:1px solid var(--border-color); border-radius:10px; overflow:hidden; background: var(--surface-bg); display:flex; flex-direction:column; }
.image-comparison .card .media { position: relative; width:100%; height: 200px; background: var(--surface-2, var(--surface-bg)); display:block; }
.image-comparison .card .media img { width:100%; height:100%; object-fit: contain; display:block; }
.image-comparison .badge { position:absolute; top:8px; left:8px; font-size:11px; padding:3px 6px; border-radius:6px; background: var(--surface-bg); color: var(--text-color); border:1px solid var(--border-color); }
.image-comparison .meta { padding:8px 10px; border-top:1px solid var(--border-color); font-size:12px; display:flex; height: 55px; align-items:start; justify-content:space-between; gap:8px; }
.image-comparison .meta .label { color: var(--muted-color); }
.image-comparison .meta .value { font-weight:600; }
</style>
<script>
(() => {
const THIS_SCRIPT = document.currentScript;
const bootstrap = () => {
const scriptEl = THIS_SCRIPT;
const host = scriptEl && scriptEl.parentElement;
let container = null;
if (host && host.querySelector) {
container = host.querySelector('.image-comparison');
}
if (!container) {
let sib = scriptEl && scriptEl.previousElementSibling;
while (sib && !(sib.classList && sib.classList.contains('image-comparison'))) {
sib = sib.previousElementSibling;
}
container = sib || document.querySelector('.image-comparison');
}
if (!container) return;
if (container.dataset && container.dataset.mounted === 'true') return; if (container.dataset) container.dataset.mounted = 'true';
// Known filenames in /public/data/comparison
const FILES = {
'1': { query: 'id_1_query.png', 1: 'id_1_rank_1_sim_1.000.png', 2: 'id_1_rank_2_sim_0.165.png', 3: 'id_1_rank_3_sim_0.143.png' },
'2': { query: 'id_2_query.png', 1: 'id_2_rank_1_sim_1.000.png', 2: 'id_2_rank_2_sim_0.978.png', 3: 'id_2_rank_3_sim_0.975.png' },
'3': { query: 'id_3_query.png', 1: 'id_3_rank_1_sim_0.936.png', 2: 'id_3_rank_2_sim_0.686.png', 3: 'id_3_rank_3_sim_0.676.png' },
};
// Images served from [domain]/public/data/comparison/*.png → path is /data/comparison/
const CANDIDATE_BASES = [ '/data/comparison/' ];
const resolveBase = (candidates, filename) => new Promise((resolve) => {
let idx = 0; const tryNext = () => {
if (idx >= candidates.length) return resolve(candidates[0]);
const img = new Image();
img.onload = () => resolve(candidates[idx]);
img.onerror = () => { idx += 1; tryNext(); };
img.src = candidates[idx] + filename;
}; tryNext();
});
// Controls
const controls = document.createElement('div'); controls.className = 'controls';
const label = document.createElement('label'); label.textContent = 'Example';
const select = document.createElement('select');
const EXAMPLE_LABELS = { '1': 'Photo', '2': 'Chart', '3': 'Drawing' };
['1','2','3'].forEach((id)=>{ const o=document.createElement('option'); o.value=id; o.textContent=EXAMPLE_LABELS[id]; select.appendChild(o); });
label.appendChild(select); controls.appendChild(label); container.appendChild(controls);
// Grid
const grid = document.createElement('div'); grid.className = 'grid'; container.appendChild(grid);
let basePath = CANDIDATE_BASES[0];
const parseInfo = (filename) => {
const rankMatch = filename.match(/rank_(\d+)/i); const rank = rankMatch ? rankMatch[1] : '';
const simMatch = filename.match(/sim_([0-9.]+)/i); const sim = simMatch ? simMatch[1] : '';
return { rank, sim };
};
const formatSim = (val) => {
if (val == null || val === '') return '—';
return String(val).replace(/\.$/, '');
};
const render = (id) => {
const files = FILES[id]; if (!files) return;
const ordered = [files.query, files[1], files[2], files[3]]; // query, then matches 1, 2, 3
grid.innerHTML = '';
ordered.forEach((fname, idx) => {
const { sim } = parseInfo(fname);
const isQuery = idx === 0;
const card = document.createElement('div'); card.className = 'card';
const media = document.createElement('div'); media.className = 'media';
const img = document.createElement('img'); img.alt = `example ${id} ${isQuery ? 'query' : `match ${idx}`}`; img.loading = 'lazy'; img.src = basePath + fname; media.appendChild(img);
const meta = document.createElement('div'); meta.className = 'meta';
if (isQuery) {
const label = document.createElement('span'); label.className = 'value'; label.textContent = 'Query';
meta.appendChild(label);
} else {
const content = document.createElement('span');
content.innerHTML = `<span class="value">Match ${idx}</span><br><span class="label">Similarity: ${formatSim(sim)}</span>`;
meta.appendChild(content);
}
card.appendChild(media); card.appendChild(meta); grid.appendChild(card);
});
};
(async () => {
// Resolve a working base then initial render
basePath = await resolveBase(CANDIDATE_BASES, FILES['1'].query);
render('1');
})();
select.addEventListener('change', () => render(select.value));
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', bootstrap, { once: true });
} else { bootstrap(); }
})();
</script>