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"; import type { ImageDocument } from "@core/document"; import type { SelectionState } from "@editor/state"; import { InpaintRegionPanel } from "../inpaint/InpaintRegionPanel"; 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 inpaintMaskedContentOptions = [ { value: "neutral", label: "Neutral fill" }, { value: "original", label: "Original gray" }, { value: "originalColor", label: "Original color" }, { value: "edges", label: "Edge map" }, ] satisfies readonly BottomControlSelectOption[]; const inpaintProfileOptions = [ { value: "remove", label: "Remove object" }, { value: "replace", label: "Replace object" }, { value: "repair", label: "Repair detail" }, { value: "material", label: "Change material" }, { value: "reshape", label: "Change shape" }, { value: "custom", label: "Custom" }, ] satisfies readonly BottomControlSelectOption[]; const structureControlOptions = [ { value: "none", label: "None" }, { value: "canny", label: "Canny edges" }, { value: "depth", label: "Depth" }, { value: "pose", label: "Pose" }, ] satisfies readonly BottomControlSelectOption[]; export type GenerateControlsProps = { settings: GenerateSettings; document: ImageDocument; selection: SelectionState; resources: GenerationResourcesState; dispatch: AppStore["dispatch"]; }; export function GenerateControls({ settings, document, selection, 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); const availableStructureControls = structureControlOptions.filter((option) => option.value === "none" || option.value === settings.inpaint.structureControl || comfyOptions?.structureControls?.includes(option.value)); 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) => ( ))}