Skip to content

Commit 0a83903

Browse files
feat(ui): enhance autoprocessing
The processor is automatically selected when model is changed. But if the user manually changes the processor, processor settings, or disables the new `Auto configure processor` switch, auto processing is disabled. The user can enable auto configure by turning the switch back on. When auto configure is enabled, a small dot is overlaid on the expand button to remind the user that the system is not auto configuring the processor for them. If auto configure is enabled, the processor settings are reset to the default for the selected model.
1 parent 844058c commit 0a83903

File tree

5 files changed

+110
-30
lines changed

5 files changed

+110
-30
lines changed

invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlNetAutoProcess.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,38 @@
1-
import { AnyAction } from '@reduxjs/toolkit';
1+
import { AnyListenerPredicate } from '@reduxjs/toolkit';
22
import { startAppListening } from '..';
33
import { log } from 'app/logging/useLogger';
44
import { controlNetImageProcessed } from 'features/controlNet/store/actions';
55
import {
6+
controlNetAutoConfigToggled,
67
controlNetImageChanged,
8+
controlNetModelChanged,
79
controlNetProcessorParamsChanged,
810
controlNetProcessorTypeChanged,
911
} from 'features/controlNet/store/controlNetSlice';
1012
import { RootState } from 'app/store/store';
1113

1214
const moduleLog = log.child({ namespace: 'controlNet' });
1315

