feat: overhaul remove-background with effects pipeline, consolidate color tools

Remove Background:
- Two-phase flow: AI removes bg once, then effects adjust instantly
- Blur background effect with real-time CSS preview (portrait mode)
- Drop shadow effect with opacity control
- Gradient backgrounds with presets, custom colors, and angle
- Custom background image upload (including HEIC/HEIF)
- Solid color backgrounds moved from Python to Node.js/Sharp
- Effects-only API endpoint for instant re-renders without AI re-run
- HEIC/HEIF input support (decoded before passing to Python/rembg)
- Passport/ID photo checkbox defaults ON for People subject
- Before/after slider preserved when no effects active
- 15 comprehensive Playwright e2e tests

Color Tools:
- Consolidated 4 tools (brightness-contrast, saturation, color-channels,
  color-effects) into single "Adjust Colors" tool
- Added exposure, temperature, tint, hue, sharpness controls
- SVG filter-based live preview for all adjustments
- Backward-compatible URL redirects from old tool paths

Other fixes:
- Favicon tool: download button instead of auto-download
- Batch processing: HEIC filename extension fix
- File store: processedFilename field for proper batch downloads
This commit is contained in:
Siddharth Kumar Sah
2026-04-12 17:53:16 +08:00
parent dde70f70ad
commit 6c58f12262
28 changed files with 2236 additions and 525 deletions
+5
View File
@@ -7,6 +7,7 @@ export interface FileEntry {
previewLoading: boolean;
processedUrl: string | null;
processedPreviewUrl: string | null;
processedFilename: string | null;
processedSize: number | null;
originalSize: number;
status: "pending" | "processing" | "completed" | "failed";
@@ -25,6 +26,7 @@ function createEntry(file: File): FileEntry {
previewLoading: needsServerPreview(file),
processedUrl: null,
processedPreviewUrl: null,
processedFilename: null,
processedSize: null,
originalSize: file.size,
status: "pending",
@@ -280,6 +282,7 @@ export const useFileStore = create<FileState>((set, get) => ({
...updated[selectedIndex],
processedUrl: url,
processedPreviewUrl: previewUrl ?? null,
processedFilename: null,
status: "completed",
};
} else {
@@ -287,6 +290,7 @@ export const useFileStore = create<FileState>((set, get) => ({
...updated[selectedIndex],
processedUrl: null,
processedPreviewUrl: null,
processedFilename: null,
status: "pending",
};
}
@@ -314,6 +318,7 @@ export const useFileStore = create<FileState>((set, get) => ({
...e,
processedUrl: null,
processedPreviewUrl: null,
processedFilename: null,
processedSize: null,
status: "pending" as const,
error: null,