diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index d846f3ca47..881e531701 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -49,6 +49,7 @@ "close": "Close", "communityLabel": "Community", "controlNet": "Controlnet", + "ipAdapter": "IP Adapter", "darkMode": "Dark Mode", "discordLabel": "Discord", "dontAskMeAgain": "Don't ask me again", @@ -191,7 +192,11 @@ "showAdvanced": "Show Advanced", "toggleControlNet": "Toggle this ControlNet", "w": "W", - "weight": "Weight" + "weight": "Weight", + "enableIPAdapter": "Enable IP Adapter", + "ipAdapterModel": "Adapter Model", + "resetIPAdapterImage": "Reset IP Adapter Image", + "ipAdapterImageFallback": "No IP Adapter Image Selected" }, "embedding": { "addEmbedding": "Add Embedding", @@ -1036,6 +1041,7 @@ "serverError": "Server Error", "setCanvasInitialImage": "Set as canvas initial image", "setControlImage": "Set as control image", + "setIPAdapterImage": "Set as IP Adapter Image", "setInitialImage": "Set as initial image", "setNodeField": "Set as node field", "tempFoldersEmptied": "Temp Folder Emptied", diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/boardAndImagesDeleted.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/boardAndImagesDeleted.ts index d4a36d64dc..69b136ca60 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/boardAndImagesDeleted.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/boardAndImagesDeleted.ts @@ -1,5 +1,8 @@ import { resetCanvas } from 'features/canvas/store/canvasSlice'; -import { controlNetReset } from 'features/controlNet/store/controlNetSlice'; +import { + controlNetReset, + ipAdapterStateReset, +} from 'features/controlNet/store/controlNetSlice'; import { getImageUsage } from 'features/deleteImageModal/store/selectors'; import { nodeEditorReset } from 'features/nodes/store/nodesSlice'; import { clearInitialImage } from 'features/parameters/store/generationSlice'; @@ -18,6 +21,7 @@ export const addDeleteBoardAndImagesFulfilledListener = () => { let wasCanvasReset = false; let wasNodeEditorReset = false; let wasControlNetReset = false; + let wasIPAdapterReset = false; const state = getState(); deleted_images.forEach((image_name) => { @@ -42,6 +46,11 @@ export const addDeleteBoardAndImagesFulfilledListener = () => { dispatch(controlNetReset()); wasControlNetReset = true; } + + if (imageUsage.isIPAdapterImage && !wasIPAdapterReset) { + dispatch(ipAdapterStateReset()); + wasIPAdapterReset = true; + } }); }, }); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDeleted.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDeleted.ts index 770c9fc11b..7c51b44aa2 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDeleted.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDeleted.ts @@ -3,6 +3,7 @@ import { resetCanvas } from 'features/canvas/store/canvasSlice'; import { controlNetImageChanged, controlNetProcessedImageChanged, + ipAdapterImageChanged, } from 'features/controlNet/store/controlNetSlice'; import { imageDeletionConfirmed } from 'features/deleteImageModal/store/actions'; import { isModalOpenChanged } from 'features/deleteImageModal/store/slice'; @@ -110,6 +111,14 @@ export const addRequestedSingleImageDeletionListener = () => { } }); + // Remove IP Adapter Set Image if image is deleted. + if ( + getState().controlNet.ipAdapterInfo.adapterImage?.image_name === + imageDTO.image_name + ) { + dispatch(ipAdapterImageChanged(null)); + } + // reset nodes that use the deleted images getState().nodes.nodes.forEach((node) => { if (!isInvocationNode(node)) { @@ -227,6 +236,14 @@ export const addRequestedMultipleImageDeletionListener = () => { } }); + // Remove IP Adapter Set Image if image is deleted. + if ( + getState().controlNet.ipAdapterInfo.adapterImage?.image_name === + imageDTO.image_name + ) { + dispatch(ipAdapterImageChanged(null)); + } + // reset nodes that use the deleted images getState().nodes.nodes.forEach((node) => { if (!isInvocationNode(node)) { diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDropped.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDropped.ts index fc0b44653d..e8b4aa9210 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDropped.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDropped.ts @@ -1,7 +1,11 @@ import { createAction } from '@reduxjs/toolkit'; import { logger } from 'app/logging/logger'; +import { parseify } from 'common/util/serialize'; import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice'; -import { controlNetImageChanged } from 'features/controlNet/store/controlNetSlice'; +import { + controlNetImageChanged, + ipAdapterImageChanged, +} from 'features/controlNet/store/controlNetSlice'; import { TypesafeDraggableData, TypesafeDroppableData, @@ -14,7 +18,6 @@ import { import { initialImageChanged } from 'features/parameters/store/generationSlice'; import { imagesApi } from 'services/api/endpoints/images'; import { startAppListening } from '../'; -import { parseify } from 'common/util/serialize'; export const dndDropped = createAction<{ overData: TypesafeDroppableData; @@ -99,6 +102,18 @@ export const addImageDroppedListener = () => { return; } + /** + * Image dropped on IP Adapter image + */ + if ( + overData.actionType === 'SET_IP_ADAPTER_IMAGE' && + activeData.payloadType === 'IMAGE_DTO' && + activeData.payload.imageDTO + ) { + dispatch(ipAdapterImageChanged(activeData.payload.imageDTO)); + return; + } + /** * Image dropped on Canvas */ diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageToDeleteSelected.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageToDeleteSelected.ts index 88a4e773d5..61b4379d5d 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageToDeleteSelected.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageToDeleteSelected.ts @@ -19,6 +19,7 @@ export const addImageToDeleteSelectedListener = () => { imagesUsage.some((i) => i.isCanvasImage) || imagesUsage.some((i) => i.isInitialImage) || imagesUsage.some((i) => i.isControlNetImage) || + imagesUsage.some((i) => i.isIPAdapterImage) || imagesUsage.some((i) => i.isNodesImage); if (shouldConfirmOnDelete || isImageInUse) { diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts index 2cc406469b..1c7caaeb2f 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts @@ -1,15 +1,18 @@ import { UseToastOptions } from '@chakra-ui/react'; import { logger } from 'app/logging/logger'; import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice'; -import { controlNetImageChanged } from 'features/controlNet/store/controlNetSlice'; +import { + controlNetImageChanged, + ipAdapterImageChanged, +} from 'features/controlNet/store/controlNetSlice'; import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice'; import { initialImageChanged } from 'features/parameters/store/generationSlice'; import { addToast } from 'features/system/store/systemSlice'; +import { t } from 'i18next'; import { omit } from 'lodash-es'; import { boardsApi } from 'services/api/endpoints/boards'; import { startAppListening } from '..'; import { imagesApi } from '../../../../../services/api/endpoints/images'; -import { t } from 'i18next'; const DEFAULT_UPLOADED_TOAST: UseToastOptions = { title: t('toast.imageUploaded'), @@ -99,6 +102,17 @@ export const addImageUploadedFulfilledListener = () => { return; } + if (postUploadAction?.type === 'SET_IP_ADAPTER_IMAGE') { + dispatch(ipAdapterImageChanged(imageDTO)); + dispatch( + addToast({ + ...DEFAULT_UPLOADED_TOAST, + description: t('toast.setIPAdapterImage'), + }) + ); + return; + } + if (postUploadAction?.type === 'SET_INITIAL_IMAGE') { dispatch(initialImageChanged(imageDTO)); dispatch( diff --git a/invokeai/frontend/web/src/app/store/store.ts b/invokeai/frontend/web/src/app/store/store.ts index f84f3dd9c7..d45ae35cdb 100644 --- a/invokeai/frontend/web/src/app/store/store.ts +++ b/invokeai/frontend/web/src/app/store/store.ts @@ -86,7 +86,10 @@ export const store = configureStore({ .concat(autoBatchEnhancer()); }, middleware: (getDefaultMiddleware) => - getDefaultMiddleware({ immutableCheck: false }) + getDefaultMiddleware({ + serializableCheck: false, + immutableCheck: false, + }) .concat(api.middleware) .concat(dynamicMiddlewares) .prepend(listenerMiddleware.middleware), diff --git a/invokeai/frontend/web/src/features/controlNet/components/ControlNet.tsx b/invokeai/frontend/web/src/features/controlNet/components/ControlNet.tsx index d40a234495..13ceaf4173 100644 --- a/invokeai/frontend/web/src/features/controlNet/components/ControlNet.tsx +++ b/invokeai/frontend/web/src/features/controlNet/components/ControlNet.tsx @@ -18,6 +18,7 @@ import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import IAIIconButton from 'common/components/IAIIconButton'; import IAISwitch from 'common/components/IAISwitch'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; +import { useTranslation } from 'react-i18next'; import { useToggle } from 'react-use'; import { v4 as uuidv4 } from 'uuid'; import ControlNetImagePreview from './ControlNetImagePreview'; @@ -28,7 +29,6 @@ import ParamControlNetBeginEnd from './parameters/ParamControlNetBeginEnd'; import ParamControlNetControlMode from './parameters/ParamControlNetControlMode'; import ParamControlNetProcessorSelect from './parameters/ParamControlNetProcessorSelect'; import ParamControlNetResizeMode from './parameters/ParamControlNetResizeMode'; -import { useTranslation } from 'react-i18next'; type ControlNetProps = { controlNet: ControlNetConfig; diff --git a/invokeai/frontend/web/src/features/controlNet/components/ipAdapter/IPAdapterPanel.tsx b/invokeai/frontend/web/src/features/controlNet/components/ipAdapter/IPAdapterPanel.tsx new file mode 100644 index 0000000000..9dbd7e2ffa --- /dev/null +++ b/invokeai/frontend/web/src/features/controlNet/components/ipAdapter/IPAdapterPanel.tsx @@ -0,0 +1,33 @@ +import { Flex } from '@chakra-ui/react'; +import { memo } from 'react'; +import ParamIPAdapterFeatureToggle from './ParamIPAdapterFeatureToggle'; +import ParamIPAdapterImage from './ParamIPAdapterImage'; +import ParamIPAdapterModelSelect from './ParamIPAdapterModelSelect'; +import ParamIPAdapterWeight from './ParamIPAdapterWeight'; + +const IPAdapterPanel = () => { + return ( + + + + + + + ); +}; + +export default memo(IPAdapterPanel); diff --git a/invokeai/frontend/web/src/features/controlNet/components/ipAdapter/ParamIPAdapterFeatureToggle.tsx b/invokeai/frontend/web/src/features/controlNet/components/ipAdapter/ParamIPAdapterFeatureToggle.tsx new file mode 100644 index 0000000000..c077e7a824 --- /dev/null +++ b/invokeai/frontend/web/src/features/controlNet/components/ipAdapter/ParamIPAdapterFeatureToggle.tsx @@ -0,0 +1,41 @@ +import { createSelector } from '@reduxjs/toolkit'; +import { stateSelector } from 'app/store/store'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; +import IAISwitch from 'common/components/IAISwitch'; +import { isIPAdapterEnableToggled } from 'features/controlNet/store/controlNetSlice'; +import { memo, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; + +const selector = createSelector( + stateSelector, + (state) => { + const { isIPAdapterEnabled } = state.controlNet; + + return { isIPAdapterEnabled }; + }, + defaultSelectorOptions +); + +const ParamIPAdapterFeatureToggle = () => { + const { isIPAdapterEnabled } = useAppSelector(selector); + const dispatch = useAppDispatch(); + const { t } = useTranslation(); + + const handleChange = useCallback(() => { + dispatch(isIPAdapterEnableToggled()); + }, [dispatch]); + + return ( + + ); +}; + +export default memo(ParamIPAdapterFeatureToggle); diff --git a/invokeai/frontend/web/src/features/controlNet/components/ipAdapter/ParamIPAdapterImage.tsx b/invokeai/frontend/web/src/features/controlNet/components/ipAdapter/ParamIPAdapterImage.tsx new file mode 100644 index 0000000000..dccbd89ae7 --- /dev/null +++ b/invokeai/frontend/web/src/features/controlNet/components/ipAdapter/ParamIPAdapterImage.tsx @@ -0,0 +1,93 @@ +import { Flex } from '@chakra-ui/react'; +import { skipToken } from '@reduxjs/toolkit/dist/query'; +import { RootState } from 'app/store/store'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import IAIDndImage from 'common/components/IAIDndImage'; +import IAIIconButton from 'common/components/IAIIconButton'; +import { IAINoContentFallback } from 'common/components/IAIImageFallback'; +import { ipAdapterImageChanged } from 'features/controlNet/store/controlNetSlice'; +import { + TypesafeDraggableData, + TypesafeDroppableData, +} from 'features/dnd/types'; +import { memo, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { FaUndo } from 'react-icons/fa'; +import { useGetImageDTOQuery } from 'services/api/endpoints/images'; + +const ParamIPAdapterImage = () => { + const ipAdapterInfo = useAppSelector( + (state: RootState) => state.controlNet.ipAdapterInfo + ); + + const isIPAdapterEnabled = useAppSelector( + (state: RootState) => state.controlNet.isIPAdapterEnabled + ); + + const dispatch = useAppDispatch(); + const { t } = useTranslation(); + + const { currentData: imageDTO } = useGetImageDTOQuery( + ipAdapterInfo.adapterImage?.image_name ?? skipToken + ); + + const draggableData = useMemo(() => { + if (imageDTO) { + return { + id: 'ip-adapter-image', + payloadType: 'IMAGE_DTO', + payload: { imageDTO }, + }; + } + }, [imageDTO]); + + const droppableData = useMemo( + () => ({ + id: 'ip-adapter-image', + actionType: 'SET_IP_ADAPTER_IMAGE', + }), + [] + ); + + return ( + + + } + /> + + {ipAdapterInfo.adapterImage && ( + } + onClick={() => dispatch(ipAdapterImageChanged(null))} + isDisabled={!imageDTO} + size="sm" + sx={{ + position: 'absolute', + top: 3, + right: 3, + }} + /> + )} + + ); +}; + +export default memo(ParamIPAdapterImage); diff --git a/invokeai/frontend/web/src/features/controlNet/components/ipAdapter/ParamIPAdapterModelSelect.tsx b/invokeai/frontend/web/src/features/controlNet/components/ipAdapter/ParamIPAdapterModelSelect.tsx new file mode 100644 index 0000000000..fec1790b53 --- /dev/null +++ b/invokeai/frontend/web/src/features/controlNet/components/ipAdapter/ParamIPAdapterModelSelect.tsx @@ -0,0 +1,97 @@ +import { SelectItem } from '@mantine/core'; +import { RootState } from 'app/store/store'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import IAIMantineSelect from 'common/components/IAIMantineSelect'; +import { ipAdapterModelChanged } from 'features/controlNet/store/controlNetSlice'; +import { MODEL_TYPE_MAP } from 'features/parameters/types/constants'; +import { modelIdToIPAdapterModelParam } from 'features/parameters/util/modelIdToIPAdapterModelParams'; +import { forEach } from 'lodash-es'; +import { memo, useCallback, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useGetIPAdapterModelsQuery } from 'services/api/endpoints/models'; + +const ParamIPAdapterModelSelect = () => { + const ipAdapterModel = useAppSelector( + (state: RootState) => state.controlNet.ipAdapterInfo.model + ); + const model = useAppSelector((state: RootState) => state.generation.model); + + const dispatch = useAppDispatch(); + const { t } = useTranslation(); + + const { data: ipAdapterModels } = useGetIPAdapterModelsQuery(); + + // grab the full model entity from the RTK Query cache + const selectedModel = useMemo( + () => + ipAdapterModels?.entities[ + `${ipAdapterModel?.base_model}/ip_adapter/${ipAdapterModel?.model_name}` + ] ?? null, + [ + ipAdapterModel?.base_model, + ipAdapterModel?.model_name, + ipAdapterModels?.entities, + ] + ); + + const data = useMemo(() => { + if (!ipAdapterModels) { + return []; + } + + const data: SelectItem[] = []; + + forEach(ipAdapterModels.entities, (ipAdapterModel, id) => { + if (!ipAdapterModel) { + return; + } + + const disabled = model?.base_model !== ipAdapterModel.base_model; + + data.push({ + value: id, + label: ipAdapterModel.model_name, + group: MODEL_TYPE_MAP[ipAdapterModel.base_model], + disabled, + tooltip: disabled + ? `Incompatible base model: ${ipAdapterModel.base_model}` + : undefined, + }); + }); + + return data.sort((a, b) => (a.disabled && !b.disabled ? 1 : -1)); + }, [ipAdapterModels, model?.base_model]); + + const handleValueChanged = useCallback( + (v: string | null) => { + if (!v) { + return; + } + + const newIPAdapterModel = modelIdToIPAdapterModelParam(v); + + if (!newIPAdapterModel) { + return; + } + + dispatch(ipAdapterModelChanged(newIPAdapterModel)); + }, + [dispatch] + ); + + return ( + + ); +}; + +export default memo(ParamIPAdapterModelSelect); diff --git a/invokeai/frontend/web/src/features/controlNet/components/ipAdapter/ParamIPAdapterWeight.tsx b/invokeai/frontend/web/src/features/controlNet/components/ipAdapter/ParamIPAdapterWeight.tsx new file mode 100644 index 0000000000..df5a9a36a3 --- /dev/null +++ b/invokeai/frontend/web/src/features/controlNet/components/ipAdapter/ParamIPAdapterWeight.tsx @@ -0,0 +1,46 @@ +import { RootState } from 'app/store/store'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import IAISlider from 'common/components/IAISlider'; +import { ipAdapterWeightChanged } from 'features/controlNet/store/controlNetSlice'; +import { memo, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; + +const ParamIPAdapterWeight = () => { + const isIpAdapterEnabled = useAppSelector( + (state: RootState) => state.controlNet.isIPAdapterEnabled + ); + const ipAdapterWeight = useAppSelector( + (state: RootState) => state.controlNet.ipAdapterInfo.weight + ); + const dispatch = useAppDispatch(); + const { t } = useTranslation(); + + const handleWeightChanged = useCallback( + (weight: number) => { + dispatch(ipAdapterWeightChanged(weight)); + }, + [dispatch] + ); + + const handleWeightReset = useCallback(() => { + dispatch(ipAdapterWeightChanged(1)); + }, [dispatch]); + + return ( + + ); +}; + +export default memo(ParamIPAdapterWeight); diff --git a/invokeai/frontend/web/src/features/controlNet/store/controlNetSlice.ts b/invokeai/frontend/web/src/features/controlNet/store/controlNetSlice.ts index 8f391521d6..df55c44cf3 100644 --- a/invokeai/frontend/web/src/features/controlNet/store/controlNetSlice.ts +++ b/invokeai/frontend/web/src/features/controlNet/store/controlNetSlice.ts @@ -1,9 +1,13 @@ import { PayloadAction, createSlice } from '@reduxjs/toolkit'; -import { ControlNetModelParam } from 'features/parameters/types/parameterSchemas'; +import { + ControlNetModelParam, + IPAdapterModelParam, +} from 'features/parameters/types/parameterSchemas'; import { cloneDeep, forEach } from 'lodash-es'; import { imagesApi } from 'services/api/endpoints/images'; import { components } from 'services/api/schema'; import { isAnySessionRejected } from 'services/api/thunks/session'; +import { ImageDTO } from 'services/api/types'; import { appSocketInvocationError } from 'services/events/actions'; import { controlNetImageProcessed } from './actions'; import { @@ -56,16 +60,32 @@ export type ControlNetConfig = { shouldAutoConfig: boolean; }; +export type IPAdapterConfig = { + adapterImage: ImageDTO | null; + model: IPAdapterModelParam | null; + weight: number; +}; + export type ControlNetState = { controlNets: Record; isEnabled: boolean; pendingControlImages: string[]; + isIPAdapterEnabled: boolean; + ipAdapterInfo: IPAdapterConfig; +}; + +export const initialIPAdapterState: IPAdapterConfig = { + adapterImage: null, + model: null, + weight: 1, }; export const initialControlNetState: ControlNetState = { controlNets: {}, isEnabled: false, pendingControlImages: [], + isIPAdapterEnabled: false, + ipAdapterInfo: { ...initialIPAdapterState }, }; export const controlNetSlice = createSlice({ @@ -353,6 +373,25 @@ export const controlNetSlice = createSlice({ controlNetReset: () => { return { ...initialControlNetState }; }, + isIPAdapterEnableToggled: (state) => { + state.isIPAdapterEnabled = !state.isIPAdapterEnabled; + }, + ipAdapterImageChanged: (state, action: PayloadAction) => { + state.ipAdapterInfo.adapterImage = action.payload; + }, + ipAdapterWeightChanged: (state, action: PayloadAction) => { + state.ipAdapterInfo.weight = action.payload; + }, + ipAdapterModelChanged: ( + state, + action: PayloadAction + ) => { + state.ipAdapterInfo.model = action.payload; + }, + ipAdapterStateReset: (state) => { + state.isIPAdapterEnabled = false; + state.ipAdapterInfo = { ...initialIPAdapterState }; + }, }, extraReducers: (builder) => { builder.addCase(controlNetImageProcessed, (state, action) => { @@ -412,6 +451,11 @@ export const { controlNetProcessorTypeChanged, controlNetReset, controlNetAutoConfigToggled, + isIPAdapterEnableToggled, + ipAdapterImageChanged, + ipAdapterWeightChanged, + ipAdapterModelChanged, + ipAdapterStateReset, } = controlNetSlice.actions; export default controlNetSlice.reducer; diff --git a/invokeai/frontend/web/src/features/deleteImageModal/components/DeleteImageModal.tsx b/invokeai/frontend/web/src/features/deleteImageModal/components/DeleteImageModal.tsx index 0d8ecfbae6..78bd71e802 100644 --- a/invokeai/frontend/web/src/features/deleteImageModal/components/DeleteImageModal.tsx +++ b/invokeai/frontend/web/src/features/deleteImageModal/components/DeleteImageModal.tsx @@ -10,20 +10,20 @@ import { Text, } from '@chakra-ui/react'; import { createSelector } from '@reduxjs/toolkit'; +import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import IAIButton from 'common/components/IAIButton'; import IAISwitch from 'common/components/IAISwitch'; import { setShouldConfirmOnDelete } from 'features/system/store/systemSlice'; -import { stateSelector } from 'app/store/store'; import { some } from 'lodash-es'; import { ChangeEvent, memo, useCallback, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { imageDeletionConfirmed } from '../store/actions'; import { getImageUsage, selectImageUsage } from '../store/selectors'; import { imageDeletionCanceled, isModalOpenChanged } from '../store/slice'; -import ImageUsageMessage from './ImageUsageMessage'; import { ImageUsage } from '../store/types'; +import ImageUsageMessage from './ImageUsageMessage'; const selector = createSelector( [stateSelector, selectImageUsage], @@ -42,6 +42,7 @@ const selector = createSelector( isCanvasImage: some(allImageUsage, (i) => i.isCanvasImage), isNodesImage: some(allImageUsage, (i) => i.isNodesImage), isControlNetImage: some(allImageUsage, (i) => i.isControlNetImage), + isIPAdapterImage: some(allImageUsage, (i) => i.isIPAdapterImage), }; return { diff --git a/invokeai/frontend/web/src/features/deleteImageModal/components/ImageUsageMessage.tsx b/invokeai/frontend/web/src/features/deleteImageModal/components/ImageUsageMessage.tsx index de1782b439..6844e13a32 100644 --- a/invokeai/frontend/web/src/features/deleteImageModal/components/ImageUsageMessage.tsx +++ b/invokeai/frontend/web/src/features/deleteImageModal/components/ImageUsageMessage.tsx @@ -1,8 +1,8 @@ import { ListItem, Text, UnorderedList } from '@chakra-ui/react'; import { some } from 'lodash-es'; import { memo } from 'react'; -import { ImageUsage } from '../store/types'; import { useTranslation } from 'react-i18next'; +import { ImageUsage } from '../store/types'; type Props = { imageUsage?: ImageUsage; @@ -38,6 +38,9 @@ const ImageUsageMessage = (props: Props) => { {imageUsage.isControlNetImage && ( {t('common.controlNet')} )} + {imageUsage.isIPAdapterImage && ( + {t('common.ipAdapter')} + )} {imageUsage.isNodesImage && ( {t('common.nodeEditor')} )} diff --git a/invokeai/frontend/web/src/features/deleteImageModal/store/selectors.ts b/invokeai/frontend/web/src/features/deleteImageModal/store/selectors.ts index 37be06bad6..151e975634 100644 --- a/invokeai/frontend/web/src/features/deleteImageModal/store/selectors.ts +++ b/invokeai/frontend/web/src/features/deleteImageModal/store/selectors.ts @@ -1,9 +1,9 @@ import { createSelector } from '@reduxjs/toolkit'; import { RootState } from 'app/store/store'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; +import { isInvocationNode } from 'features/nodes/types/types'; import { some } from 'lodash-es'; import { ImageUsage } from './types'; -import { isInvocationNode } from 'features/nodes/types/types'; export const getImageUsage = (state: RootState, image_name: string) => { const { generation, canvas, nodes, controlNet } = state; @@ -27,11 +27,15 @@ export const getImageUsage = (state: RootState, image_name: string) => { c.controlImage === image_name || c.processedControlImage === image_name ); + const isIPAdapterImage = + controlNet.ipAdapterInfo.adapterImage?.image_name === image_name; + const imageUsage: ImageUsage = { isInitialImage, isCanvasImage, isNodesImage, isControlNetImage, + isIPAdapterImage, }; return imageUsage; diff --git a/invokeai/frontend/web/src/features/deleteImageModal/store/types.ts b/invokeai/frontend/web/src/features/deleteImageModal/store/types.ts index 2beaa8ca2e..fd13462866 100644 --- a/invokeai/frontend/web/src/features/deleteImageModal/store/types.ts +++ b/invokeai/frontend/web/src/features/deleteImageModal/store/types.ts @@ -10,4 +10,5 @@ export type ImageUsage = { isCanvasImage: boolean; isNodesImage: boolean; isControlNetImage: boolean; + isIPAdapterImage: boolean; }; diff --git a/invokeai/frontend/web/src/features/dnd/types/index.ts b/invokeai/frontend/web/src/features/dnd/types/index.ts index 294132d0a3..3c5751bfac 100644 --- a/invokeai/frontend/web/src/features/dnd/types/index.ts +++ b/invokeai/frontend/web/src/features/dnd/types/index.ts @@ -35,6 +35,10 @@ export type ControlNetDropData = BaseDropData & { }; }; +export type IPAdapterImageDropData = BaseDropData & { + actionType: 'SET_IP_ADAPTER_IMAGE'; +}; + export type CanvasInitialImageDropData = BaseDropData & { actionType: 'SET_CANVAS_INITIAL_IMAGE'; }; @@ -73,6 +77,7 @@ export type TypesafeDroppableData = | CurrentImageDropData | InitialImageDropData | ControlNetDropData + | IPAdapterImageDropData | CanvasInitialImageDropData | NodesImageDropData | AddToBatchDropData diff --git a/invokeai/frontend/web/src/features/dnd/util/isValidDrop.ts b/invokeai/frontend/web/src/features/dnd/util/isValidDrop.ts index f704d22dff..b040caf83d 100644 --- a/invokeai/frontend/web/src/features/dnd/util/isValidDrop.ts +++ b/invokeai/frontend/web/src/features/dnd/util/isValidDrop.ts @@ -24,6 +24,8 @@ export const isValidDrop = ( return payloadType === 'IMAGE_DTO'; case 'SET_CONTROLNET_IMAGE': return payloadType === 'IMAGE_DTO'; + case 'SET_IP_ADAPTER_IMAGE': + return payloadType === 'IMAGE_DTO'; case 'SET_CANVAS_INITIAL_IMAGE': return payloadType === 'IMAGE_DTO'; case 'SET_NODES_IMAGE': diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/DeleteBoardModal.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/DeleteBoardModal.tsx index 2c54f06cec..f83c08c923 100644 --- a/invokeai/frontend/web/src/features/gallery/components/Boards/DeleteBoardModal.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/DeleteBoardModal.tsx @@ -53,6 +53,7 @@ const DeleteBoardModal = (props: Props) => { isCanvasImage: some(allImageUsage, (i) => i.isCanvasImage), isNodesImage: some(allImageUsage, (i) => i.isNodesImage), isControlNetImage: some(allImageUsage, (i) => i.isControlNetImage), + isIPAdapterImage: some(allImageUsage, (i) => i.isIPAdapterImage), }; return { imageUsageSummary }; }), diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/addIPAdapterToLinearGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/addIPAdapterToLinearGraph.ts new file mode 100644 index 0000000000..95b63e7c3f --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/addIPAdapterToLinearGraph.ts @@ -0,0 +1,57 @@ +import { RootState } from 'app/store/store'; +import { IPAdapterInvocation } from 'services/api/types'; +import { NonNullableGraph } from '../../types/types'; +import { IP_ADAPTER } from './constants'; + +export const addIPAdapterToLinearGraph = ( + state: RootState, + graph: NonNullableGraph, + baseNodeId: string +): void => { + const { isIPAdapterEnabled, ipAdapterInfo } = state.controlNet; + + // const metadataAccumulator = graph.nodes[METADATA_ACCUMULATOR] as + // | MetadataAccumulatorInvocation + // | undefined; + + if (isIPAdapterEnabled) { + const ipAdapterNode: IPAdapterInvocation = { + id: IP_ADAPTER, + type: 'ip_adapter', + is_intermediate: true, + weight: ipAdapterInfo.weight, + ip_adapter_model: { + base_model: 'sd-1', + model_name: 'ip_adapter_plus_sd15', + }, + }; + + if (ipAdapterInfo.adapterImage) { + ipAdapterNode.image = { + image_name: ipAdapterInfo.adapterImage.image_name, + }; + } else { + return; + } + + graph.nodes[ipAdapterNode.id] = ipAdapterNode as IPAdapterInvocation; + + // if (metadataAccumulator?.ip_adapters) { + // // metadata accumulator only needs the ip_adapter field - not the whole node + // // extract what we need and add to the accumulator + // const ipAdapterField = omit(ipAdapterNode, [ + // 'id', + // 'type', + // ]) as IPAdapterField; + // metadataAccumulator.ip_adapters.push(ipAdapterField); + // } + + graph.edges.push({ + source: { node_id: ipAdapterNode.id, field: 'ip_adapter' }, + destination: { + node_id: baseNodeId, + field: 'ip_adapter', + }, + }); + } +}; diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasImageToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasImageToImageGraph.ts index e750b40220..4d733b9c73 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasImageToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasImageToImageGraph.ts @@ -5,6 +5,7 @@ import { initialGenerationState } from 'features/parameters/store/generationSlic import { ImageDTO, ImageToLatentsInvocation } from 'services/api/types'; import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; import { addDynamicPromptsToGraph } from './addDynamicPromptsToGraph'; +import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; import { addLoRAsToGraph } from './addLoRAsToGraph'; import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph'; @@ -366,6 +367,9 @@ export const buildCanvasImageToImageGraph = ( // add controlnet, mutating `graph` addControlNetToLinearGraph(state, graph, DENOISE_LATENTS); + // Add IP Adapter + addIPAdapterToLinearGraph(state, graph, DENOISE_LATENTS); + // NSFW & watermark - must be last thing added to graph if (state.system.shouldUseNSFWChecker) { // must add before watermarker! diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasInpaintGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasInpaintGraph.ts index bfedc03de4..36a17c34e5 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasInpaintGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasInpaintGraph.ts @@ -12,6 +12,7 @@ import { RangeOfSizeInvocation, } from 'services/api/types'; import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; +import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; import { addLoRAsToGraph } from './addLoRAsToGraph'; import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph'; @@ -736,6 +737,9 @@ export const buildCanvasInpaintGraph = ( // add controlnet, mutating `graph` addControlNetToLinearGraph(state, graph, DENOISE_LATENTS); + // Add IP Adapter + addIPAdapterToLinearGraph(state, graph, DENOISE_LATENTS); + // NSFW & watermark - must be last thing added to graph if (state.system.shouldUseNSFWChecker) { // must add before watermarker! diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasOutpaintGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasOutpaintGraph.ts index 71d3492bdc..ae5a9b066a 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasOutpaintGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasOutpaintGraph.ts @@ -11,6 +11,7 @@ import { RangeOfSizeInvocation, } from 'services/api/types'; import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; +import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; import { addLoRAsToGraph } from './addLoRAsToGraph'; import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph'; @@ -838,6 +839,9 @@ export const buildCanvasOutpaintGraph = ( // add controlnet, mutating `graph` addControlNetToLinearGraph(state, graph, DENOISE_LATENTS); + // Add IP Adapter + addIPAdapterToLinearGraph(state, graph, DENOISE_LATENTS); + // NSFW & watermark - must be last thing added to graph if (state.system.shouldUseNSFWChecker) { // must add before watermarker! diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasSDXLImageToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasSDXLImageToImageGraph.ts index 2a677a8775..5cc5d567d0 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasSDXLImageToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasSDXLImageToImageGraph.ts @@ -5,6 +5,7 @@ import { initialGenerationState } from 'features/parameters/store/generationSlic import { ImageDTO, ImageToLatentsInvocation } from 'services/api/types'; import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; import { addDynamicPromptsToGraph } from './addDynamicPromptsToGraph'; +import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; import { addSDXLLoRAsToGraph } from './addSDXLLoRAstoGraph'; import { addSDXLRefinerToGraph } from './addSDXLRefinerToGraph'; @@ -392,6 +393,9 @@ export const buildCanvasSDXLImageToImageGraph = ( // add controlnet, mutating `graph` addControlNetToLinearGraph(state, graph, SDXL_DENOISE_LATENTS); + // Add IP Adapter + addIPAdapterToLinearGraph(state, graph, SDXL_DENOISE_LATENTS); + // NSFW & watermark - must be last thing added to graph if (state.system.shouldUseNSFWChecker) { // must add before watermarker! diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasSDXLInpaintGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasSDXLInpaintGraph.ts index baa1c0155f..b5b765b19e 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasSDXLInpaintGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasSDXLInpaintGraph.ts @@ -46,6 +46,7 @@ import { SEAMLESS, } from './constants'; import { craftSDXLStylePrompt } from './helpers/craftSDXLStylePrompt'; +import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; /** * Builds the Canvas tab's Inpaint graph. @@ -765,6 +766,9 @@ export const buildCanvasSDXLInpaintGraph = ( // add controlnet, mutating `graph` addControlNetToLinearGraph(state, graph, SDXL_DENOISE_LATENTS); + // Add IP Adapter + addIPAdapterToLinearGraph(state, graph, SDXL_DENOISE_LATENTS); + // NSFW & watermark - must be last thing added to graph if (state.system.shouldUseNSFWChecker) { // must add before watermarker! diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasSDXLOutpaintGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasSDXLOutpaintGraph.ts index 3f8fffb70a..30cea4915a 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasSDXLOutpaintGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasSDXLOutpaintGraph.ts @@ -11,6 +11,7 @@ import { RangeOfSizeInvocation, } from 'services/api/types'; import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; +import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; import { addSDXLLoRAsToGraph } from './addSDXLLoRAstoGraph'; import { addSDXLRefinerToGraph } from './addSDXLRefinerToGraph'; @@ -868,6 +869,9 @@ export const buildCanvasSDXLOutpaintGraph = ( // add controlnet, mutating `graph` addControlNetToLinearGraph(state, graph, SDXL_DENOISE_LATENTS); + // Add IP Adapter + addIPAdapterToLinearGraph(state, graph, SDXL_DENOISE_LATENTS); + // NSFW & watermark - must be last thing added to graph if (state.system.shouldUseNSFWChecker) { // must add before watermarker! diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasSDXLTextToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasSDXLTextToImageGraph.ts index 6d787987ef..c191afe6c1 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasSDXLTextToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasSDXLTextToImageGraph.ts @@ -8,6 +8,7 @@ import { } from 'services/api/types'; import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; import { addDynamicPromptsToGraph } from './addDynamicPromptsToGraph'; +import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; import { addSDXLLoRAsToGraph } from './addSDXLLoRAstoGraph'; import { addSDXLRefinerToGraph } from './addSDXLRefinerToGraph'; @@ -372,6 +373,9 @@ export const buildCanvasSDXLTextToImageGraph = ( // add controlnet, mutating `graph` addControlNetToLinearGraph(state, graph, SDXL_DENOISE_LATENTS); + // Add IP Adapter + addIPAdapterToLinearGraph(state, graph, SDXL_DENOISE_LATENTS); + // NSFW & watermark - must be last thing added to graph if (state.system.shouldUseNSFWChecker) { // must add before watermarker! diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasTextToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasTextToImageGraph.ts index 094a7b02cc..28dd239e29 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasTextToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasTextToImageGraph.ts @@ -8,6 +8,7 @@ import { } from 'services/api/types'; import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; import { addDynamicPromptsToGraph } from './addDynamicPromptsToGraph'; +import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; import { addLoRAsToGraph } from './addLoRAsToGraph'; import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph'; @@ -345,6 +346,9 @@ export const buildCanvasTextToImageGraph = ( // add controlnet, mutating `graph` addControlNetToLinearGraph(state, graph, DENOISE_LATENTS); + // Add IP Adapter + addIPAdapterToLinearGraph(state, graph, DENOISE_LATENTS); + // NSFW & watermark - must be last thing added to graph if (state.system.shouldUseNSFWChecker) { // must add before watermarker! diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearImageToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearImageToImageGraph.ts index dc9a34c67e..7127026a76 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearImageToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearImageToImageGraph.ts @@ -8,6 +8,7 @@ import { } from 'services/api/types'; import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; import { addDynamicPromptsToGraph } from './addDynamicPromptsToGraph'; +import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; import { addLoRAsToGraph } from './addLoRAsToGraph'; import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph'; @@ -364,6 +365,9 @@ export const buildLinearImageToImageGraph = ( // add controlnet, mutating `graph` addControlNetToLinearGraph(state, graph, DENOISE_LATENTS); + // Add IP Adapter + addIPAdapterToLinearGraph(state, graph, DENOISE_LATENTS); + // NSFW & watermark - must be last thing added to graph if (state.system.shouldUseNSFWChecker) { // must add before watermarker! diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearSDXLImageToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearSDXLImageToImageGraph.ts index 757a3976bb..07aaf31e12 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearSDXLImageToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearSDXLImageToImageGraph.ts @@ -8,6 +8,7 @@ import { } from 'services/api/types'; import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; import { addDynamicPromptsToGraph } from './addDynamicPromptsToGraph'; +import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; import { addSDXLLoRAsToGraph } from './addSDXLLoRAstoGraph'; import { addSDXLRefinerToGraph } from './addSDXLRefinerToGraph'; @@ -384,6 +385,9 @@ export const buildLinearSDXLImageToImageGraph = ( // add controlnet, mutating `graph` addControlNetToLinearGraph(state, graph, SDXL_DENOISE_LATENTS); + // Add IP Adapter + addIPAdapterToLinearGraph(state, graph, SDXL_DENOISE_LATENTS); + // add dynamic prompts - also sets up core iteration and seed addDynamicPromptsToGraph(state, graph); diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearSDXLTextToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearSDXLTextToImageGraph.ts index 9590e77f89..2bcd981b33 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearSDXLTextToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearSDXLTextToImageGraph.ts @@ -4,6 +4,7 @@ import { NonNullableGraph } from 'features/nodes/types/types'; import { initialGenerationState } from 'features/parameters/store/generationSlice'; import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; import { addDynamicPromptsToGraph } from './addDynamicPromptsToGraph'; +import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; import { addSDXLLoRAsToGraph } from './addSDXLLoRAstoGraph'; import { addSDXLRefinerToGraph } from './addSDXLRefinerToGraph'; @@ -277,6 +278,9 @@ export const buildLinearSDXLTextToImageGraph = ( // add controlnet, mutating `graph` addControlNetToLinearGraph(state, graph, SDXL_DENOISE_LATENTS); + // add IP Adapter + addIPAdapterToLinearGraph(state, graph, SDXL_DENOISE_LATENTS); + // add dynamic prompts - also sets up core iteration and seed addDynamicPromptsToGraph(state, graph); diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearTextToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearTextToImageGraph.ts index 5c534fff21..6235e73d08 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearTextToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearTextToImageGraph.ts @@ -8,6 +8,7 @@ import { } from 'services/api/types'; import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; import { addDynamicPromptsToGraph } from './addDynamicPromptsToGraph'; +import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; import { addLoRAsToGraph } from './addLoRAsToGraph'; import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph'; @@ -282,6 +283,9 @@ export const buildLinearTextToImageGraph = ( // add controlnet, mutating `graph` addControlNetToLinearGraph(state, graph, DENOISE_LATENTS); + // add IP Adapter + addIPAdapterToLinearGraph(state, graph, DENOISE_LATENTS); + // NSFW & watermark - must be last thing added to graph if (state.system.shouldUseNSFWChecker) { // must add before watermarker! diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/constants.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/constants.ts index 70bd0c4058..65a29e5b7b 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/constants.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/constants.ts @@ -45,6 +45,7 @@ export const MASK_RESIZE_DOWN = 'mask_resize_down'; export const COLOR_CORRECT = 'color_correct'; export const PASTE_IMAGE = 'img_paste'; export const CONTROL_NET_COLLECT = 'control_net_collect'; +export const IP_ADAPTER = 'ip_adapter'; export const DYNAMIC_PROMPT = 'dynamic_prompt'; export const IMAGE_COLLECTION = 'image_collection'; export const IMAGE_COLLECTION_ITERATE = 'image_collection_iterate'; diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/ControlNet/ParamControlNetCollapse.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/ControlNet/ParamControlNetCollapse.tsx index 39ff5022d4..2c40307ee3 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/ControlNet/ParamControlNetCollapse.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/ControlNet/ParamControlNetCollapse.tsx @@ -6,6 +6,7 @@ import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import IAICollapse from 'common/components/IAICollapse'; import IAIIconButton from 'common/components/IAIIconButton'; import ControlNet from 'features/controlNet/components/ControlNet'; +import IPAdapterPanel from 'features/controlNet/components/ipAdapter/IPAdapterPanel'; import ParamControlNetFeatureToggle from 'features/controlNet/components/parameters/ParamControlNetFeatureToggle'; import { controlNetAdded, @@ -25,14 +26,23 @@ import { v4 as uuidv4 } from 'uuid'; const selector = createSelector( [stateSelector], ({ controlNet }) => { - const { controlNets, isEnabled } = controlNet; + const { controlNets, isEnabled, isIPAdapterEnabled } = controlNet; const validControlNets = getValidControlNets(controlNets); - const activeLabel = - isEnabled && validControlNets.length > 0 - ? `${validControlNets.length} Active` - : undefined; + let activeLabel = undefined; + + if (isEnabled && validControlNets.length > 0) { + activeLabel = `${validControlNets.length} ControlNet`; + } + + if (isIPAdapterEnabled) { + if (activeLabel) { + activeLabel = `${activeLabel}, IP Adapter`; + } else { + activeLabel = 'IP Adapter'; + } + } return { controlNetsArray: map(controlNets), activeLabel }; }, @@ -101,6 +111,7 @@ const ParamControlNetCollapse = () => { ))} + ); diff --git a/invokeai/frontend/web/src/features/parameters/types/parameterSchemas.ts b/invokeai/frontend/web/src/features/parameters/types/parameterSchemas.ts index fa3d7b22fe..1b29993712 100644 --- a/invokeai/frontend/web/src/features/parameters/types/parameterSchemas.ts +++ b/invokeai/frontend/web/src/features/parameters/types/parameterSchemas.ts @@ -339,7 +339,7 @@ export const zIPAdapterModel = z.object({ /** * Type alias for model parameter, inferred from its zod schema */ -export type zIPAdapterModelParam = z.infer; +export type IPAdapterModelParam = z.infer; /** * Zod schema for l2l strength parameter */ diff --git a/invokeai/frontend/web/src/services/api/schema.d.ts b/invokeai/frontend/web/src/services/api/schema.d.ts index 82682a0760..91749c585f 100644 --- a/invokeai/frontend/web/src/services/api/schema.d.ts +++ b/invokeai/frontend/web/src/services/api/schema.d.ts @@ -2375,7 +2375,7 @@ export type components = { * @description The nodes in this graph */ nodes?: { - [key: string]: components["schemas"]["BooleanInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["ImageProcessorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["SDXLLoraLoaderInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["MetadataAccumulatorInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["ClipSkipInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["ONNXPromptInvocation"] | components["schemas"]["ONNXTextToLatentsInvocation"] | components["schemas"]["ONNXLatentsToImageInvocation"] | components["schemas"]["OnnxModelLoaderInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["GraphInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"]; + [key: string]: components["schemas"]["BooleanInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["ImageProcessorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["SDXLLoraLoaderInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["MetadataAccumulatorInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["ClipSkipInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["ONNXPromptInvocation"] | components["schemas"]["ONNXTextToLatentsInvocation"] | components["schemas"]["ONNXLatentsToImageInvocation"] | components["schemas"]["OnnxModelLoaderInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["GraphInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"]; }; /** * Edges @@ -2418,7 +2418,7 @@ export type components = { * @description The results of node executions */ results: { - [key: string]: components["schemas"]["BooleanOutput"] | components["schemas"]["BooleanCollectionOutput"] | components["schemas"]["IntegerOutput"] | components["schemas"]["IntegerCollectionOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["StringOutput"] | components["schemas"]["StringCollectionOutput"] | components["schemas"]["ImageOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["DenoiseMaskOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["LatentsCollectionOutput"] | components["schemas"]["ColorOutput"] | components["schemas"]["ColorCollectionOutput"] | components["schemas"]["ConditioningOutput"] | components["schemas"]["ConditioningCollectionOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["LoraLoaderOutput"] | components["schemas"]["SDXLLoraLoaderOutput"] | components["schemas"]["VaeLoaderOutput"] | components["schemas"]["SeamlessModeOutput"] | components["schemas"]["MetadataAccumulatorOutput"] | components["schemas"]["ClipSkipInvocationOutput"] | components["schemas"]["IPAdapterOutput"] | components["schemas"]["SchedulerOutput"] | components["schemas"]["ONNXModelLoaderOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["StringPosNegOutput"] | components["schemas"]["String2Output"] | components["schemas"]["SDXLModelLoaderOutput"] | components["schemas"]["SDXLRefinerModelLoaderOutput"] | components["schemas"]["GraphInvocationOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["CollectInvocationOutput"]; + [key: string]: components["schemas"]["BooleanOutput"] | components["schemas"]["BooleanCollectionOutput"] | components["schemas"]["IntegerOutput"] | components["schemas"]["IntegerCollectionOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["StringOutput"] | components["schemas"]["StringCollectionOutput"] | components["schemas"]["ImageOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["DenoiseMaskOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["LatentsCollectionOutput"] | components["schemas"]["ColorOutput"] | components["schemas"]["ColorCollectionOutput"] | components["schemas"]["ConditioningOutput"] | components["schemas"]["ConditioningCollectionOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["LoraLoaderOutput"] | components["schemas"]["SDXLLoraLoaderOutput"] | components["schemas"]["VaeLoaderOutput"] | components["schemas"]["SeamlessModeOutput"] | components["schemas"]["MetadataAccumulatorOutput"] | components["schemas"]["ClipSkipInvocationOutput"] | components["schemas"]["IPAdapterOutput"] | components["schemas"]["SchedulerOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["ONNXModelLoaderOutput"] | components["schemas"]["SDXLModelLoaderOutput"] | components["schemas"]["SDXLRefinerModelLoaderOutput"] | components["schemas"]["StringPosNegOutput"] | components["schemas"]["String2Output"] | components["schemas"]["GraphInvocationOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["CollectInvocationOutput"]; }; /** * Errors @@ -7706,18 +7706,18 @@ export type components = { /** Ui Order */ ui_order?: number; }; + /** + * CLIPVisionModelFormat + * @description An enumeration. + * @enum {string} + */ + CLIPVisionModelFormat: "diffusers"; /** * StableDiffusion2ModelFormat * @description An enumeration. * @enum {string} */ StableDiffusion2ModelFormat: "checkpoint" | "diffusers"; - /** - * ControlNetModelFormat - * @description An enumeration. - * @enum {string} - */ - ControlNetModelFormat: "checkpoint" | "diffusers"; /** * StableDiffusionXLModelFormat * @description An enumeration. @@ -7725,29 +7725,29 @@ export type components = { */ StableDiffusionXLModelFormat: "checkpoint" | "diffusers"; /** - * StableDiffusion1ModelFormat + * ControlNetModelFormat * @description An enumeration. * @enum {string} */ - StableDiffusion1ModelFormat: "checkpoint" | "diffusers"; + ControlNetModelFormat: "checkpoint" | "diffusers"; /** * IPAdapterModelFormat * @description An enumeration. * @enum {string} */ IPAdapterModelFormat: "invokeai"; + /** + * StableDiffusion1ModelFormat + * @description An enumeration. + * @enum {string} + */ + StableDiffusion1ModelFormat: "checkpoint" | "diffusers"; /** * StableDiffusionOnnxModelFormat * @description An enumeration. * @enum {string} */ StableDiffusionOnnxModelFormat: "olive" | "onnx"; - /** - * CLIPVisionModelFormat - * @description An enumeration. - * @enum {string} - */ - CLIPVisionModelFormat: "diffusers"; }; responses: never; parameters: never; @@ -7864,7 +7864,7 @@ export type operations = { }; requestBody: { content: { - "application/json": components["schemas"]["BooleanInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["ImageProcessorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["SDXLLoraLoaderInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["MetadataAccumulatorInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["ClipSkipInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["ONNXPromptInvocation"] | components["schemas"]["ONNXTextToLatentsInvocation"] | components["schemas"]["ONNXLatentsToImageInvocation"] | components["schemas"]["OnnxModelLoaderInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["GraphInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"]; + "application/json": components["schemas"]["BooleanInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["ImageProcessorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["SDXLLoraLoaderInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["MetadataAccumulatorInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["ClipSkipInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["ONNXPromptInvocation"] | components["schemas"]["ONNXTextToLatentsInvocation"] | components["schemas"]["ONNXLatentsToImageInvocation"] | components["schemas"]["OnnxModelLoaderInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["GraphInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"]; }; }; responses: { @@ -7905,7 +7905,7 @@ export type operations = { }; requestBody: { content: { - "application/json": components["schemas"]["BooleanInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["ImageProcessorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["SDXLLoraLoaderInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["MetadataAccumulatorInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["ClipSkipInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["ONNXPromptInvocation"] | components["schemas"]["ONNXTextToLatentsInvocation"] | components["schemas"]["ONNXLatentsToImageInvocation"] | components["schemas"]["OnnxModelLoaderInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["GraphInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"]; + "application/json": components["schemas"]["BooleanInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["ImageProcessorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["SDXLLoraLoaderInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["MetadataAccumulatorInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["ClipSkipInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["ONNXPromptInvocation"] | components["schemas"]["ONNXTextToLatentsInvocation"] | components["schemas"]["ONNXLatentsToImageInvocation"] | components["schemas"]["OnnxModelLoaderInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["GraphInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"]; }; }; responses: { diff --git a/invokeai/frontend/web/src/services/api/types.ts b/invokeai/frontend/web/src/services/api/types.ts index 0c6d2ad1c4..c432f70d93 100644 --- a/invokeai/frontend/web/src/services/api/types.ts +++ b/invokeai/frontend/web/src/services/api/types.ts @@ -63,6 +63,7 @@ export type ControlNetModelField = s['ControlNetModelField']; export type IPAdapterModelField = s['IPAdapterModelField']; export type ModelsList = s['ModelsList']; export type ControlField = s['ControlField']; +export type IPAdapterField = s['IPAdapterField']; // Model Configs export type LoRAModelConfig = s['LoRAModelConfig']; @@ -139,6 +140,7 @@ export type SeamlessModeInvocation = s['SeamlessModeInvocation']; // ControlNet Nodes export type ControlNetInvocation = s['ControlNetInvocation']; +export type IPAdapterInvocation = s['IPAdapterInvocation']; export type CannyImageProcessorInvocation = s['CannyImageProcessorInvocation']; export type ContentShuffleImageProcessorInvocation = s['ContentShuffleImageProcessorInvocation']; @@ -177,6 +179,10 @@ export type ControlNetAction = { controlNetId: string; }; +export type IPAdapterAction = { + type: 'SET_IP_ADAPTER_IMAGE'; +}; + export type InitialImageAction = { type: 'SET_INITIAL_IMAGE'; }; @@ -202,6 +208,7 @@ export type AddToBatchAction = { export type PostUploadAction = | ControlNetAction + | IPAdapterAction | InitialImageAction | NodesAction | CanvasInitialImageAction