import { Download } from "lucide-react"; import { useEffect, useRef, useState } from "react"; import { ProgressCard } from "@/components/common/progress-card"; import { useTranslation } from "@/contexts/i18n-context"; import { useToolProcessor } from "@/hooks/use-tool-processor"; import { useFileStore } from "@/stores/file-store"; const PRESETS: { label: string; shadow: string; highlight: string }[] = [ { label: "Classic", shadow: "#1e3a8a", highlight: "#fbbf24" }, { label: "Noir", shadow: "#000000", highlight: "#ffffff" }, { label: "Sepia", shadow: "#2d1b00", highlight: "#ffe8c2" }, { label: "Midnight", shadow: "#0f2027", highlight: "#78ffd6" }, { label: "Sunset", shadow: "#3a1c71", highlight: "#ffaf7b" }, { label: "Cyber", shadow: "#0f0c29", highlight: "#f857a6" }, { label: "Ocean", shadow: "#000428", highlight: "#43cea2" }, { label: "Forest", shadow: "#022c12", highlight: "#9be15d" }, ]; interface DuotoneSettingsProps { onImageStyle?: (style: React.CSSProperties | null) => void; onImageOverlay?: (node: React.ReactNode) => void; } export function DuotoneSettings({ onImageStyle, onImageOverlay }: DuotoneSettingsProps) { const { t } = useTranslation(); const { files } = useFileStore(); const entry = useFileStore((s) => s.entries[s.selectedIndex]); const blobUrl = entry?.blobUrl; const { processFiles, processAllFiles, processing, error, downloadUrl, progress } = useToolProcessor("duotone"); const [shadow, setShadow] = useState("#1e3a8a"); const [highlight, setHighlight] = useState("#fbbf24"); const [intensity, setIntensity] = useState(100); // Stable refs for preview callbacks to avoid stale closures const onImageStyleRef = useRef(onImageStyle); useEffect(() => { onImageStyleRef.current = onImageStyle; }); const onImageOverlayRef = useRef(onImageOverlay); useEffect(() => { onImageOverlayRef.current = onImageOverlay; }); // Live preview: a self-contained duotone overlay (its own grayscale copy of // the image plus lighten/darken color layers, isolated so the parent pane // filter can't re-gray it). Intensity fades toward the real color image // beneath, matching the backend blend. onImageStyle activates the wrapper // branch in image-viewer so the overlay children actually mount. useEffect(() => { if (!blobUrl) return; onImageStyleRef.current?.({}); onImageOverlayRef.current?.(