import type { ImageDocument } from "@core/document"; import { createDocumentReadIndex, resolveIndexedLayerBounds } from "@editor/document-indexes"; import type { SelectionState } from "@editor/state"; import type { GenerateSettings } from "@editor/tools"; export type GenerationPrecondition = | { ready: true } | { ready: false; message: string; repair?: "add-mask" | "set-outpaint-padding" }; export function checkGenerationPreconditions( document: ImageDocument, selection: SelectionState, settings: GenerateSettings, ): GenerationPrecondition { if (!settings.prompt.trim()) return missing("Enter a prompt to generate an image."); const artboard = selection.artboardId ? document.artboards.find((candidate) => candidate.id === selection.artboardId) : document.artboards[0]; if (!artboard) return missing("Create an artboard before generating an image."); if (settings.mode === "text-to-image") return { ready: true }; if (selection.layerIds.length !== 1 || !selection.layerIds[0]) { return missing(modeSelectionMessage(settings.mode)); } const index = createDocumentReadIndex(document); const layerInfo = index.layerInfoById.get(selection.layerIds[0]); if (!layerInfo || layerInfo.artboardId !== artboard.id || (layerInfo.layer.type !== "image" && layerInfo.layer.type !== "raster")) { return missing(modeSelectionMessage(settings.mode)); } const asset = index.assetById.get(layerInfo.layer.assetId); if (!asset) return missing("The selected layer is missing its source image."); if (asset.intrinsicSize.w <= 0 || asset.intrinsicSize.h <= 0) return missing("The selected image has an invalid size."); if (settings.mode === "outpaint") { const padding = settings.outpaint; if (padding.left + padding.top + padding.right + padding.bottom <= 0) { return missing("Add outpaint padding on at least one side.", "set-outpaint-padding"); } } if (settings.mode !== "inpaint") return { ready: true }; const region = document.inpaintRegions.find((candidate) => candidate.targetLayerId === layerInfo.layer.id && candidate.enabled); if (!region) return missing("Paint an AI edit region over the area you want to replace.", "add-mask"); const maskAsset = index.assetById.get(region.maskAssetId); if (!maskAsset) return missing("The AI edit region is missing its mask data."); if (Math.round(asset.intrinsicSize.w) !== Math.round(maskAsset.intrinsicSize.w) || Math.round(asset.intrinsicSize.h) !== Math.round(maskAsset.intrinsicSize.h)) { return missing("The selected layer and mask image sizes must match."); } if (!resolveIndexedLayerBounds(index, layerInfo.layer)) return missing("The selected layer has invalid geometry."); return { ready: true }; } function modeSelectionMessage(mode: GenerateSettings["mode"]): string { if (mode === "image-to-image") return "Select exactly one image or raster layer for image-to-image generation."; if (mode === "inpaint") return "Select exactly one masked image or raster layer to inpaint."; return "Select exactly one image or raster layer to outpaint."; } function missing(message: string, repair?: Extract["repair"]): GenerationPrecondition { return { ready: false, message, ...(repair ? { repair } : {}) }; }