diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageHeader.tsx b/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageHeader.tsx
index 972dc26b89..e366d0cad9 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageHeader.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageHeader.tsx
@@ -4,9 +4,13 @@ import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useRefImageEntity } from 'features/controlLayers/components/RefImage/useRefImageEntity';
import { useRefImageIdContext } from 'features/controlLayers/contexts/RefImageIdContext';
-import { refImageDeleted, selectRefImageEntityIds } from 'features/controlLayers/store/refImagesSlice';
+import {
+ refImageDeleted,
+ refImageIsEnabledToggled,
+ selectRefImageEntityIds,
+} from 'features/controlLayers/store/refImagesSlice';
import { memo, useCallback, useMemo } from 'react';
-import { PiTrashBold } from 'react-icons/pi';
+import { PiEyeBold, PiEyeSlashBold, PiTrashBold } from 'react-icons/pi';
const textSx: SystemStyleObject = {
color: 'base.300',
@@ -28,21 +32,37 @@ export const RefImageHeader = memo(() => {
dispatch(refImageDeleted({ id }));
}, [dispatch, id]);
+ const toggleIsEnabled = useCallback(() => {
+ dispatch(refImageIsEnabledToggled({ id }));
+ }, [dispatch, id]);
+
return (
Reference Image #{refImageNumber}
- }
- colorScheme="error"
- />
+
+ : }
+ colorScheme={entity.isEnabled ? 'base' : 'warning'}
+ />
+ }
+ colorScheme="error"
+ />
+
);
});
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImagePreview.tsx b/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImagePreview.tsx
index e5a0e5c02a..100e7496ff 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImagePreview.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImagePreview.tsx
@@ -19,6 +19,9 @@ const baseSx: SystemStyleObject = {
'&[data-is-open="true"]': {
borderColor: 'invokeBlue.300',
},
+ '&[data-is-disabled="true"]': {
+ opacity: 0.4,
+ },
};
const weightDisplaySx: SystemStyleObject = {
@@ -36,6 +39,9 @@ const getImageSxWithWeight = (weight: number): SystemStyleObject => {
return {
...baseSx,
+ '&[data-is-disabled="true"]': {
+ opacity: 0.4,
+ },
_after: {
content: '""',
position: 'absolute',
@@ -97,6 +103,7 @@ export const RefImagePreview = memo(() => {
flexShrink={0}
data-is-open={selectedEntityId === id && isPanelOpen}
data-is-error={true}
+ data-is-disabled={!entity.isEnabled}
sx={sx}
/>
);
@@ -114,6 +121,7 @@ export const RefImagePreview = memo(() => {
sx={sx}
data-is-open={selectedEntityId === id && isPanelOpen}
data-is-error={!entity.config.model}
+ data-is-disabled={!entity.isEnabled}
role="button"
onClick={onClick}
cursor="pointer"
diff --git a/invokeai/frontend/web/src/features/controlLayers/store/refImagesSlice.ts b/invokeai/frontend/web/src/features/controlLayers/store/refImagesSlice.ts
index b9725b3f17..e709372118 100644
--- a/invokeai/frontend/web/src/features/controlLayers/store/refImagesSlice.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/store/refImagesSlice.ts
@@ -222,6 +222,14 @@ export const refImagesSlice = createSlice({
}
state.selectedEntityId = id;
},
+ refImageIsEnabledToggled: (state, action: PayloadActionWithId) => {
+ const { id } = action.payload;
+ const entity = selectRefImageEntity(state, id);
+ if (!entity) {
+ return;
+ }
+ entity.isEnabled = !entity.isEnabled;
+ },
refImagesReset: () => getInitialRefImagesState(),
},
extraReducers(builder) {
@@ -243,6 +251,7 @@ export const {
refImageIPAdapterWeightChanged,
refImageIPAdapterBeginEndStepPctChanged,
refImageFLUXReduxImageInfluenceChanged,
+ refImageIsEnabledToggled,
} = refImagesSlice.actions;
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
diff --git a/invokeai/frontend/web/src/features/controlLayers/store/types.ts b/invokeai/frontend/web/src/features/controlLayers/store/types.ts
index abb22cb4ea..88d24d703d 100644
--- a/invokeai/frontend/web/src/features/controlLayers/store/types.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/store/types.ts
@@ -302,6 +302,7 @@ const zCanvasEntityBase = z.object({
const zRefImageState = z.object({
id: zId,
+ isEnabled: z.boolean().default(true),
// This should be named `referenceImage` but we need to keep it as `ipAdapter` for backwards compatibility
config: z.discriminatedUnion('type', [
zIPAdapterConfig,
diff --git a/invokeai/frontend/web/src/features/controlLayers/store/util.ts b/invokeai/frontend/web/src/features/controlLayers/store/util.ts
index aad3669df1..2d40cf1779 100644
--- a/invokeai/frontend/web/src/features/controlLayers/store/util.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/store/util.ts
@@ -132,6 +132,7 @@ export const initialControlLoRA: ControlLoRAConfig = {
export const getReferenceImageState = (id: string, overrides?: PartialDeep): RefImageState => {
const entityState: RefImageState = {
id,
+ isEnabled: true,
config: deepClone(initialIPAdapter),
};
merge(entityState, overrides);
diff --git a/invokeai/frontend/web/src/features/metadata/util/parsers.ts b/invokeai/frontend/web/src/features/metadata/util/parsers.ts
index de961ddee4..dbeeb0a2ba 100644
--- a/invokeai/frontend/web/src/features/metadata/util/parsers.ts
+++ b/invokeai/frontend/web/src/features/metadata/util/parsers.ts
@@ -632,6 +632,7 @@ const parseIPAdapterToIPAdapterLayer: MetadataParseFunc = async (
const layer: RefImageState = {
id: getPrefixedId('ip_adapter'),
+ isEnabled: true,
config: {
type: 'ip_adapter',
model: zModelIdentifierField.parse(ipAdapterModel),
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addFLUXRedux.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addFLUXRedux.ts
index bac14a218d..7ab0cb2d38 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addFLUXRedux.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addFLUXRedux.ts
@@ -18,6 +18,7 @@ type AddFLUXReduxArg = {
export const addFLUXReduxes = ({ entities, g, collector, model }: AddFLUXReduxArg): AddFLUXReduxResult => {
const validFLUXReduxes = entities
+ .filter((entity) => entity.isEnabled)
.filter((entity) => isFLUXReduxConfig(entity.config))
.filter((entity) => getGlobalReferenceImageWarnings(entity, model).length === 0);
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addIPAdapters.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addIPAdapters.ts
index ab5fb74465..0302d73397 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addIPAdapters.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addIPAdapters.ts
@@ -17,6 +17,7 @@ type AddIPAdaptersArg = {
export const addIPAdapters = ({ entities, g, collector, model }: AddIPAdaptersArg): AddIPAdaptersResult => {
const validIPAdapters = entities
+ .filter((entity) => entity.isEnabled)
.filter((entity) => isIPAdapterConfig(entity.config))
.filter((entity) => getGlobalReferenceImageWarnings(entity, model).length === 0);
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildChatGPT4oGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildChatGPT4oGraph.ts
index eb64c4b19b..4ff09fb2e2 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildChatGPT4oGraph.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildChatGPT4oGraph.ts
@@ -39,6 +39,7 @@ export const buildChatGPT4oGraph = async (arg: GraphBuilderArg): Promise entity.isEnabled)
.filter((entity) => isChatGPT4oReferenceImageConfig(entity.config))
.filter((entity) => getGlobalReferenceImageWarnings(entity, model).length === 0)
.toReversed(); // sends them in order they are displayed in the list
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildFluxKontextGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildFluxKontextGraph.ts
index e8a55a7e8d..60852daad1 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildFluxKontextGraph.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildFluxKontextGraph.ts
@@ -37,6 +37,7 @@ export const buildFluxKontextGraph = (arg: GraphBuilderArg): GraphBuilderReturn
assert(model.base === 'flux-kontext', 'Model is not a Flux Kontext model');
const validRefImages = refImages.entities
+ .filter((entity) => entity.isEnabled)
.filter((entity) => isFluxKontextReferenceImageConfig(entity.config))
.filter((entity) => getGlobalReferenceImageWarnings(entity, model).length === 0)
.toReversed(); // sends them in order they are displayed in the list
diff --git a/invokeai/frontend/web/src/features/queue/store/readiness.ts b/invokeai/frontend/web/src/features/queue/store/readiness.ts
index ae2fb9a204..f8c36fef29 100644
--- a/invokeai/frontend/web/src/features/queue/store/readiness.ts
+++ b/invokeai/frontend/web/src/features/queue/store/readiness.ts
@@ -632,7 +632,7 @@ const getReasonsWhyCannotEnqueueCanvasTab = (arg: {
});
// Flux Kontext only supports 1x Reference Image at a time.
- const referenceImageCount = refImages.entities.length;
+ const referenceImageCount = refImages.entities.filter((entity) => entity.isEnabled).length;
if (model?.base === 'flux-kontext' && referenceImageCount > 1) {
reasons.push({ content: i18n.t('parameters.invoke.fluxKontextMultipleReferenceImages') });