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}