import { useEffect, useRef, useState, type RefObject } from "react"; import { CaretDown, CaretUp } from "@phosphor-icons/react"; import { commandIds } from "@commands/ids"; import type { AppStore } from "@editor/store"; import type { GenerationResourcesState } from "@editor/state"; import type { GenerateArchitecture, GenerateIntent, GenerateMode, GenerateSettings } from "@editor/tools"; import { resolveGenerationModeOptions, resolveGenerationModelOptions, resolveGenerationStringOptions, resolveGenerationSupportOptions } from "@operations/generation/options"; import { BottomControlSelectMenu, type BottomControlSelectOption } from "./SelectMenu"; import { BottomControlSlider } from "./Slider"; const architectures = [ { value: "sdxl", label: "SDXL" }, { value: "z-image", label: "Z-Image" }, { value: "z-image-turbo", label: "Z-Image Turbo" }, { value: "anima", label: "Anima" }, ] satisfies readonly BottomControlSelectOption[]; const modes = [ { value: "text-to-image", label: "Text → image" }, { value: "image-to-image", label: "Image → image" }, { value: "inpaint", label: "Inpaint" }, { value: "outpaint", label: "Outpaint" }, ] satisfies readonly BottomControlSelectOption[]; const sizePresets = [ { label: "1:1", w: 1024, h: 1024 }, { label: "4:3", w: 1152, h: 896 }, { label: "3:4", w: 896, h: 1152 }, { label: "16:9", w: 1344, h: 768 }, { label: "9:16", w: 768, h: 1344 }, ] as const; const inpaintPolarityOptions = [ { value: "hidden", label: "Hidden / erased" }, { value: "revealed", label: "Revealed / painted" }, ] satisfies readonly BottomControlSelectOption[]; const inpaintMaskedContentOptions = [ { value: "neutral", label: "Neutral fill" }, { value: "original", label: "Original gray" }, { value: "originalColor", label: "Original color" }, { value: "edges", label: "Edge map" }, ] satisfies readonly BottomControlSelectOption[]; export type GenerateControlsProps = { settings: GenerateSettings; resources: GenerationResourcesState; dispatch: AppStore["dispatch"]; }; export function GenerateControls({ settings, resources, dispatch }: GenerateControlsProps) { const comfyOptions = resources.options; const [advancedOpen, setAdvancedOpen] = useState(false); const [outpaintOpen, setOutpaintOpen] = useState(false); const [inpaintOpen, setInpaintOpen] = useState(false); const [sizeOpen, setSizeOpen] = useState(false); const sizeRef = useRef(null); const modelOptions = resolveGenerationModelOptions(settings, comfyOptions); const supportOptions = resolveGenerationSupportOptions(settings, comfyOptions); const samplerOptions = resolveGenerationStringOptions(comfyOptions?.samplers, settings.sampler); const schedulerOptions = resolveGenerationStringOptions(comfyOptions?.schedulers, settings.scheduler); const modeOptions = resolveGenerationModeOptions(settings, comfyOptions, modes); useEffect(() => { if (!sizeOpen) return; const close = (event: PointerEvent) => { if (!sizeRef.current?.contains(event.target as Node)) setSizeOpen(false); }; window.addEventListener("pointerdown", close); return () => window.removeEventListener("pointerdown", close); }, [sizeOpen]); return (
{resources.error ?

{resources.error}

: null}

Edit with AI

Choose an outcome. Image Studio configures the matching generation workflow.

{intentOptions.map((intent) => ( ))}