feat(web): add tool settings UI for all core image tools

Adds Zustand file store, useToolProcessor hook for upload/process/download
flow, and 7 settings components: resize (with social media presets), crop
(with aspect ratio presets), rotate/flip, convert, compress (quality +
target size modes), strip-metadata, and color adjustments (brightness,
contrast, saturation, channels, effects). Updates tool-page to render the
appropriate settings panel based on toolId.
This commit is contained in:
Siddharth Kumar Sah
2026-03-22 03:56:44 +08:00
parent 37112af779
commit 8964bc5cd8
10 changed files with 1335 additions and 21 deletions
+81
View File
@@ -0,0 +1,81 @@
import { useCallback } from "react";
import { useFileStore } from "@/stores/file-store";
function getToken(): string {
return localStorage.getItem("stirling-token") || "";
}
interface ProcessResult {
jobId: string;
downloadUrl: string;
originalSize: number;
processedSize: number;
}
export function useToolProcessor(toolId: string) {
const {
processing,
error,
processedUrl,
originalSize,
processedSize,
setProcessing,
setError,
setProcessedUrl,
setSizes,
setJobId,
} = useFileStore();
const processFiles = useCallback(
async (files: File[], settings: Record<string, unknown>) => {
if (files.length === 0) {
setError("No files selected");
return;
}
setProcessing(true);
setError(null);
setProcessedUrl(null);
try {
// Build multipart form with the file and settings
const formData = new FormData();
formData.append("file", files[0]);
formData.append("settings", JSON.stringify(settings));
const res = await fetch(`/api/v1/tools/${toolId}`, {
method: "POST",
headers: { Authorization: `Bearer ${getToken()}` },
body: formData,
});
if (!res.ok) {
const body = await res.json().catch(() => ({}));
throw new Error(
body.error || body.details || `Processing failed: ${res.status}`,
);
}
const result: ProcessResult = await res.json();
setJobId(result.jobId);
setProcessedUrl(result.downloadUrl);
setSizes(result.originalSize, result.processedSize);
} catch (err) {
setError(err instanceof Error ? err.message : "Processing failed");
} finally {
setProcessing(false);
}
},
[toolId, setProcessing, setError, setProcessedUrl, setSizes, setJobId],
);
return {
processFiles,
processing,
error,
downloadUrl: processedUrl,
originalSize,
processedSize,
};
}