mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat(adjustments): add real-time live preview for all color tools
Add CSS filter-based live preview for brightness, contrast, saturation, color effects (grayscale/sepia/invert), and color channels (via SVG feColorMatrix). The image in the right panel updates instantly as sliders are adjusted. Adds cssFilter prop to ImageViewer, onPreviewFilter callback to ColorSettings, and wires them through the tool page.
This commit is contained in:
@@ -67,17 +67,25 @@ const COLOR_TOOL_IDS = new Set([
|
||||
// Tools that don't need a file dropzone (they generate content or have custom UI)
|
||||
const NO_DROPZONE_TOOLS = new Set(["qr-generate"]);
|
||||
const SIDE_BY_SIDE_TOOLS = new Set(["resize", "crop", "rotate"]);
|
||||
const LIVE_PREVIEW_TOOLS = new Set(["rotate"]);
|
||||
const LIVE_PREVIEW_TOOLS = new Set([
|
||||
"rotate",
|
||||
"brightness-contrast",
|
||||
"saturation",
|
||||
"color-channels",
|
||||
"color-effects",
|
||||
]);
|
||||
const NO_COMPARISON_TOOLS = new Set(["strip-metadata", "convert"]);
|
||||
const INTERACTIVE_CROP_TOOLS = new Set(["crop"]);
|
||||
|
||||
function ToolSettingsPanel({
|
||||
toolId,
|
||||
onPreviewTransform,
|
||||
onPreviewFilter,
|
||||
cropProps,
|
||||
}: {
|
||||
toolId: string;
|
||||
onPreviewTransform?: (t: PreviewTransform) => void;
|
||||
onPreviewFilter?: (filter: string) => void;
|
||||
cropProps?: {
|
||||
cropState: {
|
||||
crop: Crop;
|
||||
@@ -97,7 +105,8 @@ function ToolSettingsPanel({
|
||||
if (toolId === "convert") return <ConvertSettings />;
|
||||
if (toolId === "compress") return <CompressSettings />;
|
||||
if (toolId === "strip-metadata") return <StripMetadataSettings />;
|
||||
if (COLOR_TOOL_IDS.has(toolId)) return <ColorSettings toolId={toolId} />;
|
||||
if (COLOR_TOOL_IDS.has(toolId))
|
||||
return <ColorSettings toolId={toolId} onPreviewFilter={onPreviewFilter} />;
|
||||
// Phase 3: Watermark & Overlay
|
||||
if (toolId === "watermark-text") return <WatermarkTextSettings />;
|
||||
if (toolId === "watermark-image") return <WatermarkImageSettings />;
|
||||
@@ -229,6 +238,7 @@ export function ToolPage() {
|
||||
);
|
||||
const [mobileSettingsOpen, setMobileSettingsOpen] = useState(true);
|
||||
const [previewTransform, setPreviewTransform] = useState<PreviewTransform | null>(null);
|
||||
const [previewFilter, setPreviewFilter] = useState<string>("");
|
||||
|
||||
const [cropCrop, setCropCrop] = useState<Crop>({
|
||||
unit: "%",
|
||||
@@ -364,6 +374,7 @@ export function ToolPage() {
|
||||
onPreviewTransform={
|
||||
LIVE_PREVIEW_TOOLS.has(tool.id) ? setPreviewTransform : undefined
|
||||
}
|
||||
onPreviewFilter={LIVE_PREVIEW_TOOLS.has(tool.id) ? setPreviewFilter : undefined}
|
||||
cropProps={
|
||||
INTERACTIVE_CROP_TOOLS.has(tool.id)
|
||||
? {
|
||||
@@ -465,6 +476,9 @@ export function ToolPage() {
|
||||
cssFlipV: previewTransform.flipV,
|
||||
}
|
||||
: {})}
|
||||
{...(LIVE_PREVIEW_TOOLS.has(tool.id) && previewFilter
|
||||
? { cssFilter: previewFilter }
|
||||
: {})}
|
||||
/>
|
||||
) : (
|
||||
<Dropzone onFiles={handleFiles} accept="image/*" multiple currentFiles={files} />
|
||||
@@ -533,6 +547,7 @@ export function ToolPage() {
|
||||
<ToolSettingsPanel
|
||||
toolId={tool.id}
|
||||
onPreviewTransform={LIVE_PREVIEW_TOOLS.has(tool.id) ? setPreviewTransform : undefined}
|
||||
onPreviewFilter={LIVE_PREVIEW_TOOLS.has(tool.id) ? setPreviewFilter : undefined}
|
||||
cropProps={
|
||||
INTERACTIVE_CROP_TOOLS.has(tool.id)
|
||||
? {
|
||||
@@ -648,6 +663,9 @@ export function ToolPage() {
|
||||
cssFlipV: previewTransform.flipV,
|
||||
}
|
||||
: {})}
|
||||
{...(LIVE_PREVIEW_TOOLS.has(tool.id) && previewFilter
|
||||
? { cssFilter: previewFilter }
|
||||
: {})}
|
||||
/>
|
||||
) : (
|
||||
<Dropzone onFiles={handleFiles} accept="image/*" multiple currentFiles={files} />
|
||||
|
||||
Reference in New Issue
Block a user