From 03f3ad435a691ee2370af94caabbfa09ba247e76 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Sat, 3 Jun 2023 22:48:16 +1000 Subject: [PATCH] feat(ui): updated controlnet logic/ui --- .../listeners/controlNetAutoProcess.ts | 16 ++- .../controlNet/components/ControlNet.tsx | 99 ++++++++----------- .../components/ControlNetImagePreview.tsx | 12 +-- .../parameters/ParamControlNetModel.tsx | 3 + .../processors/common/ProcessorWrapper.tsx | 4 +- .../features/controlNet/store/constants.ts | 8 ++ .../controlNet/store/controlNetSlice.ts | 16 +-- .../src/features/controlNet/store/types.ts | 2 +- .../nodes/util/addControlNetToLinearGraph.ts | 7 +- 9 files changed, 73 insertions(+), 94 deletions(-) diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlNetAutoProcess.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlNetAutoProcess.ts index d53907e673..9f98b8f25e 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlNetAutoProcess.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlNetAutoProcess.ts @@ -6,7 +6,6 @@ import { controlNetImageChanged, controlNetProcessorParamsChanged, controlNetProcessorTypeChanged, - isControlNetImagePreprocessedToggled, } from 'features/controlNet/store/controlNetSlice'; import { RootState } from 'app/store/store'; @@ -16,25 +15,22 @@ const predicate = (action: AnyAction, state: RootState) => { const isActionMatched = controlNetProcessorParamsChanged.match(action) || controlNetImageChanged.match(action) || - controlNetProcessorTypeChanged.match(action) || - isControlNetImagePreprocessedToggled.match(action); + controlNetProcessorTypeChanged.match(action); if (!isActionMatched) { return false; } - const { controlNetId } = action.payload; + const { controlImage, processorType } = + state.controlNet.controlNets[action.payload.controlNetId]; - const shouldAutoProcess = - !state.controlNet.controlNets[controlNetId].isPreprocessed; + const isProcessorSelected = processorType !== 'none'; const isBusy = state.system.isProcessing; - const hasControlImage = Boolean( - state.controlNet.controlNets[controlNetId].controlImage - ); + const hasControlImage = Boolean(controlImage); - return shouldAutoProcess && !isBusy && hasControlImage; + return isProcessorSelected && !isBusy && hasControlImage; }; /** diff --git a/invokeai/frontend/web/src/features/controlNet/components/ControlNet.tsx b/invokeai/frontend/web/src/features/controlNet/components/ControlNet.tsx index 2f6e1f676b..903d453446 100644 --- a/invokeai/frontend/web/src/features/controlNet/components/ControlNet.tsx +++ b/invokeai/frontend/web/src/features/controlNet/components/ControlNet.tsx @@ -4,7 +4,6 @@ import { controlNetAdded, controlNetRemoved, controlNetToggled, - isControlNetImagePreprocessedToggled, } from '../store/controlNetSlice'; import { useAppDispatch } from 'app/store/storeHooks'; import ParamControlNetModel from './parameters/ParamControlNetModel'; @@ -22,7 +21,7 @@ import { TabPanel, Box, } from '@chakra-ui/react'; -import { FaCopy, FaTrash } from 'react-icons/fa'; +import { FaCopy, FaPlus, FaTrash, FaWrench } from 'react-icons/fa'; import ParamControlNetBeginEnd from './parameters/ParamControlNetBeginEnd'; import ControlNetImagePreview from './ControlNetImagePreview'; @@ -34,6 +33,7 @@ import ControlNetProcessorComponent from './ControlNetProcessorComponent'; import ControlNetPreprocessButton from './ControlNetPreprocessButton'; import IAIButton from 'common/components/IAIButton'; import IAISwitch from 'common/components/IAISwitch'; +import { ChevronDownIcon, ChevronUpIcon } from '@chakra-ui/icons'; type ControlNetProps = { controlNet: ControlNetConfig; @@ -48,12 +48,12 @@ const ControlNet = (props: ControlNetProps) => { beginStepPct, endStepPct, controlImage, - isPreprocessed, processedControlImage, processorNode, + processorType, } = props.controlNet; const dispatch = useAppDispatch(); - const [shouldShowAdvanced, onToggleAdvanced] = useToggle(true); + const [shouldShowAdvanced, onToggleAdvanced] = useToggle(false); const handleDelete = useCallback(() => { dispatch(controlNetRemoved({ controlNetId })); @@ -69,23 +69,20 @@ const ControlNet = (props: ControlNetProps) => { dispatch(controlNetToggled({ controlNetId })); }, [controlNetId, dispatch]); - const handleToggleIsPreprocessed = useCallback(() => { - dispatch(isControlNetImagePreprocessedToggled({ controlNetId })); - }, [controlNetId, dispatch]); - return ( @@ -103,19 +100,38 @@ const ControlNet = (props: ControlNetProps) => { } /> } /> + + } + /> {isEnabled && ( <> @@ -125,38 +141,13 @@ const ControlNet = (props: ControlNetProps) => { flexDir: 'column', gap: 2, w: 'full', - h: 32, - paddingInlineStart: 2, - paddingInlineEnd: shouldShowAdvanced ? 2 : 0, + h: 24, + paddingInlineStart: 1, + paddingInlineEnd: shouldShowAdvanced ? 1 : 0, pb: 2, justifyContent: 'space-between', }} > - - - - - Preprocessed - - - - - - Advanced - - - { sx={{ alignItems: 'center', justifyContent: 'center', - h: 32, - w: 32, + h: 24, + w: 24, aspectRatio: '1/1', }} > @@ -188,18 +179,14 @@ const ControlNet = (props: ControlNetProps) => { - {!isPreprocessed && ( - <> - - - - )} + + )} diff --git a/invokeai/frontend/web/src/features/controlNet/components/ControlNetImagePreview.tsx b/invokeai/frontend/web/src/features/controlNet/components/ControlNetImagePreview.tsx index 099e58ce80..2183945e9b 100644 --- a/invokeai/frontend/web/src/features/controlNet/components/ControlNetImagePreview.tsx +++ b/invokeai/frontend/web/src/features/controlNet/components/ControlNetImagePreview.tsx @@ -28,12 +28,8 @@ type Props = { }; const ControlNetImagePreview = (props: Props) => { - const { - controlNetId, - controlImage, - processedControlImage, - isPreprocessed: isControlImageProcessed, - } = props.controlNet; + const { controlNetId, controlImage, processedControlImage, processorType } = + props.controlNet; const dispatch = useAppDispatch(); const { isProcessingControlImage } = useAppSelector(selector); const containerRef = useRef(null); @@ -56,7 +52,7 @@ const ControlNetImagePreview = (props: Props) => { processedControlImage && !isMouseOverImage && !isProcessingControlImage && - !isControlImageProcessed; + processorType !== 'none'; return ( @@ -64,7 +60,7 @@ const ControlNetImagePreview = (props: Props) => { image={controlImage} onDrop={handleControlImageChanged} isDropDisabled={Boolean( - processedControlImage && !isControlImageProcessed + processedControlImage && processorType !== 'none' )} /> diff --git a/invokeai/frontend/web/src/features/controlNet/components/parameters/ParamControlNetModel.tsx b/invokeai/frontend/web/src/features/controlNet/components/parameters/ParamControlNetModel.tsx index 7de32bb107..113b1148f4 100644 --- a/invokeai/frontend/web/src/features/controlNet/components/parameters/ParamControlNetModel.tsx +++ b/invokeai/frontend/web/src/features/controlNet/components/parameters/ParamControlNetModel.tsx @@ -27,9 +27,12 @@ const ParamIsControlNetModel = (props: ParamIsControlNetModelProps) => { return ( ); diff --git a/invokeai/frontend/web/src/features/controlNet/components/processors/common/ProcessorWrapper.tsx b/invokeai/frontend/web/src/features/controlNet/components/processors/common/ProcessorWrapper.tsx index f1c9cb8048..5dc0a909d5 100644 --- a/invokeai/frontend/web/src/features/controlNet/components/processors/common/ProcessorWrapper.tsx +++ b/invokeai/frontend/web/src/features/controlNet/components/processors/common/ProcessorWrapper.tsx @@ -4,7 +4,5 @@ import { PropsWithChildren } from 'react'; type Props = PropsWithChildren; export default function ProcessorWrapper(props: Props) { - return ( - {props.children} - ); + return {props.children}; } diff --git a/invokeai/frontend/web/src/features/controlNet/store/constants.ts b/invokeai/frontend/web/src/features/controlNet/store/constants.ts index b022d81b3a..c8689badf5 100644 --- a/invokeai/frontend/web/src/features/controlNet/store/constants.ts +++ b/invokeai/frontend/web/src/features/controlNet/store/constants.ts @@ -24,6 +24,14 @@ type ControlNetProcessorsDict = Record< * TODO: Generate from the OpenAPI schema */ export const CONTROLNET_PROCESSORS = { + none: { + type: 'none', + label: 'None', + description: '', + default: { + type: 'none', + }, + }, canny_image_processor: { type: 'canny_image_processor', label: 'Canny', diff --git a/invokeai/frontend/web/src/features/controlNet/store/controlNetSlice.ts b/invokeai/frontend/web/src/features/controlNet/store/controlNetSlice.ts index 4847e3c1a5..1389457aba 100644 --- a/invokeai/frontend/web/src/features/controlNet/store/controlNetSlice.ts +++ b/invokeai/frontend/web/src/features/controlNet/store/controlNetSlice.ts @@ -21,8 +21,8 @@ export const initialControlNet: Omit = { beginStepPct: 0, endStepPct: 1, controlImage: null, - isPreprocessed: false, processedControlImage: null, + processorType: 'canny_image_processor', processorNode: CONTROLNET_PROCESSORS.canny_image_processor .default as RequiredCannyImageProcessorInvocation, }; @@ -35,8 +35,8 @@ export type ControlNetConfig = { beginStepPct: number; endStepPct: number; controlImage: ImageDTO | null; - isPreprocessed: boolean; processedControlImage: ImageDTO | null; + processorType: ControlNetProcessorType; processorNode: RequiredControlNetProcessorNode; }; @@ -110,19 +110,11 @@ export const controlNetSlice = createSlice({ state.controlNets[controlNetId].processedControlImage = null; if ( controlImage !== null && - !state.controlNets[controlNetId].isPreprocessed + state.controlNets[controlNetId].processorType !== 'none' ) { state.isProcessingControlImage = true; } }, - isControlNetImagePreprocessedToggled: ( - state, - action: PayloadAction<{ controlNetId: string }> - ) => { - const { controlNetId } = action.payload; - state.controlNets[controlNetId].isPreprocessed = - !state.controlNets[controlNetId].isPreprocessed; - }, controlNetProcessedImageChanged: ( state, action: PayloadAction<{ @@ -188,6 +180,7 @@ export const controlNetSlice = createSlice({ }> ) => { const { controlNetId, processorType } = action.payload; + state.controlNets[controlNetId].processorType = processorType; state.controlNets[controlNetId].processorNode = CONTROLNET_PROCESSORS[ processorType ].default as RequiredControlNetProcessorNode; @@ -210,7 +203,6 @@ export const { controlNetAddedFromImage, controlNetRemoved, controlNetImageChanged, - isControlNetImagePreprocessedToggled, controlNetProcessedImageChanged, controlNetToggled, controlNetModelChanged, diff --git a/invokeai/frontend/web/src/features/controlNet/store/types.ts b/invokeai/frontend/web/src/features/controlNet/store/types.ts index 808a50010b..4ee15b39b9 100644 --- a/invokeai/frontend/web/src/features/controlNet/store/types.ts +++ b/invokeai/frontend/web/src/features/controlNet/store/types.ts @@ -36,7 +36,7 @@ export type ControlNetProcessorNode = * Any ControlNet processor type */ export type ControlNetProcessorType = NonNullable< - ControlNetProcessorNode['type'] + ControlNetProcessorNode['type'] | 'none' >; /** diff --git a/invokeai/frontend/web/src/features/nodes/util/addControlNetToLinearGraph.ts b/invokeai/frontend/web/src/features/nodes/util/addControlNetToLinearGraph.ts index b386b41dc7..9c77681d18 100644 --- a/invokeai/frontend/web/src/features/nodes/util/addControlNetToLinearGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/addControlNetToLinearGraph.ts @@ -33,13 +33,12 @@ export const addControlNetToLinearGraph = ( const { controlNetId, isEnabled, - isPreprocessed: isControlImageProcessed, controlImage, processedControlImage, beginStepPct, endStepPct, model, - processorNode, + processorType, weight, } = controlNet; @@ -57,14 +56,14 @@ export const addControlNetToLinearGraph = ( control_weight: weight, }; - if (processedControlImage && !isControlImageProcessed) { + if (processedControlImage && processorType !== 'none') { // 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 && isControlImageProcessed) { + } else if (controlImage && processorType !== 'none') { // The control image is preprocessed const { image_name, image_origin } = controlImage; controlNetNode.image = {