From d7bfe6b5964ea736876eb62bcf2b8addf2a3fe23 Mon Sep 17 00:00:00 2001 From: Masahiko AMANO Date: Mon, 22 Jun 2026 22:13:38 +0300 Subject: [PATCH] feat(frontend): enlarge cluster images in a preview lightbox MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The duplicate cluster view only showed 96px thumbnails, too small to tell near-duplicates apart. Add a zoom button per tile that opens a lightbox showing the full preview — the same image the single-file viewer uses — and pages across the cluster with arrows/Esc. The zoom button stops propagation so it doesn't also reassign the cluster's "keep" selection. --- .../components/file/PreviewLightbox.svelte | 252 ++++++++++++++++++ .../src/routes/files/duplicates/+page.svelte | 80 +++++- 2 files changed, 331 insertions(+), 1 deletion(-) create mode 100644 frontend/src/lib/components/file/PreviewLightbox.svelte diff --git a/frontend/src/lib/components/file/PreviewLightbox.svelte b/frontend/src/lib/components/file/PreviewLightbox.svelte new file mode 100644 index 0000000..fc0a526 --- /dev/null +++ b/frontend/src/lib/components/file/PreviewLightbox.svelte @@ -0,0 +1,252 @@ + + + + + + + + + diff --git a/frontend/src/routes/files/duplicates/+page.svelte b/frontend/src/routes/files/duplicates/+page.svelte index 6e0ec0f..93ab502 100644 --- a/frontend/src/routes/files/duplicates/+page.svelte +++ b/frontend/src/routes/files/duplicates/+page.svelte @@ -4,6 +4,7 @@ import { getDuplicates, dismissDuplicate, type DuplicateCluster } from '$lib/api/duplicates'; import Thumb from '$lib/components/file/Thumb.svelte'; import DuplicateMergeDialog from '$lib/components/file/DuplicateMergeDialog.svelte'; + import PreviewLightbox from '$lib/components/file/PreviewLightbox.svelte'; import type { File } from '$lib/api/types'; const LIMIT = 20; @@ -22,6 +23,10 @@ let mergeKeep = $state(null); let mergeDiscard = $state(null); + // Enlarged-preview lightbox: thumbnails are too small to tell near-duplicates + // apart, so a zoom opens the full preview and pages across the cluster. + let lightbox = $state<{ files: File[]; startId: string } | null>(null); + $effect(() => { if (!initialLoaded && !loading) void load(); }); @@ -61,6 +66,10 @@ keepers = { ...keepers, [clusterKey(c)]: id }; } + function openLightbox(c: DuplicateCluster, startId: string) { + lightbox = { files: c.files, startId }; + } + function openMerge(c: DuplicateCluster, other: File) { const keep = c.files.find((f) => f.id === keeperId(c)); if (!keep) return; @@ -160,7 +169,34 @@ onclick={() => setKeeper(c, f.id)} title="Click to keep this one" > - +
+ + +
{#if f.id === keep}Keep{/if} {f.original_name ?? '—'} {f.mime_type} · {f.tags?.length ?? 0} tags @@ -191,6 +227,14 @@ +{#if lightbox} + (lightbox = null)} + /> +{/if} + {#if mergeKeep && mergeDiscard}