feat(web): add Stirling-PDF-style file preview, review panel, and tool chaining UX

After upload: dropzone replaced by image viewer with zoom controls.
After processing: review panel shows result preview, file info, download,
undo button, and suggested next tools for chaining.
This commit is contained in:
Siddharth Kumar Sah
2026-03-22 10:47:46 +08:00
parent 80bb0e29a0
commit ab0b0344b9
6 changed files with 528 additions and 53 deletions
+25 -1
View File
@@ -10,6 +10,8 @@ interface FileState {
error: string | null;
originalSize: number | null;
processedSize: number | null;
selectedFileName: string | null;
selectedFileSize: number | null;
setFiles: (files: File[]) => void;
setJobId: (id: string) => void;
setProcessedUrl: (url: string | null) => void;
@@ -17,6 +19,8 @@ interface FileState {
setProcessing: (v: boolean) => void;
setError: (e: string | null) => void;
setSizes: (original: number, processed: number) => void;
/** Clear processed result but keep the original uploaded file. */
undoProcessing: () => void;
reset: () => void;
}
@@ -29,13 +33,23 @@ export const useFileStore = create<FileState>((set, get) => ({
error: null,
originalSize: null,
processedSize: null,
selectedFileName: null,
selectedFileSize: 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 });
const firstName = files.length > 0 ? files[0].name : null;
const firstSize = files.length > 0 ? files[0].size : null;
set({
files,
error: null,
originalBlobUrl: blobUrl,
selectedFileName: firstName,
selectedFileSize: firstSize,
});
},
setJobId: (id) => set({ jobId: id }),
setProcessedUrl: (url) => set({ processedUrl: url }),
@@ -44,6 +58,14 @@ export const useFileStore = create<FileState>((set, get) => ({
setError: (e) => set({ error: e, processing: false }),
setSizes: (original, processed) =>
set({ originalSize: original, processedSize: processed }),
undoProcessing: () => {
set({
processedUrl: null,
jobId: null,
processedSize: null,
error: null,
});
},
reset: () => {
const old = get().originalBlobUrl;
if (old) URL.revokeObjectURL(old);
@@ -56,6 +78,8 @@ export const useFileStore = create<FileState>((set, get) => ({
error: null,
originalSize: null,
processedSize: null,
selectedFileName: null,
selectedFileSize: null,
});
},
}));