diff --git a/invokeai/frontend/web/src/common/components/IAIDndImage.tsx b/invokeai/frontend/web/src/common/components/IAIDndImage.tsx
index a6667e73be..1451f82677 100644
--- a/invokeai/frontend/web/src/common/components/IAIDndImage.tsx
+++ b/invokeai/frontend/web/src/common/components/IAIDndImage.tsx
@@ -48,7 +48,7 @@ const IAIDndImage = (props: IAIDndImageProps) => {
isDragDisabled = false,
fallback = ,
payloadImage,
- minSize = 36,
+ minSize = 24,
} = props;
const dndId = useRef(uuidv4());
const { getUrl } = useGetUrl();
diff --git a/invokeai/frontend/web/src/common/components/IAISimpleCheckbox.tsx b/invokeai/frontend/web/src/common/components/IAISimpleCheckbox.tsx
index 4d21d3d3d0..2d28b5b72e 100644
--- a/invokeai/frontend/web/src/common/components/IAISimpleCheckbox.tsx
+++ b/invokeai/frontend/web/src/common/components/IAISimpleCheckbox.tsx
@@ -1,8 +1,8 @@
import { Checkbox, CheckboxProps, Text } from '@chakra-ui/react';
-import { memo, ReactNode } from 'react';
+import { memo, ReactElement } from 'react';
type IAISimpleCheckboxProps = CheckboxProps & {
- label: string | ReactNode;
+ label: string | ReactElement;
};
const IAISimpleCheckbox = (props: IAISimpleCheckboxProps) => {
diff --git a/invokeai/frontend/web/src/features/controlNet/components/ControlNet.tsx b/invokeai/frontend/web/src/features/controlNet/components/ControlNet.tsx
index 803444d96b..2b86ca0e4d 100644
--- a/invokeai/frontend/web/src/features/controlNet/components/ControlNet.tsx
+++ b/invokeai/frontend/web/src/features/controlNet/components/ControlNet.tsx
@@ -58,11 +58,7 @@ const ControlNet = (props: ControlNetProps) => {
return (
-
+
{
- const { controlNetId, controlImage, processedControlImage } = props;
+ const {
+ controlNetId,
+ controlImage,
+ processedControlImage,
+ isControlImageProcessed,
+ } = props.controlNet;
const dispatch = useAppDispatch();
const { isProcessingControlImage } = useAppSelector(selector);
+ const containerRef = useRef(null);
- const [shouldShowProcessedImage, setShouldShowProcessedImage] =
- useState(true);
+ const isMouseOverImage = useHoverDirty(containerRef);
const handleControlImageChanged = useCallback(
(controlImage: ImageDTO) => {
@@ -46,12 +51,15 @@ const ControlNetImagePreview = (props: Props) => {
Number(controlImage?.width) > Number(processedControlImage?.width) ||
Number(controlImage?.height) > Number(processedControlImage?.height);
+ const shouldShowProcessedImage =
+ controlImage &&
+ processedControlImage &&
+ !isMouseOverImage &&
+ !isProcessingControlImage &&
+ !isControlImageProcessed;
+
return (
- setShouldShowProcessedImage(false)}
- onMouseOut={() => setShouldShowProcessedImage(true)}
- >
+
{
processorNode,
} = props.controlNet;
const dispatch = useAppDispatch();
- const handleReset = useCallback(() => {
- dispatch(
- controlNetProcessedImageChanged({
- controlNetId,
- processedControlImage: null,
- })
- );
+
+ const handleDelete = useCallback(() => {
+ dispatch(controlNetRemoved(controlNetId));
}, [controlNetId, dispatch]);
- const handleControlNetRemoved = useCallback(() => {
- dispatch(controlNetRemoved(controlNetId));
+ const handleDuplicate = useCallback(() => {
+ dispatch(
+ controlNetAdded({ controlNetId: uuidv4(), controlNet: props.controlNet })
+ );
+ }, [dispatch, props.controlNet]);
+
+ const handleToggleIsEnabled = useCallback(() => {
+ dispatch(controlNetToggled(controlNetId));
+ }, [controlNetId, dispatch]);
+
+ const handleToggleIsPreprocessed = useCallback(() => {
+ dispatch(isControlNetImageProcessedToggled(controlNetId));
}, [controlNetId, dispatch]);
return (
+
+
+ }
+ />
+ }
+ />
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+ Enabled
+
+
+
+
+
+ Preprocessed
+
+
+
+
);
diff --git a/invokeai/frontend/web/src/features/controlNet/components/parameters/ParamControlNetBeginEnd.tsx b/invokeai/frontend/web/src/features/controlNet/components/parameters/ParamControlNetBeginEnd.tsx
index e258d4cf29..bb2f151193 100644
--- a/invokeai/frontend/web/src/features/controlNet/components/parameters/ParamControlNetBeginEnd.tsx
+++ b/invokeai/frontend/web/src/features/controlNet/components/parameters/ParamControlNetBeginEnd.tsx
@@ -50,7 +50,7 @@ const ParamControlNetBeginEnd = (props: Props) => {
return (
- Begin & End Step %
+ Begin / End Step Percentage
{
{!mini && (
<>
- {' '}
+ action: PayloadAction<{ controlNetId: string; controlNet?: ControlNet }>
) => {
- const { controlNetId } = action.payload;
+ const { controlNetId, controlNet } = action.payload;
state.controlNets[controlNetId] = {
- ...initialControlNet,
+ ...(controlNet ?? initialControlNet),
controlNetId,
};
},
@@ -116,11 +116,9 @@ export const controlNetSlice = createSlice({
},
isControlNetImageProcessedToggled: (
state,
- action: PayloadAction<{
- controlNetId: string;
- }>
+ action: PayloadAction
) => {
- const { controlNetId } = action.payload;
+ const controlNetId = action.payload;
state.controlNets[controlNetId].isControlImageProcessed =
!state.controlNets[controlNetId].isControlImageProcessed;
},
diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildTextToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildTextToImageGraph.ts
index 9975d446a3..161f857bd7 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildTextToImageGraph.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildTextToImageGraph.ts
@@ -346,6 +346,11 @@ export const buildTextToImageGraph = (state: RootState): Graph => {
weight,
} = controlNet;
+ if (!isEnabled) {
+ // Skip disabled ControlNets
+ return;
+ }
+
const controlNetNode: ControlNetInvocation = {
id: `control_net_${controlNetId}`,
type: 'controlnet',
@@ -355,14 +360,14 @@ export const buildTextToImageGraph = (state: RootState): Graph => {
control_weight: weight,
};
- if (processedControlImage) {
+ if (processedControlImage && !isControlImageProcessed) {
// We've already processed the image in the app, so we can just use the processed image
const { image_name, image_origin } = processedControlImage;
controlNetNode.image = {
image_name,
image_origin,
};
- } else if (controlImage) {
+ } else if (controlImage && isControlImageProcessed) {
// The control image is preprocessed
const { image_name, image_origin } = controlImage;
controlNetNode.image = {
@@ -370,9 +375,10 @@ export const buildTextToImageGraph = (state: RootState): Graph => {
image_origin,
};
} else {
- // The control image is not processed, so we need to add a preprocess node
- // TODO: Add preprocess node
+ // Skip ControlNets without an unprocessed image - should never happen if everything is working correctly
+ return;
}
+
graph.nodes[controlNetNode.id] = controlNetNode;
if (size(controlNets) > 1) {
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 2359e5123c..6369ab56de 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
@@ -1,6 +1,6 @@
import {
+ Divider,
Flex,
- Spacer,
Tab,
TabList,
TabPanel,
@@ -9,7 +9,7 @@ import {
} from '@chakra-ui/react';
import { useTranslation } from 'react-i18next';
import IAICollapse from 'common/components/IAICollapse';
-import { memo, useCallback } from 'react';
+import { Fragment, memo, useCallback } from 'react';
import IAIIconButton from 'common/components/IAIIconButton';
import { FaPlus } from 'react-icons/fa';
import ControlNet from 'features/controlNet/components/ControlNet';
@@ -21,11 +21,11 @@ import {
isControlNetEnabledToggled,
} from 'features/controlNet/store/controlNetSlice';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
-import { map, startCase } from 'lodash-es';
+import { map } from 'lodash-es';
import { v4 as uuidv4 } from 'uuid';
-import { CloseIcon } from '@chakra-ui/icons';
import ControlNetMini from 'features/controlNet/components/ControlNetMini';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
+import IAIButton from 'common/components/IAIButton';
const selector = createSelector(
controlNetSelector,
@@ -56,11 +56,26 @@ const ParamControlNetCollapse = () => {
}
return (
- <>
- {controlNetsArray.map((c) => (
-
- ))}
- >
+
+ {controlNetsArray.length === 0 && (
+
+ Add ControlNet
+
+ )}
+
+ {controlNetsArray.map((c, i) => (
+
+ {i > 0 && }
+
+
+ ))}
+
+
);
return (