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 @@
+
+
+
+
+
+
+
{
+ if (e.target === e.currentTarget) onClose();
+ }}
+>
+
+
+
+ {#if src}
+
![{current?.original_name]()
+ {:else if failed}
+
Failed to load preview
+ {:else}
+
Loading…
+ {/if}
+
+ {#if hasPrev}
+
+ {/if}
+ {#if hasNext}
+
+ {/if}
+
+
+
+ {current?.original_name ?? '—'}
+ {index + 1} / {files.length}
+
+
+
+
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}