feat(web): add before/after image comparison slider component

Draggable split-view slider with clip-path reveals original vs processed
image. Shows file size badges with savings percentage. Supports pointer
events for both mouse and touch. Integrated into tool-page to replace
dropzone after processing completes. Updated file-store with
originalBlobUrl for comparison. Enhanced dropzone with multi-file count
badge and file list display.
This commit is contained in:
Siddharth Kumar Sah
2026-03-22 04:03:30 +08:00
parent 8964bc5cd8
commit 1a22522349
4 changed files with 242 additions and 12 deletions
+20 -4
View File
@@ -4,6 +4,8 @@ interface FileState {
files: File[];
jobId: string | null;
processedUrl: string | null;
/** Blob URL for the original image (for before/after comparison). */
originalBlobUrl: string | null;
processing: boolean;
error: string | null;
originalSize: number | null;
@@ -11,35 +13,49 @@ interface FileState {
setFiles: (files: File[]) => void;
setJobId: (id: string) => void;
setProcessedUrl: (url: string | null) => void;
setOriginalBlobUrl: (url: string | null) => void;
setProcessing: (v: boolean) => void;
setError: (e: string | null) => void;
setSizes: (original: number, processed: number) => void;
reset: () => void;
}
export const useFileStore = create<FileState>((set) => ({
export const useFileStore = create<FileState>((set, get) => ({
files: [],
jobId: null,
processedUrl: null,
originalBlobUrl: null,
processing: false,
error: null,
originalSize: null,
processedSize: null,
setFiles: (files) => set({ files, error: null }),
setFiles: (files) => {
// Revoke old blob URL if any
const old = get().originalBlobUrl;
if (old) URL.revokeObjectURL(old);
// Create a blob URL for the first file for before/after preview
const blobUrl = files.length > 0 ? URL.createObjectURL(files[0]) : null;
set({ files, error: null, originalBlobUrl: blobUrl });
},
setJobId: (id) => set({ jobId: id }),
setProcessedUrl: (url) => set({ processedUrl: url }),
setOriginalBlobUrl: (url) => set({ originalBlobUrl: url }),
setProcessing: (v) => set({ processing: v }),
setError: (e) => set({ error: e, processing: false }),
setSizes: (original, processed) =>
set({ originalSize: original, processedSize: processed }),
reset: () =>
reset: () => {
const old = get().originalBlobUrl;
if (old) URL.revokeObjectURL(old);
set({
files: [],
jobId: null,
processedUrl: null,
originalBlobUrl: null,
processing: false,
error: null,
originalSize: null,
processedSize: null,
}),
});
},
}));