Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ui): allow invoke when inpaint/raster layers are empty #7410

Merged
merged 3 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 16 additions & 18 deletions invokeai/frontend/web/public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1039,24 +1039,7 @@
"canvasIsSelectingObject": "Canvas is busy (selecting object)",
"noPrompts": "No prompts generated",
"noNodesInGraph": "No nodes in graph",
"systemDisconnected": "System disconnected",
"layer": {
"unsupportedModel": "layer not supported for selected base model",
"controlAdapterNoModelSelected": "no Control Adapter model selected",
"controlAdapterIncompatibleBaseModel": "incompatible Control Adapter base model",
"t2iAdapterIncompatibleBboxWidth": "$t(parameters.invoke.layer.t2iAdapterRequiresDimensionsToBeMultipleOf) {{multiple}}, bbox width is {{width}}",
"t2iAdapterIncompatibleBboxHeight": "$t(parameters.invoke.layer.t2iAdapterRequiresDimensionsToBeMultipleOf) {{multiple}}, bbox height is {{height}}",
"t2iAdapterIncompatibleScaledBboxWidth": "$t(parameters.invoke.layer.t2iAdapterRequiresDimensionsToBeMultipleOf) {{multiple}}, scaled bbox width is {{width}}",
"t2iAdapterIncompatibleScaledBboxHeight": "$t(parameters.invoke.layer.t2iAdapterRequiresDimensionsToBeMultipleOf) {{multiple}}, scaled bbox height is {{height}}",
"ipAdapterNoModelSelected": "no IP adapter selected",
"ipAdapterIncompatibleBaseModel": "incompatible IP Adapter base model",
"ipAdapterNoImageSelected": "no IP Adapter image selected",
"rgNoPromptsOrIPAdapters": "no text prompts or IP Adapters",
"rgNegativePromptNotSupported": "negative prompt not supported for selected base model",
"rgReferenceImagesNotSupported": "regional reference images not supported for selected base model",
"rgAutoNegativeNotSupported": "auto-negative not supported for selected base model",
"emptyLayer": "empty layer"
}
"systemDisconnected": "System disconnected"
},
"maskBlur": "Mask Blur",
"negativePromptPlaceholder": "Negative Prompt",
Expand Down Expand Up @@ -1805,6 +1788,21 @@
"replaceCurrent": "Replace Current",
"controlLayerEmptyState": "<UploadButton>Upload an image</UploadButton>, drag an image from the <GalleryButton>gallery</GalleryButton> onto this layer, or draw on the canvas to get started.",
"referenceImageEmptyState": "<UploadButton>Upload an image</UploadButton> or drag an image from the <GalleryButton>gallery</GalleryButton> onto this layer to get started.",
"warnings": {
"problemsFound": "Problems found",
"unsupportedModel": "layer not supported for selected base model",
"controlAdapterNoModelSelected": "no Control Layer model selected",
"controlAdapterIncompatibleBaseModel": "incompatible Control Layer base model",
"controlAdapterNoControl": "no control selected/drawn",
"ipAdapterNoModelSelected": "no Reference Image model selected",
"ipAdapterIncompatibleBaseModel": "incompatible Reference Image base model",
"ipAdapterNoImageSelected": "no Reference Image image selected",
"rgNoPromptsOrIPAdapters": "no text prompts or Reference Images",
"rgNegativePromptNotSupported": "Negative Prompt not supported for selected base model",
"rgReferenceImagesNotSupported": "regional Reference Images not supported for selected base model",
"rgAutoNegativeNotSupported": "Auto-Negative not supported for selected base model",
"rgNoRegion": "no region drawn"
},
"controlMode": {
"controlMode": "Control Mode",
"balanced": "Balanced (recommended)",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ const TooltipContent = memo((props: { warnings: string[] }) => {
const { t } = useTranslation();
return (
<Flex flexDir="column">
<Text>{t('common.warnings')}:</Text>
<Text>{t('controlLayers.warnings.problemsFound')}:</Text>
<UnorderedList>
{props.warnings.map((warning, index) => (
<ListItem key={index}>{warning}</ListItem>
Expand Down
164 changes: 92 additions & 72 deletions invokeai/frontend/web/src/features/controlLayers/store/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,52 +7,69 @@ import type {
} from 'features/controlLayers/store/types';
import type { ParameterModel } from 'features/parameters/types/parameterSchemas';

const WARNINGS = {
UNSUPPORTED_MODEL: 'controlLayers.warnings.unsupportedModel',
RG_NO_PROMPTS_OR_IP_ADAPTERS: 'controlLayers.warnings.rgNoPromptsOrIPAdapters',
RG_NEGATIVE_PROMPT_NOT_SUPPORTED: 'controlLayers.warnings.rgNegativePromptNotSupported',
RG_REFERENCE_IMAGES_NOT_SUPPORTED: 'controlLayers.warnings.rgReferenceImagesNotSupported',
RG_AUTO_NEGATIVE_NOT_SUPPORTED: 'controlLayers.warnings.rgAutoNegativeNotSupported',
RG_NO_REGION: 'controlLayers.warnings.rgNoRegion',
IP_ADAPTER_NO_MODEL_SELECTED: 'controlLayers.warnings.ipAdapterNoModelSelected',
IP_ADAPTER_INCOMPATIBLE_BASE_MODEL: 'controlLayers.warnings.ipAdapterIncompatibleBaseModel',
IP_ADAPTER_NO_IMAGE_SELECTED: 'controlLayers.warnings.ipAdapterNoImageSelected',
CONTROL_ADAPTER_NO_MODEL_SELECTED: 'controlLayers.warnings.controlAdapterNoModelSelected',
CONTROL_ADAPTER_INCOMPATIBLE_BASE_MODEL: 'controlLayers.warnings.controlAdapterIncompatibleBaseModel',
CONTROL_ADAPTER_NO_CONTROL: 'controlLayers.warnings.controlAdapterNoControl',
} as const;

type WarningTKey = (typeof WARNINGS)[keyof typeof WARNINGS];

export const getRegionalGuidanceWarnings = (
entity: CanvasRegionalGuidanceState,
model: ParameterModel | null
): string[] => {
const warnings: string[] = [];
): WarningTKey[] => {
const warnings: WarningTKey[] = [];

if (entity.objects.length === 0) {
// Layer is in empty state - skip other checks
warnings.push('parameters.invoke.layer.emptyLayer');
} else {
if (entity.positivePrompt === null && entity.negativePrompt === null && entity.referenceImages.length === 0) {
// Must have at least 1 prompt or IP Adapter
warnings.push('parameters.invoke.layer.rgNoPromptsOrIPAdapters');
}
// Layer is in empty state
warnings.push(WARNINGS.RG_NO_REGION);
}

if (model) {
if (model.base === 'sd-3' || model.base === 'sd-2') {
// Unsupported model architecture
warnings.push('parameters.invoke.layer.unsupportedModel');
} else if (model.base === 'flux') {
// Some features are not supported for flux models
if (entity.negativePrompt !== null) {
warnings.push('parameters.invoke.layer.rgNegativePromptNotSupported');
}
if (entity.referenceImages.length > 0) {
warnings.push('parameters.invoke.layer.rgReferenceImagesNotSupported');
if (entity.positivePrompt === null && entity.negativePrompt === null && entity.referenceImages.length === 0) {
// Must have at least 1 prompt or IP Adapter
warnings.push(WARNINGS.RG_NO_PROMPTS_OR_IP_ADAPTERS);
}

if (model) {
if (model.base === 'sd-3' || model.base === 'sd-2') {
// Unsupported model architecture
warnings.push(WARNINGS.UNSUPPORTED_MODEL);
} else if (model.base === 'flux') {
// Some features are not supported for flux models
if (entity.negativePrompt !== null) {
warnings.push(WARNINGS.RG_NEGATIVE_PROMPT_NOT_SUPPORTED);
}
if (entity.referenceImages.length > 0) {
warnings.push(WARNINGS.RG_REFERENCE_IMAGES_NOT_SUPPORTED);
}
if (entity.autoNegative) {
warnings.push(WARNINGS.RG_AUTO_NEGATIVE_NOT_SUPPORTED);
}
} else {
entity.referenceImages.forEach(({ ipAdapter }) => {
if (!ipAdapter.model) {
// No model selected
warnings.push(WARNINGS.IP_ADAPTER_NO_MODEL_SELECTED);
} else if (ipAdapter.model.base !== model.base) {
// Supported model architecture but doesn't match
warnings.push(WARNINGS.IP_ADAPTER_INCOMPATIBLE_BASE_MODEL);
}
if (entity.autoNegative) {
warnings.push('parameters.invoke.layer.rgAutoNegativeNotSupported');

if (!ipAdapter.image) {
// No image selected
warnings.push(WARNINGS.IP_ADAPTER_NO_IMAGE_SELECTED);
}
} else {
entity.referenceImages.forEach(({ ipAdapter }) => {
if (!ipAdapter.model) {
// No model selected
warnings.push('parameters.invoke.layer.ipAdapterNoModelSelected');
} else if (ipAdapter.model.base !== model.base) {
// Supported model architecture but doesn't match
warnings.push('parameters.invoke.layer.ipAdapterIncompatibleBaseModel');
}

if (!ipAdapter.image) {
// No image selected
warnings.push('parameters.invoke.layer.ipAdapterNoImageSelected');
}
});
}
});
}
}

Expand All @@ -62,72 +79,75 @@ export const getRegionalGuidanceWarnings = (
export const getGlobalReferenceImageWarnings = (
entity: CanvasReferenceImageState,
model: ParameterModel | null
): string[] => {
const warnings: string[] = [];
): WarningTKey[] => {
const warnings: WarningTKey[] = [];

if (!entity.ipAdapter.model) {
// No model selected
warnings.push('parameters.invoke.layer.ipAdapterNoModelSelected');
warnings.push(WARNINGS.IP_ADAPTER_NO_MODEL_SELECTED);
} else if (model) {
if (model.base === 'sd-3' || model.base === 'sd-2') {
// Unsupported model architecture
warnings.push('parameters.invoke.layer.unsupportedModel');
warnings.push(WARNINGS.UNSUPPORTED_MODEL);
} else if (entity.ipAdapter.model.base !== model.base) {
// Supported model architecture but doesn't match
warnings.push('parameters.invoke.layer.ipAdapterIncompatibleBaseModel');
warnings.push(WARNINGS.IP_ADAPTER_INCOMPATIBLE_BASE_MODEL);
}
}

if (!entity.ipAdapter.image) {
// No image selected
warnings.push('parameters.invoke.layer.ipAdapterNoImageSelected');
warnings.push(WARNINGS.IP_ADAPTER_NO_IMAGE_SELECTED);
}

return warnings;
};

export const getControlLayerWarnings = (entity: CanvasControlLayerState, model: ParameterModel | null): string[] => {
const warnings: string[] = [];
export const getControlLayerWarnings = (
entity: CanvasControlLayerState,
model: ParameterModel | null
): WarningTKey[] => {
const warnings: WarningTKey[] = [];

if (entity.objects.length === 0) {
// Layer is in empty state - skip other checks
warnings.push('parameters.invoke.layer.emptyLayer');
} else {
if (!entity.controlAdapter.model) {
// No model selected
warnings.push('parameters.invoke.layer.controlAdapterNoModelSelected');
} else if (model) {
if (model.base === 'sd-3' || model.base === 'sd-2') {
// Unsupported model architecture
warnings.push('parameters.invoke.layer.unsupportedModel');
} else if (entity.controlAdapter.model.base !== model.base) {
// Supported model architecture but doesn't match
warnings.push('parameters.invoke.layer.controlAdapterIncompatibleBaseModel');
}
// Layer is in empty state
warnings.push(WARNINGS.CONTROL_ADAPTER_NO_CONTROL);
}

if (!entity.controlAdapter.model) {
// No model selected
warnings.push(WARNINGS.CONTROL_ADAPTER_NO_MODEL_SELECTED);
} else if (model) {
if (model.base === 'sd-3' || model.base === 'sd-2') {
// Unsupported model architecture
warnings.push(WARNINGS.UNSUPPORTED_MODEL);
} else if (entity.controlAdapter.model.base !== model.base) {
// Supported model architecture but doesn't match
warnings.push(WARNINGS.CONTROL_ADAPTER_INCOMPATIBLE_BASE_MODEL);
}
}

return warnings;
};

export const getRasterLayerWarnings = (entity: CanvasRasterLayerState, _model: ParameterModel | null): string[] => {
const warnings: string[] = [];
export const getRasterLayerWarnings = (
_entity: CanvasRasterLayerState,
_model: ParameterModel | null
): WarningTKey[] => {
const warnings: WarningTKey[] = [];

if (entity.objects.length === 0) {
// Layer is in empty state - skip other checks
warnings.push('parameters.invoke.layer.emptyLayer');
}
// There are no warnings at the moment for raster layers.

return warnings;
};

export const getInpaintMaskWarnings = (entity: CanvasInpaintMaskState, _model: ParameterModel | null): string[] => {
const warnings: string[] = [];
export const getInpaintMaskWarnings = (
_entity: CanvasInpaintMaskState,
_model: ParameterModel | null
): WarningTKey[] => {
const warnings: WarningTKey[] = [];

if (entity.objects.length === 0) {
// Layer is in empty state - skip other checks
warnings.push('parameters.invoke.layer.emptyLayer');
}
// There are no warnings at the moment for inpaint masks.

return warnings;
};