import { useRef, useState } from "react"; import { useVirtualizer } from "@tanstack/react-virtual"; import { DuplicateGroup, useGalleryStore } from "../store"; import { FolderScopeDropdown } from "./FolderScopeDropdown"; import { mediaSrc } from "../lib/mediaSrc"; function formatBytes(bytes: number): string { if (bytes >= 1_073_741_824) return `${(bytes / 1_073_741_824).toFixed(1)} GB`; if (bytes >= 1_048_576) return `${(bytes / 1_048_576).toFixed(1)} MB`; if (bytes >= 1024) return `${(bytes / 1024).toFixed(0)} KB`; return `${bytes} B`; } function DuplicateGroupCard({ group }: { group: DuplicateGroup }) { const selectedIds = useGalleryStore((state) => state.duplicateSelectedIds); const toggleDuplicateSelected = useGalleryStore((state) => state.toggleDuplicateSelected); const selectAllDuplicates = useGalleryStore((state) => state.selectAllDuplicates); const groupSelectedCount = group.images.filter((img) => selectedIds.has(img.id)).length; const noneSelected = groupSelectedCount === 0; // "Keep all but the first" — a common quick action const handleKeepFirst = () => { const toDelete = group.images.slice(1).map((img) => img.id); // Clear any selection for this group first, then add the ones to delete for (const img of group.images) { if (selectedIds.has(img.id)) toggleDuplicateSelected(img.id); } selectAllDuplicates(toDelete); }; return (
{duplicateScanning ? duplicateScanProgress ? `${progressLabel}… ${duplicateScanProgress.processed.toLocaleString()} / ${duplicateScanProgress.total.toLocaleString()}${duplicateScanProgress.skipped > 0 ? ` · ${duplicateScanProgress.skipped.toLocaleString()} skipped` : ""}` : "Starting scan…" : hasResults ? `${duplicateGroups.length} group${duplicateGroups.length === 1 ? "" : "s"} · ${formatBytes(totalWasted)} reclaimable` : duplicateLastScanned !== null ? "No duplicates found" : "Scan your library for identical files"}
{!duplicateScanning && duplicateLastScanned !== null && (Last scanned {formatRelativeTime(duplicateLastScanned)}
)}Delete from disk
Permanently delete {selectedCount} file{selectedCount === 1 ? "" : "s"} from your computer. This removes the actual file{selectedCount === 1 ? "" : "s"} from disk and cannot be undone.
{duplicateScanError}
) : null} {duplicateScanWarning ? ({duplicateScanWarning}
) : null} {deleteResult ? ({deleteResult}
) : null}Finds files with identical content regardless of filename or location. Click Scan for duplicates to begin.
Large libraries may take a minute — files are hashed from disk.
No duplicate files found.