import { type ChangeEvent, useMemo, useState } from 'react';
import { FolderClock, Layers3, SlidersHorizontal, Sparkles, WandSparkles, Workflow } from 'lucide-react';
import { useDropzone } from 'react-dropzone';
import { useGenerationActions } from '../hooks/use-generation-actions';
import { cn } from '../lib/utils';
import { useStore } from '../store/useStore';
import type { GenerationSettings as GenerationSettingsShape } from '../types';
import { ImageInput } from './ImageInput';
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from './ui/accordion';
import { Button } from './ui/button';
import { Input } from './ui/input';
import { Label } from './ui/label';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from './ui/select';
import { ScrollArea } from './ui/scroll-area';
import { Switch } from './ui/switch';
import { useShallow } from 'zustand/react/shallow';
type FeedbackTone = 'success' | 'warning' | 'error';
type FeedbackState = {
tone: FeedbackTone;
text: string;
};
type SelectOption = {
value: string;
label: string;
};
const samplerOptions = [
'dpmpp_2m',
'dpmpp_2m_cfgpp',
'dpmpp_sde',
'dpmpp_sde_cfgpp',
'euler',
'euler_cfgpp',
'euler_ancestral',
'euler_ancestral_cfgpp',
];
const schedulerOptions = ['karras', 'exponential', 'sgm_uniform', 'simple', 'normal', 'ays'];
const controlTypes = ['canny', 'depth', 'pose', 'softedge'];
const multiscalePresets = ['balanced', 'detailed', 'creative', 'disabled'];
function Field({
label,
description,
children,
}: {
label: string;
description?: string;
children: React.ReactNode;
}) {
return (
{description ?
{description}
: null}
{children}
);
}
function FeatureSwitch({
checked,
description,
disabled,
label,
onCheckedChange,
}: {
checked: boolean;
description?: string;
disabled?: boolean;
label: string;
onCheckedChange: (checked: boolean) => void;
}) {
return (
{label}
{description ?
{description}
: null}
);
}
function StatusLine({ tone, text }: FeedbackState) {
return (
{text}
);
}
function SupportHint({
capability,
label,
}: {
capability?: boolean;
label: string;
}) {
if (capability !== false) return null;
return {label}
;
}
function OptionSelect({
disabled,
onValueChange,
options,
placeholder,
value,
}: {
disabled?: boolean;
onValueChange: (value: string) => void;
options: SelectOption[];
placeholder: string;
value?: string;
}) {
return (
);
}
export function GenerationSettings() {
const {
availableControlNets,
availableModels,
settings,
settingsHistory,
setSettings,
status,
} = useStore(useShallow((state) => ({
availableControlNets: state.availableControlNets,
availableModels: state.availableModels,
settings: state.settings,
settingsHistory: state.settingsHistory,
setSettings: state.setSettings,
status: state.status,
})));
const { importSettingsFromFiles, restoreLastSeed, saveSettingsSnapshot, updateAutotuneSettings } =
useGenerationActions();
const [historyFeedback, setHistoryFeedback] = useState(null);
const [actionFeedback, setActionFeedback] = useState(null);
const [performanceFeedback, setPerformanceFeedback] = useState(null);
const modelOptions = useMemo(
() => availableModels.map((model) => ({ value: model.path, label: model.name })),
[availableModels],
);
const controlNetOptions = useMemo(
() => availableControlNets.map((model) => ({ value: model, label: model })),
[availableControlNets],
);
const currentModel = availableModels.find((model) => model.path === settings.model_path);
const capabilities = currentModel?.capabilities;
const importDropzone = useDropzone({
accept: { 'image/*': [] },
maxFiles: 1,
multiple: false,
onDrop: (acceptedFiles) => {
void (async () => {
const result = await importSettingsFromFiles(acceptedFiles);
setHistoryFeedback({
tone: result.ok ? (result.warning ? 'warning' : 'success') : 'error',
text: result.warning ? `${result.message} ${result.warning}` : result.message,
});
})();
},
});
const updateNumber =
(key: keyof GenerationSettingsShape, fallback = 0) =>
(event: ChangeEvent) => {
const raw = event.currentTarget.value;
const nextValue = raw === '' ? fallback : Number(raw);
if (!Number.isNaN(nextValue)) {
setSettings({ [key]: nextValue } as Partial);
}
};
const restoreSnapshot = (snapshot: GenerationSettingsShape) => {
setSettings(snapshot);
setHistoryFeedback({
tone: 'success',
text: 'Restored a saved local snapshot.',
});
};
const handleStableFastChange = (checked: boolean) => {
if (checked && settings.torch_compile) {
void (async () => {
const result = await updateAutotuneSettings({
stable_fast: true,
torch_compile: false,
vae_autotune: settings.vae_autotune,
});
setPerformanceFeedback(result.ok ? null : { tone: 'error', text: result.message });
})();
return;
}
setSettings({ stable_fast: checked });
setPerformanceFeedback(null);
};
const handleModelAutotuneChange = (checked: boolean) => {
void (async () => {
const result = await updateAutotuneSettings({
stable_fast: checked ? false : settings.stable_fast,
torch_compile: checked,
vae_autotune: settings.vae_autotune,
});
setPerformanceFeedback(result.ok ? null : { tone: 'error', text: result.message });
})();
};
const handleVaeAutotuneChange = (checked: boolean) => {
void (async () => {
const result = await updateAutotuneSettings({
torch_compile: settings.torch_compile,
vae_autotune: checked,
});
setPerformanceFeedback(result.ok ? null : { tone: 'error', text: result.message });
})();
};
const capabilityTokens = [
currentModel?.type,
capabilities?.supports_img2img ? 'Img2Img' : null,
capabilities?.supports_controlnet ? 'ControlNet' : null,
capabilities?.supports_hires_fix ? 'Hires Fix' : null,
].filter(Boolean) as string[];
return (
Technical controls live here. The main prompt stays in the composer.
{currentModel ? currentModel.name : 'No model selected'}
{status === 'error' ?
: null}
{capabilityTokens.length > 0 ? (
capabilityTokens.map((token) => (
{token}
))
) : (
Waiting for model metadata
)}
Output
Sampling
setSettings({ sampler: value })}
options={samplerOptions.map((sampler) => ({ value: sampler, label: sampler }))}
placeholder="Sampler"
value={settings.sampler}
/>
setSettings({ scheduler: value })}
options={schedulerOptions.map((scheduler) => ({ value: scheduler, label: scheduler }))}
placeholder="Scheduler"
value={settings.scheduler}
/>
setSettings({ preview_fidelity: value as NonNullable })
}
options={[
{ value: 'low', label: 'Low · faster' },
{ value: 'balanced', label: 'Balanced · default' },
{ value: 'high', label: 'High · slower' },
]}
placeholder="Preview fidelity"
value={settings.preview_fidelity || 'balanced'}
/>
setSettings({ reuse_seed: checked })}
/>
{actionFeedback ?
: null}
Enhancements
setSettings({ hiresfix: checked })}
/>
setSettings({ adetailer: checked })}
/>
setSettings({ enhance_prompt: checked })}
/>
setSettings({ enable_preview: checked })}
/>
Refiner
setSettings({ refiner_model_path: value === '__none' ? '' : value })}
options={[{ value: '__none', label: 'None' }, ...modelOptions]}
placeholder="None"
value={settings.refiner_model_path || '__none'}
/>
Image to image
setSettings({ img2img_mode: checked })}
/>
{settings.img2img_mode ? (
setSettings({ img2img_image: base64 ?? undefined })}
/>
) : null}
ControlNet
Performance and optimizations
setSettings({ weight_quantization: value === 'none' ? null : (value as 'fp8' | 'nvfp4') })
}
options={[
{ value: 'none', label: 'None · FP16/BF16' },
{ value: 'fp8', label: 'FP8 · 8-bit' },
{ value: 'nvfp4', label: 'NVFP4 · 4-bit' },
]}
placeholder="Weight quantization"
value={settings.weight_quantization || 'none'}
/>
setSettings({ keep_models_loaded: checked })}
/>
setSettings({ deepcache_enabled: checked })}
/>
setSettings({ tome_enabled: checked })}
/>
{performanceFeedback ? : null}
Multiscale generation
setSettings({ enable_multiscale: checked })}
/>
{settings.enable_multiscale ? (
<>
setSettings({ multiscale_preset: value })}
options={multiscalePresets.map((preset) => ({ value: preset, label: preset }))}
placeholder="Preset"
value={settings.multiscale_preset}
/>
>
) : null}
History and import
setSettings({ persist_prompt_history: checked })}
/>
{settingsHistory.length > 0 ? (
Local snapshots
{settingsHistory.slice(0, 5).map((snapshot) => (
))}
) : null}
{historyFeedback ? : null}
);
}