mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): enable control adapters on image drop (#4627)
## What type of PR is this? (check all applicable)
- [ ] Refactor
- [x] Feature
- [ ] Bug Fix
- [ ] Optimization
- [ ] Documentation Update
- [ ] Community Node Submission
## Have you discussed this change with the InvokeAI team?
- [x] Yes
- [ ] No, because:
## Description
[feat(ui): enable control adapters on image
drop](aa4b56baf2
)
- Dropping/uploading an image on control adapter enables it (controlnet
& ip adapter)
- The image components are always enabled to allow this
This commit is contained in:
commit
19e487b5ee
@ -4,7 +4,9 @@ import { parseify } from 'common/util/serialize';
|
||||
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
||||
import {
|
||||
controlNetImageChanged,
|
||||
controlNetIsEnabledChanged,
|
||||
ipAdapterImageChanged,
|
||||
isIPAdapterEnabledChanged,
|
||||
} from 'features/controlNet/store/controlNetSlice';
|
||||
import {
|
||||
TypesafeDraggableData,
|
||||
@ -99,6 +101,12 @@ export const addImageDroppedListener = () => {
|
||||
controlNetId,
|
||||
})
|
||||
);
|
||||
dispatch(
|
||||
controlNetIsEnabledChanged({
|
||||
controlNetId,
|
||||
isEnabled: true,
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -111,6 +119,7 @@ export const addImageDroppedListener = () => {
|
||||
activeData.payload.imageDTO
|
||||
) {
|
||||
dispatch(ipAdapterImageChanged(activeData.payload.imageDTO));
|
||||
dispatch(isIPAdapterEnabledChanged(true));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,9 @@ import { logger } from 'app/logging/logger';
|
||||
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
||||
import {
|
||||
controlNetImageChanged,
|
||||
controlNetIsEnabledChanged,
|
||||
ipAdapterImageChanged,
|
||||
isIPAdapterEnabledChanged,
|
||||
} from 'features/controlNet/store/controlNetSlice';
|
||||
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
|
||||
import { initialImageChanged } from 'features/parameters/store/generationSlice';
|
||||
@ -87,6 +89,12 @@ export const addImageUploadedFulfilledListener = () => {
|
||||
|
||||
if (postUploadAction?.type === 'SET_CONTROLNET_IMAGE') {
|
||||
const { controlNetId } = postUploadAction;
|
||||
dispatch(
|
||||
controlNetIsEnabledChanged({
|
||||
controlNetId,
|
||||
isEnabled: true,
|
||||
})
|
||||
);
|
||||
dispatch(
|
||||
controlNetImageChanged({
|
||||
controlNetId,
|
||||
@ -104,6 +112,7 @@ export const addImageUploadedFulfilledListener = () => {
|
||||
|
||||
if (postUploadAction?.type === 'SET_IP_ADAPTER_IMAGE') {
|
||||
dispatch(ipAdapterImageChanged(imageDTO));
|
||||
dispatch(isIPAdapterEnabledChanged(true));
|
||||
dispatch(
|
||||
addToast({
|
||||
...DEFAULT_UPLOADED_TOAST,
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { ChangeEvent, memo, useCallback } from 'react';
|
||||
import { FaCopy, FaTrash } from 'react-icons/fa';
|
||||
import {
|
||||
ControlNetConfig,
|
||||
controlNetDuplicated,
|
||||
controlNetRemoved,
|
||||
controlNetToggled,
|
||||
controlNetIsEnabledChanged,
|
||||
} from '../store/controlNetSlice';
|
||||
import ParamControlNetModel from './parameters/ParamControlNetModel';
|
||||
import ParamControlNetWeight from './parameters/ParamControlNetWeight';
|
||||
@ -77,9 +77,17 @@ const ControlNet = (props: ControlNetProps) => {
|
||||
);
|
||||
}, [controlNetId, dispatch]);
|
||||
|
||||
const handleToggleIsEnabled = useCallback(() => {
|
||||
dispatch(controlNetToggled({ controlNetId }));
|
||||
}, [controlNetId, dispatch]);
|
||||
const handleToggleIsEnabled = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch(
|
||||
controlNetIsEnabledChanged({
|
||||
controlNetId,
|
||||
isEnabled: e.target.checked,
|
||||
})
|
||||
);
|
||||
},
|
||||
[controlNetId, dispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
@ -106,8 +114,8 @@ const ControlNet = (props: ControlNetProps) => {
|
||||
sx={{
|
||||
w: 'full',
|
||||
minW: 0,
|
||||
opacity: isEnabled ? 1 : 0.5,
|
||||
pointerEvents: isEnabled ? 'auto' : 'none',
|
||||
// opacity: isEnabled ? 1 : 0.5,
|
||||
// pointerEvents: isEnabled ? 'auto' : 'none',
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: '0.1s',
|
||||
}}
|
||||
|
@ -13,6 +13,7 @@ import {
|
||||
import { setHeight, setWidth } from 'features/parameters/store/generationSlice';
|
||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||
import { memo, useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaRulerVertical, FaSave, FaUndo } from 'react-icons/fa';
|
||||
import {
|
||||
useAddImageToBoardMutation,
|
||||
@ -26,7 +27,6 @@ import {
|
||||
ControlNetConfig,
|
||||
controlNetImageChanged,
|
||||
} from '../store/controlNetSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
type Props = {
|
||||
controlNet: ControlNetConfig;
|
||||
@ -52,7 +52,6 @@ const ControlNetImagePreview = ({ isSmall, controlNet }: Props) => {
|
||||
controlImage: controlImageName,
|
||||
processedControlImage: processedControlImageName,
|
||||
processorType,
|
||||
isEnabled,
|
||||
controlNetId,
|
||||
} = controlNet;
|
||||
|
||||
@ -172,15 +171,13 @@ const ControlNetImagePreview = ({ isSmall, controlNet }: Props) => {
|
||||
h: isSmall ? 28 : 366, // magic no touch
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
pointerEvents: isEnabled ? 'auto' : 'none',
|
||||
opacity: isEnabled ? 1 : 0.5,
|
||||
}}
|
||||
>
|
||||
<IAIDndImage
|
||||
draggableData={draggableData}
|
||||
droppableData={droppableData}
|
||||
imageDTO={controlImage}
|
||||
isDropDisabled={shouldShowProcessedImage || !isEnabled}
|
||||
isDropDisabled={shouldShowProcessedImage}
|
||||
postUploadAction={postUploadAction}
|
||||
/>
|
||||
|
||||
@ -202,7 +199,6 @@ const ControlNetImagePreview = ({ isSmall, controlNet }: Props) => {
|
||||
droppableData={droppableData}
|
||||
imageDTO={processedControlImage}
|
||||
isUploadDisabled={true}
|
||||
isDropDisabled={!isEnabled}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
|
@ -3,8 +3,8 @@ 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 { isIPAdapterEnabledChanged } from 'features/controlNet/store/controlNetSlice';
|
||||
import { ChangeEvent, memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const selector = createSelector(
|
||||
@ -22,9 +22,12 @@ const ParamIPAdapterFeatureToggle = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleChange = useCallback(() => {
|
||||
dispatch(isIPAdapterEnableToggled());
|
||||
}, [dispatch]);
|
||||
const handleChange = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch(isIPAdapterEnabledChanged(e.target.checked));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<IAISwitch
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { skipToken } from '@reduxjs/toolkit/dist/query';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import IAIDndImage from 'common/components/IAIDndImage';
|
||||
import IAIDndImageIcon from 'common/components/IAIDndImageIcon';
|
||||
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
||||
@ -16,15 +18,17 @@ import { FaUndo } from 'react-icons/fa';
|
||||
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
|
||||
import { PostUploadAction } from 'services/api/types';
|
||||
|
||||
const selector = createSelector(
|
||||
stateSelector,
|
||||
({ controlNet }) => {
|
||||
const { ipAdapterInfo } = controlNet;
|
||||
return { ipAdapterInfo };
|
||||
},
|
||||
defaultSelectorOptions
|
||||
);
|
||||
|
||||
const ParamIPAdapterImage = () => {
|
||||
const ipAdapterInfo = useAppSelector(
|
||||
(state: RootState) => state.controlNet.ipAdapterInfo
|
||||
);
|
||||
|
||||
const isIPAdapterEnabled = useAppSelector(
|
||||
(state: RootState) => state.controlNet.isIPAdapterEnabled
|
||||
);
|
||||
|
||||
const { ipAdapterInfo } = useAppSelector(selector);
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
@ -71,8 +75,6 @@ const ParamIPAdapterImage = () => {
|
||||
droppableData={droppableData}
|
||||
draggableData={draggableData}
|
||||
postUploadAction={postUploadAction}
|
||||
isUploadDisabled={!isIPAdapterEnabled}
|
||||
isDropDisabled={!isIPAdapterEnabled}
|
||||
dropLabel={t('toast.setIPAdapterImage')}
|
||||
noContentFallback={
|
||||
<IAINoContentFallback
|
||||
|
@ -11,6 +11,9 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useGetIPAdapterModelsQuery } from 'services/api/endpoints/models';
|
||||
|
||||
const ParamIPAdapterModelSelect = () => {
|
||||
const isEnabled = useAppSelector(
|
||||
(state: RootState) => state.controlNet.isIPAdapterEnabled
|
||||
);
|
||||
const ipAdapterModel = useAppSelector(
|
||||
(state: RootState) => state.controlNet.ipAdapterInfo.model
|
||||
);
|
||||
@ -90,6 +93,7 @@ const ParamIPAdapterModelSelect = () => {
|
||||
data={data}
|
||||
onChange={handleValueChanged}
|
||||
sx={{ width: '100%' }}
|
||||
disabled={!isEnabled}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -146,16 +146,16 @@ export const controlNetSlice = createSlice({
|
||||
const { controlNetId } = action.payload;
|
||||
delete state.controlNets[controlNetId];
|
||||
},
|
||||
controlNetToggled: (
|
||||
controlNetIsEnabledChanged: (
|
||||
state,
|
||||
action: PayloadAction<{ controlNetId: string }>
|
||||
action: PayloadAction<{ controlNetId: string; isEnabled: boolean }>
|
||||
) => {
|
||||
const { controlNetId } = action.payload;
|
||||
const { controlNetId, isEnabled } = action.payload;
|
||||
const cn = state.controlNets[controlNetId];
|
||||
if (!cn) {
|
||||
return;
|
||||
}
|
||||
cn.isEnabled = !cn.isEnabled;
|
||||
cn.isEnabled = isEnabled;
|
||||
},
|
||||
controlNetImageChanged: (
|
||||
state,
|
||||
@ -377,8 +377,8 @@ export const controlNetSlice = createSlice({
|
||||
controlNetReset: () => {
|
||||
return { ...initialControlNetState };
|
||||
},
|
||||
isIPAdapterEnableToggled: (state) => {
|
||||
state.isIPAdapterEnabled = !state.isIPAdapterEnabled;
|
||||
isIPAdapterEnabledChanged: (state, action: PayloadAction<boolean>) => {
|
||||
state.isIPAdapterEnabled = action.payload;
|
||||
},
|
||||
ipAdapterImageChanged: (state, action: PayloadAction<ImageDTO | null>) => {
|
||||
state.ipAdapterInfo.adapterImage = action.payload;
|
||||
@ -450,7 +450,7 @@ export const {
|
||||
controlNetRemoved,
|
||||
controlNetImageChanged,
|
||||
controlNetProcessedImageChanged,
|
||||
controlNetToggled,
|
||||
controlNetIsEnabledChanged,
|
||||
controlNetModelChanged,
|
||||
controlNetWeightChanged,
|
||||
controlNetBeginStepPctChanged,
|
||||
@ -461,7 +461,7 @@ export const {
|
||||
controlNetProcessorTypeChanged,
|
||||
controlNetReset,
|
||||
controlNetAutoConfigToggled,
|
||||
isIPAdapterEnableToggled,
|
||||
isIPAdapterEnabledChanged,
|
||||
ipAdapterImageChanged,
|
||||
ipAdapterWeightChanged,
|
||||
ipAdapterModelChanged,
|
||||
|
@ -23,17 +23,18 @@ import { v4 as uuidv4 } from 'uuid';
|
||||
const selector = createSelector(
|
||||
[stateSelector],
|
||||
({ controlNet }) => {
|
||||
const { controlNets, isEnabled, isIPAdapterEnabled } = controlNet;
|
||||
const { controlNets, isEnabled, isIPAdapterEnabled, ipAdapterInfo } =
|
||||
controlNet;
|
||||
|
||||
const validControlNets = getValidControlNets(controlNets);
|
||||
|
||||
const isIPAdapterValid = ipAdapterInfo.model && ipAdapterInfo.adapterImage;
|
||||
let activeLabel = undefined;
|
||||
|
||||
if (isEnabled && validControlNets.length > 0) {
|
||||
activeLabel = `${validControlNets.length} ControlNet`;
|
||||
}
|
||||
|
||||
if (isIPAdapterEnabled) {
|
||||
if (isIPAdapterEnabled && isIPAdapterValid) {
|
||||
if (activeLabel) {
|
||||
activeLabel = `${activeLabel}, IP Adapter`;
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user