diff --git a/frontend/views/GenSpace.tsx b/frontend/views/GenSpace.tsx index 7e750269..01b05457 100644 --- a/frontend/views/GenSpace.tsx +++ b/frontend/views/GenSpace.tsx @@ -359,6 +359,11 @@ function PromptBar({ onIcLoraCondTypeChange, icLoraStrength, onIcLoraStrengthChange, + seedLocked, + lockedSeed, + onSeedLockedChange, + onLockedSeedChange, + onRandomizeSeed, }: { mode: 'image' | 'video' | 'retake' | 'ic-lora' onModeChange: (mode: 'image' | 'video' | 'retake' | 'ic-lora') => void @@ -390,6 +395,11 @@ function PromptBar({ onIcLoraCondTypeChange?: (type: ICLoraConditioningType) => void icLoraStrength?: number onIcLoraStrengthChange?: (strength: number) => void + seedLocked: boolean + lockedSeed: number + onSeedLockedChange: (locked: boolean) => void + onLockedSeedChange: (seed: number) => void + onRandomizeSeed: () => void }) { const inputRef = useRef(null) const audioInputRef = useRef(null) @@ -407,6 +417,51 @@ function PromptBar({ : ['540p', '720p', '1080p'] const videoFpsOptions = shouldVideoGenerateWithLtxApi ? [...FORCED_API_VIDEO_FPS] : [24, 25, 50] + const seedControls = ( + <> +
+ onSeedLockedChange(value === 'fixed')} + options={[ + { value: 'random', label: 'Random Seed' }, + { value: 'fixed', label: 'Fixed Seed' }, + ]} + trigger={ + <> + SEED + {seedLocked ? 'Fixed' : 'Random'} + + + } + /> + {seedLocked && ( +
+ onLockedSeedChange(parseInt(e.target.value, 10) || 0)} + className="w-12 bg-transparent text-zinc-300 font-medium focus:outline-none" + placeholder="S" + /> + +
+ )} + + ) + const handleDrop = (e: React.DragEvent) => { e.preventDefault() setIsDragOver(false) @@ -569,7 +624,7 @@ function PromptBar({
{/* Bottom row: Mode selector + Settings */} -
+
{/* Mode dropdown */} {isRetake ? ( -
Trim in the panel above, then retake
+ <> +
Trim in the panel above, then retake
+ {seedControls} + ) : isIcLora ? ( <> } /> + {seedControls} ) : mode === 'image' ? ( <> @@ -673,7 +732,7 @@ function PromptBar({ } /> - + {seedControls} ) : ( <> @@ -771,7 +830,7 @@ function PromptBar({ } /> - + {seedControls} )} @@ -879,7 +938,7 @@ export function GenSpace() { setGenSpaceIcLoraSource, setPendingIcLoraUpdate, } = useProjects() - const { shouldVideoGenerateWithLtxApi, forceApiGenerations, settings: appSettings } = useAppSettings() + const { shouldVideoGenerateWithLtxApi, forceApiGenerations, settings: appSettings, updateSettings } = useAppSettings() const [mode, setMode] = useState<'image' | 'video' | 'retake' | 'ic-lora'>('video') const [prompt, setPrompt] = useState('') const [inputImage, setInputImage] = useState(null) @@ -910,6 +969,22 @@ export function GenSpace() { } } | null>(null) const [settings, setSettings] = useState(() => ({ ...DEFAULT_VIDEO_SETTINGS })) + const handleSeedLockedChange = useCallback((seedLocked: boolean) => { + updateSettings((prev) => ({ ...prev, seedLocked })) + }, [updateSettings]) + const handleLockedSeedChange = useCallback((lockedSeed: number) => { + updateSettings((prev) => ({ + ...prev, + lockedSeed: Math.max(0, Math.min(2147483647, lockedSeed)), + })) + }, [updateSettings]) + const handleRandomizeSeed = useCallback(() => { + updateSettings((prev) => ({ + ...prev, + seedLocked: true, + lockedSeed: Math.floor(Math.random() * 2147483647), + })) + }, [updateSettings]) const applyForcedVideoSettings = useCallback( (next: { model: string; duration: number; videoResolution: string; fps: number; audio: boolean; aspectRatio: string; imageResolution: string; variations: number }) => { if (!shouldVideoGenerateWithLtxApi || mode !== 'video') return next @@ -1714,6 +1789,11 @@ export function GenSpace() { onIcLoraCondTypeChange={setIcLoraCondType} icLoraStrength={icLoraStrength} onIcLoraStrengthChange={setIcLoraStrength} + seedLocked={appSettings.seedLocked} + lockedSeed={appSettings.lockedSeed} + onSeedLockedChange={handleSeedLockedChange} + onLockedSeedChange={handleLockedSeedChange} + onRandomizeSeed={handleRandomizeSeed} />