diff --git a/components/canvas/EnhanceProWorkspace.tsx b/components/canvas/EnhanceProWorkspace.tsx index 3b029195f..1591338da 100644 --- a/components/canvas/EnhanceProWorkspace.tsx +++ b/components/canvas/EnhanceProWorkspace.tsx @@ -2,6 +2,7 @@ import { CheckCircle2, + ChevronDown, ImageIcon, Loader2, Send, @@ -17,6 +18,11 @@ import { import { toast } from "sonner"; import useSWR from "swr"; import { Button } from "@/components/ui/button"; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "@/components/ui/collapsible"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { @@ -27,6 +33,7 @@ import { SelectValue, } from "@/components/ui/select"; import { Switch } from "@/components/ui/switch"; +import { Slider } from "@/components/ui/slider"; import { Textarea } from "@/components/ui/textarea"; import { defaultEnhanceProPresets, @@ -111,6 +118,10 @@ const COMPACT_FIELD_CLASS = `${SOLID_PANEL_FIELD_CLASS} h-10 rounded-[16px] text const COMPACT_LABEL_CLASS = "text-slate-600 text-[11px] leading-4"; const COMPACT_TOGGLE_CARD_CLASS = "flex items-center justify-between gap-2.5 px-3 py-2.5"; +const SLIDER_TRACK_CLASS = "h-2 bg-[#ece7da] shadow-inner"; +const SLIDER_RANGE_CLASS = "bg-[#3b82f6]"; +const SLIDER_THUMB_CLASS = + "h-4 w-4 border border-[#d8e4ff] bg-white shadow-[0_0_0_4px_rgba(59,130,246,0.16)]"; function clampNumber(value: number, min: number, max: number) { return Math.min(max, Math.max(min, value)); @@ -235,6 +246,53 @@ function getActiveResultLabel(index: number) { return `结果 ${index + 1}`; } +type ParameterSliderRowProps = { + label: string; + value: number; + min: number; + max: number; + step: number; + onChange: (value: number) => void; + formatValue?: (value: number) => string; + disabled?: boolean; +}; + +function ParameterSliderRow({ + label, + value, + min, + max, + step, + onChange, + formatValue, + disabled = false, +}: ParameterSliderRowProps) { + const displayValue = formatValue ? formatValue(value) : String(value); + + return ( +
+
+
{label}
+
+ {displayValue} +
+
+ onChange(next ?? value)} + rangeClassName={SLIDER_RANGE_CLASS} + step={step} + thumbClassName={SLIDER_THUMB_CLASS} + trackClassName={SLIDER_TRACK_CLASS} + value={[value]} + /> +
+ ); +} + function buildEnhanceSourcePayload( sourceImageUrl?: string | null ): Pick< @@ -288,6 +346,9 @@ export function EnhanceProWorkspace({ EnhanceProImageResult[] >([]); const [lastErrorMessage, setLastErrorMessage] = useState(""); + const [settingsOpen, setSettingsOpen] = useState(true); + const [adjustmentsOpen, setAdjustmentsOpen] = useState(true); + const [faceOpen, setFaceOpen] = useState(true); const [previewOffset, setPreviewOffset] = useState({ x: 0, y: 0 }); const [isPreviewDragging, setIsPreviewDragging] = useState(false); const previewDragStateRef = useRef<{ @@ -377,6 +438,12 @@ export function EnhanceProWorkspace({ previewDragStateRef.current = null; }, [activePreviewUrl]); + useEffect(() => { + if (formState.face_enhancement) { + setFaceOpen(true); + } + }, [formState.face_enhancement]); + const handleNumericField = ( key: K, nextValue: number @@ -570,7 +637,7 @@ export function EnhanceProWorkspace({ return (
-
+
{activePreviewUrl ? (
) : null} + +
+
+ + {selectedGeneratedImage ? ( + + ) : null} + {actionHint} + +
+
+ + +
+
-
+
{isLoading ? (
) : null} -
-
- - - {selectedPreset ? ( -
- {selectedPreset.description || - `${selectedPreset.model} · ${getSubjectDetectionLabel(selectedPreset.subjectDetection)}`} -
- ) : null} -
- -
-