14-
const predicate = (action: AnyAction, state: RootState) => {
16+
const predicate: AnyListenerPredicate<RootState> = (action, state) => {
1517
const isActionMatched =
1618
controlNetProcessorParamsChanged.match(action) ||
19+
controlNetModelChanged.match(action) ||
1720
controlNetImageChanged.match(action) ||
18-
controlNetProcessorTypeChanged.match(action);
21+
controlNetProcessorTypeChanged.match(action) ||
22+
controlNetAutoConfigToggled.match(action);
1923

2024
if (!isActionMatched) {
2125
return false;
2226
}
2327

24-
const { controlImage, processorType } =
28+
const { controlImage, processorType, shouldAutoConfig } =
2529
state.controlNet.controlNets[action.payload.controlNetId];
2630

31+
if (controlNetModelChanged.match(action) && !shouldAutoConfig) {
32+
// do not process if the action is a model change but the processor settings are dirty
33+
return false;
34+
}
35+
2736
const isProcessorSelected = processorType !== 'none';
2837

2938
const isBusy = state.system.isProcessing;
@@ -49,7 +58,10 @@ export const addControlNetAutoProcessListener = () => {
4958

5059
// Cancel any in-progress instances of this listener
5160
cancelActiveListeners();
52-
61+
moduleLog.trace(
62+
{ data: action.payload },
63+
'ControlNet auto-process triggered'
64+
);
5365
// Delay before starting actual work
5466
await delay(300);
5567

invokeai/frontend/web/src/features/controlNet/components/ControlNet.tsx

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,8 @@ import {
88
import { useAppDispatch } from 'app/store/storeHooks';
99
import ParamControlNetModel from './parameters/ParamControlNetModel';
1010
import ParamControlNetWeight from './parameters/ParamControlNetWeight';
11-
import {
12-
Checkbox,
13-
Flex,
14-
FormControl,
15-
FormLabel,
16-
HStack,
17-
TabList,
18-
TabPanels,
19-
Tabs,
20-
Tab,
21-
TabPanel,
22-
Box,
23-
VStack,
24-
ChakraProps,
25-
} from '@chakra-ui/react';
26-
import { FaCopy, FaPlus, FaTrash, FaWrench } from 'react-icons/fa';
11+
import { Flex, Box, ChakraProps } from '@chakra-ui/react';
12+
import { FaCopy, FaTrash } from 'react-icons/fa';
2713

2814
import ParamControlNetBeginEnd from './parameters/ParamControlNetBeginEnd';
2915
import ControlNetImagePreview from './ControlNetImagePreview';
@@ -32,10 +18,9 @@ import { v4 as uuidv4 } from 'uuid';
3218
import { useToggle } from 'react-use';
3319
import ParamControlNetProcessorSelect from './parameters/ParamControlNetProcessorSelect';
3420
import ControlNetProcessorComponent from './ControlNetProcessorComponent';
35-
import ControlNetPreprocessButton from './ControlNetPreprocessButton';
36-
import IAIButton from 'common/components/IAIButton';
3721
import IAISwitch from 'common/components/IAISwitch';
38-
import { ChevronDownIcon, ChevronUpIcon } from '@chakra-ui/icons';
22+
import { ChevronUpIcon } from '@chakra-ui/icons';
23+
import ParamControlNetShouldAutoConfig from './ParamControlNetShouldAutoConfig';
3924

4025
const expandedControlImageSx: ChakraProps['sx'] = { maxH: 96 };
4126

@@ -55,6 +40,7 @@ const ControlNet = (props: ControlNetProps) => {
5540
processedControlImage,
5641
processorNode,
5742
processorType,
43+
shouldAutoConfig,
5844
} = props.controlNet;
5945
const dispatch = useAppDispatch();
6046
const [isExpanded, toggleIsExpanded] = useToggle(false);
@@ -81,6 +67,7 @@ const ControlNet = (props: ControlNetProps) => {
8167
p: 3,
8268
bg: 'base.850',
8369
borderRadius: 'base',
70+
position: 'relative',
8471
}}
8572
>
8673
<Flex sx={{ gap: 2 }}>
@@ -119,7 +106,7 @@ const ControlNet = (props: ControlNetProps) => {
119106
/>
120107
<IAIIconButton
121108
size="sm"
122-
aria-label="Expand"
109+
aria-label="Show All Options"
123110
onClick={toggleIsExpanded}
124111
variant="link"
125112
icon={
@@ -134,6 +121,19 @@ const ControlNet = (props: ControlNetProps) => {
134121
/>
135122
}
136123
/>
124+
{!shouldAutoConfig && (
125+
<Box
126+
sx={{
127+
position: 'absolute',
128+
w: 1.5,
129+
h: 1.5,
130+
borderRadius: 'full',
131+
bg: 'error.200',
132+
top: 4,
133+
insetInlineEnd: 4,
134+
}}
135+
/>
136+
)}
137137
</Flex>
138138
{isEnabled && (
139139
<>
@@ -192,6 +192,10 @@ const ControlNet = (props: ControlNetProps) => {
192192
controlNetId={controlNetId}
193193
processorNode={processorNode}
194194
/>
195+
<ParamControlNetShouldAutoConfig
196+
controlNetId={controlNetId}
197+
shouldAutoConfig={shouldAutoConfig}
198+
/>
195199
</>
196200
)}
197201
</>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { useAppDispatch } from 'app/store/storeHooks';
2+
import IAISwitch from 'common/components/IAISwitch';
3+
import { controlNetAutoConfigToggled } from 'features/controlNet/store/controlNetSlice';
4+
import { memo, useCallback } from 'react';
5+
6+
type Props = {
7+
controlNetId: string;
8+
shouldAutoConfig: boolean;
9+
};
10+
11+
const ParamControlNetShouldAutoConfig = (props: Props) => {
12+
const { controlNetId, shouldAutoConfig } = props;
13+
const dispatch = useAppDispatch();
14+
15+
const handleShouldAutoConfigChanged = useCallback(() => {
16+
dispatch(controlNetAutoConfigToggled({ controlNetId }));
17+
}, [controlNetId, dispatch]);
18+
19+
return (
20+
<IAISwitch
21+
label="Auto configure processor"
22+
aria-label="Auto configure processor"
23+
isChecked={shouldAutoConfig}
24+
onChange={handleShouldAutoConfigChanged}
25+
/>
26+
);
27+
};
28+
29+
export default memo(ParamControlNetShouldAutoConfig);

invokeai/frontend/web/src/features/controlNet/components/parameters/ParamControlNetModel.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ import {
77
import { controlNetModelChanged } from 'features/controlNet/store/controlNetSlice';
88
import { memo, useCallback } from 'react';
99

10-
type ParamIsControlNetModelProps = {
10+
type ParamControlNetModelProps = {
1111
controlNetId: string;
1212
model: ControlNetModel;
1313
};
1414

15-
const ParamIsControlNetModel = (props: ParamIsControlNetModelProps) => {
15+
const ParamControlNetModel = (props: ParamControlNetModelProps) => {
1616
const { controlNetId, model } = props;
1717
const dispatch = useAppDispatch();
1818

@@ -38,4 +38,4 @@ const ParamIsControlNetModel = (props: ParamIsControlNetModelProps) => {
3838
);
3939
};
4040

41-
export default memo(ParamIsControlNetModel);
41+
export default memo(ParamControlNetModel);

invokeai/frontend/web/src/features/controlNet/store/controlNetSlice.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { PayloadAction, isAnyOf } from '@reduxjs/toolkit';
1+
import { PayloadAction } from '@reduxjs/toolkit';
22
import { createSlice } from '@reduxjs/toolkit';
33
import { RootState } from 'app/store/store';
44
import { ImageDTO } from 'services/api';
@@ -30,6 +30,7 @@ export const initialControlNet: Omit<ControlNetConfig, 'controlNetId'> = {
3030
processorType: 'canny_image_processor',
3131
processorNode: CONTROLNET_PROCESSORS.canny_image_processor
3232
.default as RequiredCannyImageProcessorInvocation,
33+
shouldAutoConfig: false,
3334
};
3435

3536
export type ControlNetConfig = {
@@ -43,6 +44,7 @@ export type ControlNetConfig = {
4344
processedControlImage: ImageDTO | null;
4445
processorType: ControlNetProcessorType;
4546
processorNode: RequiredControlNetProcessorNode;
47+
shouldAutoConfig: boolean;
4648
};
4749

4850
export type ControlNetState = {
@@ -140,8 +142,9 @@ export const controlNetSlice = createSlice({
140142
) => {
141143
const { controlNetId, model } = action.payload;
142144
state.controlNets[controlNetId].model = model;
145+
state.controlNets[controlNetId].processedControlImage = null;
143146

144-
if (!state.controlNets[controlNetId].controlImage) {
147+
if (state.controlNets[controlNetId].shouldAutoConfig) {
145148
const processorType = CONTROLNET_MODEL_MAP[model];
146149
if (processorType) {
147150
state.controlNets[controlNetId].processorType = processorType;
@@ -192,6 +195,7 @@ export const controlNetSlice = createSlice({
192195
...processorNode,
193196
...changes,
194197
};
198+
state.controlNets[controlNetId].shouldAutoConfig = false;
195199
},
196200
controlNetProcessorTypeChanged: (
197201
state,
@@ -201,10 +205,40 @@ export const controlNetSlice = createSlice({
201205
}>
202206
) => {
203207
const { controlNetId, processorType } = action.payload;
208+
state.controlNets[controlNetId].processedControlImage = null;
204209
state.controlNets[controlNetId].processorType = processorType;
205210
state.controlNets[controlNetId].processorNode = CONTROLNET_PROCESSORS[
206211
processorType
207212
].default as RequiredControlNetProcessorNode;
213+
state.controlNets[controlNetId].shouldAutoConfig = false;
214+
},
215+
controlNetAutoConfigToggled: (
216+
state,
217+
action: PayloadAction<{
218+
controlNetId: string;
219+
}>
220+
) => {
221+
const { controlNetId } = action.payload;
222+
const newShouldAutoConfig =
223+
!state.controlNets[controlNetId].shouldAutoConfig;
224+
225+
if (newShouldAutoConfig) {
226+
// manage the processor for the user
227+
const processorType =
228+
CONTROLNET_MODEL_MAP[state.controlNets[controlNetId].model];
229+
if (processorType) {
230+
state.controlNets[controlNetId].processorType = processorType;
231+
state.controlNets[controlNetId].processorNode = CONTROLNET_PROCESSORS[
232+
processorType
233+
].default as RequiredControlNetProcessorNode;
234+
} else {
235+
state.controlNets[controlNetId].processorType = 'none';
236+
state.controlNets[controlNetId].processorNode = CONTROLNET_PROCESSORS
237+
.none.default as RequiredControlNetProcessorNode;
238+
}
239+
}
240+
241+
state.controlNets[controlNetId].shouldAutoConfig = newShouldAutoConfig;
208242
},
209243
controlNetReset: () => {
210244
return { ...initialControlNetState };
@@ -274,6 +308,7 @@ export const {
274308
controlNetProcessorParamsChanged,
275309
controlNetProcessorTypeChanged,
276310
controlNetReset,
311+
controlNetAutoConfigToggled,
277312
} = controlNetSlice.actions;
278313

279314
export default controlNetSlice.reducer;

0 commit comments

Comments
 (0)