mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat(web): remove mode selector, add colorize strength slider
This commit is contained in:
@@ -4,14 +4,6 @@ import { ProgressCard } from "@/components/common/progress-card";
|
|||||||
import { useToolProcessor } from "@/hooks/use-tool-processor";
|
import { useToolProcessor } from "@/hooks/use-tool-processor";
|
||||||
import { useFileStore } from "@/stores/file-store";
|
import { useFileStore } from "@/stores/file-store";
|
||||||
|
|
||||||
type Mode = "auto" | "light" | "heavy";
|
|
||||||
|
|
||||||
const MODES: { id: Mode; label: string; desc: string }[] = [
|
|
||||||
{ id: "light", label: "Light", desc: "Gentle touch, preserves details" },
|
|
||||||
{ id: "auto", label: "Auto", desc: "Balanced restoration" },
|
|
||||||
{ id: "heavy", label: "Heavy", desc: "Aggressive repair for severe damage" },
|
|
||||||
];
|
|
||||||
|
|
||||||
export interface RestorePhotoControlsProps {
|
export interface RestorePhotoControlsProps {
|
||||||
settings?: Record<string, unknown>;
|
settings?: Record<string, unknown>;
|
||||||
onChange?: (settings: Record<string, unknown>) => void;
|
onChange?: (settings: Record<string, unknown>) => void;
|
||||||
@@ -21,20 +13,19 @@ export function RestorePhotoControls({
|
|||||||
settings: initialSettings,
|
settings: initialSettings,
|
||||||
onChange,
|
onChange,
|
||||||
}: RestorePhotoControlsProps) {
|
}: RestorePhotoControlsProps) {
|
||||||
const [mode, setMode] = useState<Mode>("auto");
|
|
||||||
const [scratchRemoval, setScratchRemoval] = useState(true);
|
const [scratchRemoval, setScratchRemoval] = useState(true);
|
||||||
const [faceEnhancement, setFaceEnhancement] = useState(true);
|
const [faceEnhancement, setFaceEnhancement] = useState(true);
|
||||||
const [fidelity, setFidelity] = useState(70);
|
const [fidelity, setFidelity] = useState(70);
|
||||||
const [denoise, setDenoise] = useState(true);
|
const [denoise, setDenoise] = useState(true);
|
||||||
const [denoiseStrength, setDenoiseStrength] = useState(40);
|
const [denoiseStrength, setDenoiseStrength] = useState(25);
|
||||||
const [colorize, setColorize] = useState(false);
|
const [colorize, setColorize] = useState(false);
|
||||||
|
const [colorizeStrength, setColorizeStrength] = useState(85);
|
||||||
|
|
||||||
// One-time init from pipeline settings
|
// One-time init from pipeline settings
|
||||||
const initializedRef = useRef(false);
|
const initializedRef = useRef(false);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!initialSettings || initializedRef.current) return;
|
if (!initialSettings || initializedRef.current) return;
|
||||||
initializedRef.current = true;
|
initializedRef.current = true;
|
||||||
if (initialSettings.mode != null) setMode(initialSettings.mode as Mode);
|
|
||||||
if (initialSettings.scratchRemoval != null)
|
if (initialSettings.scratchRemoval != null)
|
||||||
setScratchRemoval(Boolean(initialSettings.scratchRemoval));
|
setScratchRemoval(Boolean(initialSettings.scratchRemoval));
|
||||||
if (initialSettings.faceEnhancement != null)
|
if (initialSettings.faceEnhancement != null)
|
||||||
@@ -44,6 +35,8 @@ export function RestorePhotoControls({
|
|||||||
if (initialSettings.denoiseStrength != null)
|
if (initialSettings.denoiseStrength != null)
|
||||||
setDenoiseStrength(Number(initialSettings.denoiseStrength));
|
setDenoiseStrength(Number(initialSettings.denoiseStrength));
|
||||||
if (initialSettings.colorize != null) setColorize(Boolean(initialSettings.colorize));
|
if (initialSettings.colorize != null) setColorize(Boolean(initialSettings.colorize));
|
||||||
|
if (initialSettings.colorizeStrength != null)
|
||||||
|
setColorizeStrength(Number(initialSettings.colorizeStrength));
|
||||||
}, [initialSettings]);
|
}, [initialSettings]);
|
||||||
|
|
||||||
// Emit settings on change
|
// Emit settings on change
|
||||||
@@ -54,44 +47,26 @@ export function RestorePhotoControls({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onChangeRef.current?.({
|
onChangeRef.current?.({
|
||||||
mode,
|
|
||||||
scratchRemoval,
|
scratchRemoval,
|
||||||
faceEnhancement,
|
faceEnhancement,
|
||||||
fidelity: fidelity / 100,
|
fidelity: fidelity / 100,
|
||||||
denoise,
|
denoise,
|
||||||
denoiseStrength,
|
denoiseStrength,
|
||||||
colorize,
|
colorize,
|
||||||
|
colorizeStrength,
|
||||||
});
|
});
|
||||||
}, [mode, scratchRemoval, faceEnhancement, fidelity, denoise, denoiseStrength, colorize]);
|
}, [
|
||||||
|
scratchRemoval,
|
||||||
const activeMode = MODES.find((m) => m.id === mode);
|
faceEnhancement,
|
||||||
|
fidelity,
|
||||||
|
denoise,
|
||||||
|
denoiseStrength,
|
||||||
|
colorize,
|
||||||
|
colorizeStrength,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{/* Mode selector */}
|
|
||||||
<div>
|
|
||||||
<p className="text-xs text-muted-foreground mb-1">Restoration Mode</p>
|
|
||||||
<div className="grid grid-cols-3 gap-1">
|
|
||||||
{MODES.map((m) => (
|
|
||||||
<button
|
|
||||||
key={m.id}
|
|
||||||
type="button"
|
|
||||||
onClick={() => setMode(m.id)}
|
|
||||||
className={`flex flex-col items-center gap-0.5 text-xs py-2 rounded transition-colors ${
|
|
||||||
mode === m.id
|
|
||||||
? "bg-primary text-primary-foreground"
|
|
||||||
: "bg-muted text-muted-foreground hover:bg-muted/80"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{m.label}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
{activeMode && <p className="text-[10px] text-muted-foreground mt-1">{activeMode.desc}</p>}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="border-t border-border pt-3" />
|
|
||||||
|
|
||||||
{/* Feature toggles */}
|
{/* Feature toggles */}
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{/* Scratch Removal */}
|
{/* Scratch Removal */}
|
||||||
@@ -205,6 +180,29 @@ export function RestorePhotoControls({
|
|||||||
className="h-4 w-4 rounded border-muted-foreground accent-primary"
|
className="h-4 w-4 rounded border-muted-foreground accent-primary"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
{/* Colorize strength slider */}
|
||||||
|
{colorize && (
|
||||||
|
<div className="pl-2 border-l-2 border-primary/20">
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<p className="text-xs text-muted-foreground">Colorize Strength</p>
|
||||||
|
<span className="text-xs font-mono tabular-nums">{colorizeStrength}%</span>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
min={0}
|
||||||
|
max={100}
|
||||||
|
step={5}
|
||||||
|
value={colorizeStrength}
|
||||||
|
onChange={(e) => setColorizeStrength(Number(e.target.value))}
|
||||||
|
className="w-full h-1.5 rounded-full appearance-none bg-muted accent-primary"
|
||||||
|
/>
|
||||||
|
<div className="flex justify-between text-[10px] text-muted-foreground mt-0.5">
|
||||||
|
<span>Subtle</span>
|
||||||
|
<span>Vivid</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -201,6 +201,7 @@ export const en = {
|
|||||||
},
|
},
|
||||||
pipeline: { name: "Pipeline Builder", description: "Chain multiple tools into a workflow" },
|
pipeline: { name: "Pipeline Builder", description: "Chain multiple tools into a workflow" },
|
||||||
},
|
},
|
||||||
|
colorizeStrength: "Colorize Strength",
|
||||||
imageEnhancement: {
|
imageEnhancement: {
|
||||||
deepEnhance: "Deep Enhance (AI)",
|
deepEnhance: "Deep Enhance (AI)",
|
||||||
deepEnhanceDescription: "Removes noise and artifacts using AI",
|
deepEnhanceDescription: "Removes noise and artifacts using AI",
|
||||||
|
|||||||
Reference in New Issue
Block a user