mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-02-14 03:25:29 -05:00
Add reference image enable/disable functionality
Co-authored-by: kent <kent@invoke.ai>
This commit is contained in:
committed by
Kent Keirsey
parent
11fc7af1c8
commit
adbcc191d9
@@ -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 (
|
||||
<Flex justifyContent="space-between" alignItems="center" w="full" ps={2}>
|
||||
<Text fontWeight="semibold" sx={textSx} data-is-error={!entity.config.image}>
|
||||
Reference Image #{refImageNumber}
|
||||
</Text>
|
||||
<IconButton
|
||||
tooltip="Delete Reference Image"
|
||||
size="xs"
|
||||
variant="link"
|
||||
alignSelf="stretch"
|
||||
aria-label="Delete ref image"
|
||||
onClick={deleteRefImage}
|
||||
icon={<PiTrashBold />}
|
||||
colorScheme="error"
|
||||
/>
|
||||
<Flex>
|
||||
<IconButton
|
||||
tooltip={entity.isEnabled ? 'Disable Reference Image' : 'Enable Reference Image'}
|
||||
size="xs"
|
||||
variant="link"
|
||||
alignSelf="stretch"
|
||||
aria-label={entity.isEnabled ? 'Disable ref image' : 'Enable ref image'}
|
||||
onClick={toggleIsEnabled}
|
||||
icon={entity.isEnabled ? <PiEyeBold /> : <PiEyeSlashBold />}
|
||||
colorScheme={entity.isEnabled ? 'base' : 'warning'}
|
||||
/>
|
||||
<IconButton
|
||||
tooltip="Delete Reference Image"
|
||||
size="xs"
|
||||
variant="link"
|
||||
alignSelf="stretch"
|
||||
aria-label="Delete ref image"
|
||||
onClick={deleteRefImage}
|
||||
icon={<PiTrashBold />}
|
||||
colorScheme="error"
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -132,6 +132,7 @@ export const initialControlLoRA: ControlLoRAConfig = {
|
||||
export const getReferenceImageState = (id: string, overrides?: PartialDeep<RefImageState>): RefImageState => {
|
||||
const entityState: RefImageState = {
|
||||
id,
|
||||
isEnabled: true,
|
||||
config: deepClone(initialIPAdapter),
|
||||
};
|
||||
merge(entityState, overrides);
|
||||
|
||||
@@ -632,6 +632,7 @@ const parseIPAdapterToIPAdapterLayer: MetadataParseFunc<RefImageState> = async (
|
||||
|
||||
const layer: RefImageState = {
|
||||
id: getPrefixedId('ip_adapter'),
|
||||
isEnabled: true,
|
||||
config: {
|
||||
type: 'ip_adapter',
|
||||
model: zModelIdentifierField.parse(ipAdapterModel),
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ export const buildChatGPT4oGraph = async (arg: GraphBuilderArg): Promise<GraphBu
|
||||
assert(isChatGPT4oAspectRatioID(bbox.aspectRatio.id), 'ChatGPT 4o does not support this aspect ratio');
|
||||
|
||||
const validRefImages = refImages.entities
|
||||
.filter((entity) => 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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') });
|
||||
|
||||
Reference in New Issue
Block a user