feat(ui): add mini/advanced controlnet ui

This commit is contained in:
psychedelicious
2023-06-03 15:05:49 +10:00
parent 69f0ba65f1
commit d6c08ba469
18 changed files with 430 additions and 548 deletions

View File

@@ -1,32 +1,42 @@
import { memo, useCallback } from 'react';
import {
ControlNet,
controlNetProcessedImageChanged,
ControlNetConfig,
controlNetAdded,
controlNetRemoved,
controlNetToggled,
isControlNetImagePreprocessedToggled,
} from '../store/controlNetSlice';
import { useAppDispatch } from 'app/store/storeHooks';
import ParamControlNetModel from './parameters/ParamControlNetModel';
import ParamControlNetWeight from './parameters/ParamControlNetWeight';
import {
Box,
Checkbox,
Flex,
Tab,
FormControl,
FormLabel,
HStack,
TabList,
TabPanel,
TabPanels,
Tabs,
Text,
Tab,
TabPanel,
Box,
} from '@chakra-ui/react';
import IAIButton from 'common/components/IAIButton';
import { FaUndo } from 'react-icons/fa';
import { FaCopy, FaTrash } from 'react-icons/fa';
import ParamControlNetBeginEnd from './parameters/ParamControlNetBeginEnd';
import ControlNetImagePreview from './ControlNetImagePreview';
import IAIIconButton from 'common/components/IAIIconButton';
import { v4 as uuidv4 } from 'uuid';
import { useToggle } from 'react-use';
import ParamControlNetProcessorSelect from './parameters/ParamControlNetProcessorSelect';
import ControlNetProcessorComponent from './ControlNetProcessorComponent';
import ControlNetPreprocessButton from './ControlNetPreprocessButton';
import ParamControlNetBeginEnd from './parameters/ParamControlNetBeginEnd';
import ControlNetImagePreview from './ControlNetImagePreview';
import IAIButton from 'common/components/IAIButton';
import IAISwitch from 'common/components/IAISwitch';
type ControlNetProps = {
controlNet: ControlNet;
controlNet: ControlNetConfig;
};
const ControlNet = (props: ControlNetProps) => {
@@ -38,24 +48,160 @@ const ControlNet = (props: ControlNetProps) => {
beginStepPct,
endStepPct,
controlImage,
isControlImageProcessed,
isPreprocessed: isControlImageProcessed,
processedControlImage,
processorNode,
} = props.controlNet;
const dispatch = useAppDispatch();
const handleReset = useCallback(() => {
dispatch(
controlNetProcessedImageChanged({
controlNetId,
processedControlImage: null,
})
);
const [shouldShowAdvanced, onToggleAdvanced] = useToggle(true);
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(isControlNetImagePreprocessedToggled({ controlNetId }));
}, [controlNetId, dispatch]);
return (
<Flex
sx={{
flexDir: 'column',
gap: 2,
p: 2,
bg: 'base.850',
borderRadius: 'base',
}}
>
<HStack>
<IAISwitch
aria-label="Toggle ControlNet"
isChecked={isEnabled}
onChange={handleToggleIsEnabled}
/>
<Box
w="full"
opacity={isEnabled ? 1 : 0.5}
pointerEvents={isEnabled ? 'auto' : 'none'}
transitionProperty="common"
transitionDuration="0.1s"
>
<ParamControlNetModel controlNetId={controlNetId} model={model} />
</Box>
<IAIIconButton
size="sm"
tooltip="Duplicate ControlNet"
aria-label="Duplicate ControlNet"
onClick={handleDuplicate}
icon={<FaCopy />}
/>
<IAIIconButton
size="sm"
tooltip="Delete ControlNet"
aria-label="Delete ControlNet"
colorScheme="error"
onClick={handleDelete}
icon={<FaTrash />}
/>
</HStack>
{isEnabled && (
<>
<Flex sx={{ gap: 4 }}>
{!shouldShowAdvanced && (
<Flex
sx={{
alignItems: 'center',
justifyContent: 'center',
h: 32,
w: 32,
aspectRatio: '1/1',
}}
>
<ControlNetImagePreview controlNet={props.controlNet} />
</Flex>
)}
<Flex
sx={{
flexDir: 'column',
gap: 2,
w: 'full',
paddingInlineEnd: 2,
pb: shouldShowAdvanced ? 0 : 2,
justifyContent: 'space-between',
}}
>
<Flex
sx={{
justifyContent: 'space-between',
w: 'full',
}}
>
<FormControl>
<HStack>
<Checkbox
isChecked={isControlImageProcessed}
onChange={handleToggleIsPreprocessed}
/>
<FormLabel>Preprocessed</FormLabel>
</HStack>
</FormControl>
<FormControl>
<HStack>
<Checkbox
isChecked={shouldShowAdvanced}
onChange={onToggleAdvanced}
/>
<FormLabel>Advanced</FormLabel>
</HStack>
</FormControl>
</Flex>
<ParamControlNetWeight
controlNetId={controlNetId}
weight={weight}
mini
/>
<ParamControlNetBeginEnd
controlNetId={controlNetId}
beginStepPct={beginStepPct}
endStepPct={endStepPct}
mini
/>
</Flex>
</Flex>
{shouldShowAdvanced && (
<>
<Box pt={2}>
<ControlNetImagePreview controlNet={props.controlNet} />
</Box>
{!isControlImageProcessed && (
<>
<ParamControlNetProcessorSelect
controlNetId={controlNetId}
processorNode={processorNode}
/>
<ControlNetProcessorComponent
controlNetId={controlNetId}
processorNode={processorNode}
/>
</>
)}
</>
)}
</>
)}
</Flex>
);
return (
<Flex sx={{ flexDir: 'column', gap: 3 }}>
<ControlNetImagePreview controlNet={props.controlNet} />
@@ -101,18 +247,18 @@ const ControlNet = (props: ControlNetProps) => {
processorNode={processorNode}
/>
<ControlNetPreprocessButton controlNet={props.controlNet} />
<IAIButton
{/* <IAIButton
size="sm"
leftIcon={<FaUndo />}
onClick={handleReset}
isDisabled={Boolean(!processedControlImage)}
>
Reset Processing
</IAIButton>
</IAIButton> */}
</TabPanel>
</TabPanels>
</Tabs>
<IAIButton onClick={handleControlNetRemoved}>Remove ControlNet</IAIButton>
<IAIButton onClick={handleDelete}>Remove ControlNet</IAIButton>
</Flex>
);
};

View File

@@ -1,7 +1,7 @@
import { memo, useCallback, useRef, useState } from 'react';
import { ImageDTO } from 'services/api';
import {
ControlNet,
ControlNetConfig,
controlNetImageChanged,
controlNetSelector,
} from '../store/controlNetSlice';
@@ -24,7 +24,7 @@ const selector = createSelector(
);
type Props = {
controlNet: ControlNet;
controlNet: ControlNetConfig;
};
const ControlNetImagePreview = (props: Props) => {
@@ -32,7 +32,7 @@ const ControlNetImagePreview = (props: Props) => {
controlNetId,
controlImage,
processedControlImage,
isControlImageProcessed,
isPreprocessed: isControlImageProcessed,
} = props.controlNet;
const dispatch = useAppDispatch();
const { isProcessingControlImage } = useAppSelector(selector);
@@ -63,63 +63,62 @@ const ControlNetImagePreview = (props: Props) => {
<IAIDndImage
image={controlImage}
onDrop={handleControlImageChanged}
isDropDisabled={Boolean(processedControlImage)}
isDropDisabled={Boolean(
processedControlImage && !isControlImageProcessed
)}
/>
<AnimatePresence>
{controlImage &&
processedControlImage &&
shouldShowProcessedImage &&
!isProcessingControlImage && (
<motion.div
initial={{
opacity: 0,
}}
animate={{
opacity: 1,
transition: { duration: 0.1 },
}}
exit={{
opacity: 0,
transition: { duration: 0.1 },
{shouldShowProcessedImage && (
<motion.div
initial={{
opacity: 0,
}}
animate={{
opacity: 1,
transition: { duration: 0.1 },
}}
exit={{
opacity: 0,
transition: { duration: 0.1 },
}}
>
<Box
sx={{
position: 'absolute',
w: 'full',
h: 'full',
top: 0,
insetInlineStart: 0,
}}
>
{shouldShowProcessedImageBackdrop && (
<Box
sx={{
w: 'full',
h: 'full',
bg: 'base.900',
opacity: 0.7,
}}
/>
)}
<Box
sx={{
position: 'absolute',
w: 'full',
h: 'full',
top: 0,
insetInlineStart: 0,
w: 'full',
h: 'full',
}}
>
{shouldShowProcessedImageBackdrop && (
<Box
sx={{
w: 'full',
h: 'full',
bg: 'base.900',
opacity: 0.7,
}}
/>
)}
<Box
sx={{
position: 'absolute',
top: 0,
insetInlineStart: 0,
w: 'full',
h: 'full',
}}
>
<IAIDndImage
image={processedControlImage}
onDrop={handleControlImageChanged}
payloadImage={controlImage}
/>
</Box>
<IAIDndImage
image={processedControlImage}
onDrop={handleControlImageChanged}
payloadImage={controlImage}
/>
</Box>
</motion.div>
)}
</Box>
</motion.div>
)}
</AnimatePresence>
{isProcessingControlImage && (
<Box

View File

@@ -1,153 +0,0 @@
import { memo, useCallback } from 'react';
import {
ControlNet,
controlNetAdded,
controlNetRemoved,
controlNetToggled,
isControlNetImageProcessedToggled,
} from '../store/controlNetSlice';
import { useAppDispatch } from 'app/store/storeHooks';
import ParamControlNetModel from './parameters/ParamControlNetModel';
import ParamControlNetWeight from './parameters/ParamControlNetWeight';
import {
Checkbox,
Flex,
FormControl,
FormLabel,
HStack,
} from '@chakra-ui/react';
import { FaCopy, FaTrash } from 'react-icons/fa';
import ParamControlNetBeginEnd from './parameters/ParamControlNetBeginEnd';
import ControlNetImagePreview from './ControlNetImagePreview';
import IAIIconButton from 'common/components/IAIIconButton';
import { v4 as uuidv4 } from 'uuid';
type ControlNetProps = {
controlNet: ControlNet;
};
const ControlNet = (props: ControlNetProps) => {
const {
controlNetId,
isEnabled,
model,
weight,
beginStepPct,
endStepPct,
controlImage,
isControlImageProcessed,
processedControlImage,
processorNode,
} = props.controlNet;
const dispatch = useAppDispatch();
const handleDelete = useCallback(() => {
dispatch(controlNetRemoved(controlNetId));
}, [controlNetId, dispatch]);
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 (
<Flex
sx={{
flexDir: 'column',
gap: 2,
}}
>
<HStack>
<ParamControlNetModel controlNetId={controlNetId} model={model} />
<IAIIconButton
size="sm"
tooltip="Duplicate ControlNet"
aria-label="Duplicate ControlNet"
onClick={handleDuplicate}
icon={<FaCopy />}
/>
<IAIIconButton
size="sm"
tooltip="Delete ControlNet"
aria-label="Delete ControlNet"
colorScheme="error"
onClick={handleDelete}
icon={<FaTrash />}
/>
</HStack>
<Flex
sx={{
gap: 4,
paddingInlineEnd: 2,
}}
>
<Flex
sx={{
alignItems: 'center',
justifyContent: 'center',
h: 32,
w: 32,
aspectRatio: '1/1',
}}
>
<ControlNetImagePreview controlNet={props.controlNet} />
</Flex>
<Flex
sx={{
flexDir: 'column',
gap: 2,
w: 'full',
justifyContent: 'space-between',
}}
>
<ParamControlNetWeight
controlNetId={controlNetId}
weight={weight}
mini
/>
<ParamControlNetBeginEnd
controlNetId={controlNetId}
beginStepPct={beginStepPct}
endStepPct={endStepPct}
mini
/>
<Flex
sx={{
justifyContent: 'space-between',
}}
>
<FormControl>
<HStack>
<Checkbox
isChecked={isEnabled}
onChange={handleToggleIsEnabled}
/>
<FormLabel>Enabled</FormLabel>
</HStack>
</FormControl>
<FormControl>
<HStack>
<Checkbox
isChecked={isControlImageProcessed}
onChange={handleToggleIsPreprocessed}
/>
<FormLabel>Preprocessed</FormLabel>
</HStack>
</FormControl>
</Flex>
</Flex>
</Flex>
</Flex>
);
};
export default memo(ControlNet);

View File

@@ -1,12 +1,12 @@
import IAIButton from 'common/components/IAIButton';
import { memo, useCallback } from 'react';
import { ControlNet } from '../store/controlNetSlice';
import { ControlNetConfig } from '../store/controlNetSlice';
import { useAppDispatch } from 'app/store/storeHooks';
import { controlNetImageProcessed } from '../store/actions';
import { useIsReadyToInvoke } from 'common/hooks/useIsReadyToInvoke';
type Props = {
controlNet: ControlNet;
controlNet: ControlNetConfig;
};
const ControlNetPreprocessButton = (props: Props) => {

View File

@@ -1,58 +0,0 @@
import { useAppDispatch } from 'app/store/storeHooks';
import IAISlider from 'common/components/IAISlider';
import {
controlNetBeginStepPctChanged,
controlNetEndStepPctChanged,
} from 'features/controlNet/store/controlNetSlice';
import { memo, useCallback } from 'react';
type ParamControlNetBeginStepPctProps = {
controlNetId: string;
beginStepPct: number;
};
const ParamControlNetBeginStepPct = (
props: ParamControlNetBeginStepPctProps
) => {
const { controlNetId, beginStepPct } = props;
const dispatch = useAppDispatch();
const handleBeginStepPctChanged = useCallback(
(beginStepPct: number) => {
dispatch(controlNetBeginStepPctChanged({ controlNetId, beginStepPct }));
},
[controlNetId, dispatch]
);
const handleBeginStepPctReset = useCallback(() => {
dispatch(controlNetBeginStepPctChanged({ controlNetId, beginStepPct: 0 }));
}, [controlNetId, dispatch]);
const handleEndStepPctChanged = useCallback(
(endStepPct: number) => {
dispatch(controlNetEndStepPctChanged({ controlNetId, endStepPct }));
},
[controlNetId, dispatch]
);
const handleEndStepPctReset = useCallback(() => {
dispatch(controlNetEndStepPctChanged({ controlNetId, endStepPct: 0 }));
}, [controlNetId, dispatch]);
return (
<IAISlider
label="Begin Step %"
value={beginStepPct}
onChange={handleBeginStepPctChanged}
withInput
withReset
handleReset={handleBeginStepPctReset}
withSliderMarks
min={0}
max={1}
step={0.01}
/>
);
};
export default memo(ParamControlNetBeginStepPct);

View File

@@ -1,42 +0,0 @@
import { useAppDispatch } from 'app/store/storeHooks';
import IAISlider from 'common/components/IAISlider';
import { controlNetEndStepPctChanged } from 'features/controlNet/store/controlNetSlice';
import { memo, useCallback } from 'react';
type ParamControlNetEndStepPctProps = {
controlNetId: string;
endStepPct: number;
};
const ParamControlNetEndStepPct = (props: ParamControlNetEndStepPctProps) => {
const { controlNetId, endStepPct } = props;
const dispatch = useAppDispatch();
const handleEndStepPctChanged = useCallback(
(endStepPct: number) => {
dispatch(controlNetEndStepPctChanged({ controlNetId, endStepPct }));
},
[controlNetId, dispatch]
);
const handleEndStepPctReset = () => {
dispatch(controlNetEndStepPctChanged({ controlNetId, endStepPct: 0 }));
};
return (
<IAISlider
label="End Step %"
value={endStepPct}
onChange={handleEndStepPctChanged}
withInput
withReset
handleReset={handleEndStepPctReset}
withSliderMarks
min={0}
max={1}
step={0.01}
/>
);
};
export default memo(ParamControlNetEndStepPct);

View File

@@ -13,7 +13,7 @@ const ParamControlNetIsEnabled = (props: ParamControlNetIsEnabledProps) => {
const dispatch = useAppDispatch();
const handleIsEnabledChanged = useCallback(() => {
dispatch(controlNetToggled(controlNetId));
dispatch(controlNetToggled({ controlNetId }));
}, [dispatch, controlNetId]);
return (

View File

@@ -3,7 +3,7 @@ import IAIFullCheckbox from 'common/components/IAIFullCheckbox';
import IAISwitch from 'common/components/IAISwitch';
import {
controlNetToggled,
isControlNetImageProcessedToggled,
isControlNetImagePreprocessedToggled,
} from 'features/controlNet/store/controlNetSlice';
import { memo, useCallback } from 'react';
@@ -18,7 +18,7 @@ const ParamControlNetIsEnabled = (props: ParamControlNetIsEnabledProps) => {
const handleIsControlImageProcessedToggled = useCallback(() => {
dispatch(
isControlNetImageProcessedToggled({
isControlNetImagePreprocessedToggled({
controlNetId,
})
);

View File

@@ -3,8 +3,8 @@ import IAICustomSelect from 'common/components/IAICustomSelect';
import {
CONTROLNET_MODELS,
ControlNetModel,
controlNetModelChanged,
} from 'features/controlNet/store/controlNetSlice';
} from 'features/controlNet/store/constants';
import { controlNetModelChanged } from 'features/controlNet/store/controlNetSlice';
import { memo, useCallback } from 'react';
type ParamIsControlNetModelProps = {

View File

@@ -22,7 +22,7 @@ type ControlNetProcessorsDict = Record<
*
* TODO: Generate from the OpenAPI schema
*/
export const CONTROLNET_PROCESSORS = {
export const CONTROLNET_PROCESSORS: ControlNetProcessorsDict = {
canny_image_processor: {
type: 'canny_image_processor',
label: 'Canny',
@@ -164,3 +164,27 @@ export const CONTROLNET_PROCESSORS = {
},
},
};
export const CONTROLNET_MODELS = [
'lllyasviel/sd-controlnet-canny',
'lllyasviel/sd-controlnet-depth',
'lllyasviel/sd-controlnet-hed',
'lllyasviel/sd-controlnet-seg',
'lllyasviel/sd-controlnet-openpose',
'lllyasviel/sd-controlnet-scribble',
'lllyasviel/sd-controlnet-normal',
'lllyasviel/sd-controlnet-mlsd',
];
export type ControlNetModel = (typeof CONTROLNET_MODELS)[number];
export const CONTROLNET_MODEL_MAP: Record<
ControlNetModel,
ControlNetProcessorType
> = {
'lllyasviel/sd-controlnet-canny': 'canny_image_processor',
'lllyasviel/sd-controlnet-depth': 'midas_depth_image_processor',
'lllyasviel/sd-controlnet-hed': 'hed_image_processor',
'lllyasviel/sd-controlnet-openpose': 'openpose_image_processor',
'lllyasviel/sd-controlnet-mlsd': 'mlsd_image_processor',
};

View File

@@ -7,36 +7,27 @@ import {
RequiredCannyImageProcessorInvocation,
RequiredControlNetProcessorNode,
} from './types';
import { CONTROLNET_PROCESSORS } from './constants';
import {
CONTROLNET_MODELS,
CONTROLNET_PROCESSORS,
ControlNetModel,
} from './constants';
import { controlNetImageProcessed } from './actions';
export const CONTROLNET_MODELS = [
'lllyasviel/sd-controlnet-canny',
'lllyasviel/sd-controlnet-depth',
'lllyasviel/sd-controlnet-hed',
'lllyasviel/sd-controlnet-seg',
'lllyasviel/sd-controlnet-openpose',
'lllyasviel/sd-controlnet-scribble',
'lllyasviel/sd-controlnet-normal',
'lllyasviel/sd-controlnet-mlsd',
];
export type ControlNetModel = (typeof CONTROLNET_MODELS)[number];
export const initialControlNet: Omit<ControlNet, 'controlNetId'> = {
export const initialControlNet: Omit<ControlNetConfig, 'controlNetId'> = {
isEnabled: true,
model: CONTROLNET_MODELS[0],
weight: 1,
beginStepPct: 0,
endStepPct: 1,
controlImage: null,
isControlImageProcessed: false,
isPreprocessed: false,
processedControlImage: null,
processorNode: CONTROLNET_PROCESSORS.canny_image_processor
.default as RequiredCannyImageProcessorInvocation,
};
export type ControlNet = {
export type ControlNetConfig = {
controlNetId: string;
isEnabled: boolean;
model: ControlNetModel;
@@ -44,22 +35,20 @@ export type ControlNet = {
beginStepPct: number;
endStepPct: number;
controlImage: ImageDTO | null;
isControlImageProcessed: boolean;
isPreprocessed: boolean;
processedControlImage: ImageDTO | null;
processorNode: RequiredControlNetProcessorNode;
};
export type ControlNetState = {
controlNets: Record<string, ControlNet>;
controlNets: Record<string, ControlNetConfig>;
isEnabled: boolean;
shouldAutoProcess: boolean;
isProcessingControlImage: boolean;
};
export const initialControlNetState: ControlNetState = {
controlNets: {},
isEnabled: false,
shouldAutoProcess: true,
isProcessingControlImage: false,
};
@@ -72,7 +61,10 @@ export const controlNetSlice = createSlice({
},
controlNetAdded: (
state,
action: PayloadAction<{ controlNetId: string; controlNet?: ControlNet }>
action: PayloadAction<{
controlNetId: string;
controlNet?: ControlNetConfig;
}>
) => {
const { controlNetId, controlNet } = action.payload;
state.controlNets[controlNetId] = {
@@ -91,12 +83,18 @@ export const controlNetSlice = createSlice({
controlImage,
};
},
controlNetRemoved: (state, action: PayloadAction<string>) => {
const controlNetId = action.payload;
controlNetRemoved: (
state,
action: PayloadAction<{ controlNetId: string }>
) => {
const { controlNetId } = action.payload;
delete state.controlNets[controlNetId];
},
controlNetToggled: (state, action: PayloadAction<string>) => {
const controlNetId = action.payload;
controlNetToggled: (
state,
action: PayloadAction<{ controlNetId: string }>
) => {
const { controlNetId } = action.payload;
state.controlNets[controlNetId].isEnabled =
!state.controlNets[controlNetId].isEnabled;
},
@@ -110,17 +108,20 @@ export const controlNetSlice = createSlice({
const { controlNetId, controlImage } = action.payload;
state.controlNets[controlNetId].controlImage = controlImage;
state.controlNets[controlNetId].processedControlImage = null;
if (state.shouldAutoProcess && controlImage !== null) {
if (
controlImage !== null &&
!state.controlNets[controlNetId].isPreprocessed
) {
state.isProcessingControlImage = true;
}
},
isControlNetImageProcessedToggled: (
isControlNetImagePreprocessedToggled: (
state,
action: PayloadAction<string>
action: PayloadAction<{ controlNetId: string }>
) => {
const controlNetId = action.payload;
state.controlNets[controlNetId].isControlImageProcessed =
!state.controlNets[controlNetId].isControlImageProcessed;
const { controlNetId } = action.payload;
state.controlNets[controlNetId].isPreprocessed =
!state.controlNets[controlNetId].isPreprocessed;
},
controlNetProcessedImageChanged: (
state,
@@ -191,9 +192,6 @@ export const controlNetSlice = createSlice({
processorType
].default as RequiredControlNetProcessorNode;
},
shouldAutoProcessToggled: (state) => {
state.shouldAutoProcess = !state.shouldAutoProcess;
},
},
extraReducers: (builder) => {
builder.addCase(controlNetImageProcessed, (state, action) => {
@@ -212,7 +210,7 @@ export const {
controlNetAddedFromImage,
controlNetRemoved,
controlNetImageChanged,
isControlNetImageProcessedToggled,
isControlNetImagePreprocessedToggled,
controlNetProcessedImageChanged,
controlNetToggled,
controlNetModelChanged,
@@ -221,7 +219,6 @@ export const {
controlNetEndStepPctChanged,
controlNetProcessorParamsChanged,
controlNetProcessorTypeChanged,
shouldAutoProcessToggled,
} = controlNetSlice.actions;
export default controlNetSlice.reducer;