mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
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.
This commit is contained in:
parent
844058c0a5
commit
0a8390356f
@ -1,9 +1,11 @@
|
|||||||
import { AnyAction } from '@reduxjs/toolkit';
|
import { AnyListenerPredicate } from '@reduxjs/toolkit';
|
||||||
import { startAppListening } from '..';
|
import { startAppListening } from '..';
|
||||||
import { log } from 'app/logging/useLogger';
|
import { log } from 'app/logging/useLogger';
|
||||||
import { controlNetImageProcessed } from 'features/controlNet/store/actions';
|
import { controlNetImageProcessed } from 'features/controlNet/store/actions';
|
||||||
import {
|
import {
|
||||||
|
controlNetAutoConfigToggled,
|
||||||
controlNetImageChanged,
|
controlNetImageChanged,
|
||||||
|
controlNetModelChanged,
|
||||||
controlNetProcessorParamsChanged,
|
controlNetProcessorParamsChanged,
|
||||||
controlNetProcessorTypeChanged,
|
controlNetProcessorTypeChanged,
|
||||||
} from 'features/controlNet/store/controlNetSlice';
|
} from 'features/controlNet/store/controlNetSlice';
|
||||||
@ -11,19 +13,26 @@ import { RootState } from 'app/store/store';
|
|||||||
|
|
||||||
const moduleLog = log.child({ namespace: 'controlNet' });
|
const moduleLog = log.child({ namespace: 'controlNet' });
|
||||||
|
|
||||||
const predicate = (action: AnyAction, state: RootState) => {
|
const predicate: AnyListenerPredicate<RootState> = (action, state) => {
|
||||||
const isActionMatched =
|
const isActionMatched =
|
||||||
controlNetProcessorParamsChanged.match(action) ||
|
controlNetProcessorParamsChanged.match(action) ||
|
||||||
|
controlNetModelChanged.match(action) ||
|
||||||
controlNetImageChanged.match(action) ||
|
controlNetImageChanged.match(action) ||
|
||||||
controlNetProcessorTypeChanged.match(action);
|
controlNetProcessorTypeChanged.match(action) ||
|
||||||
|
controlNetAutoConfigToggled.match(action);
|
||||||
|
|
||||||
if (!isActionMatched) {
|
if (!isActionMatched) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { controlImage, processorType } =
|
const { controlImage, processorType, shouldAutoConfig } =
|
||||||
state.controlNet.controlNets[action.payload.controlNetId];
|
state.controlNet.controlNets[action.payload.controlNetId];
|
||||||
|
|
||||||
|
if (controlNetModelChanged.match(action) && !shouldAutoConfig) {
|
||||||
|
// do not process if the action is a model change but the processor settings are dirty
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const isProcessorSelected = processorType !== 'none';
|
const isProcessorSelected = processorType !== 'none';
|
||||||
|
|
||||||
const isBusy = state.system.isProcessing;
|
const isBusy = state.system.isProcessing;
|
||||||
@ -49,7 +58,10 @@ export const addControlNetAutoProcessListener = () => {
|
|||||||
|
|
||||||
// Cancel any in-progress instances of this listener
|
// Cancel any in-progress instances of this listener
|
||||||
cancelActiveListeners();
|
cancelActiveListeners();
|
||||||
|
moduleLog.trace(
|
||||||
|
{ data: action.payload },
|
||||||
|
'ControlNet auto-process triggered'
|
||||||
|
);
|
||||||
// Delay before starting actual work
|
// Delay before starting actual work
|
||||||
await delay(300);
|
await delay(300);
|
||||||
|
|
||||||
|
@ -8,22 +8,8 @@ import {
|
|||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import ParamControlNetModel from './parameters/ParamControlNetModel';
|
import ParamControlNetModel from './parameters/ParamControlNetModel';
|
||||||
import ParamControlNetWeight from './parameters/ParamControlNetWeight';
|
import ParamControlNetWeight from './parameters/ParamControlNetWeight';
|
||||||
import {
|
import { Flex, Box, ChakraProps } from '@chakra-ui/react';
|
||||||
Checkbox,
|
import { FaCopy, FaTrash } from 'react-icons/fa';
|
||||||
Flex,
|
|
||||||
FormControl,
|
|
||||||
FormLabel,
|
|
||||||
HStack,
|
|
||||||
TabList,
|
|
||||||
TabPanels,
|
|
||||||
Tabs,
|
|
||||||
Tab,
|
|
||||||
TabPanel,
|
|
||||||
Box,
|
|
||||||
VStack,
|
|
||||||
ChakraProps,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { FaCopy, FaPlus, FaTrash, FaWrench } from 'react-icons/fa';
|
|
||||||
|
|
||||||
import ParamControlNetBeginEnd from './parameters/ParamControlNetBeginEnd';
|
import ParamControlNetBeginEnd from './parameters/ParamControlNetBeginEnd';
|
||||||
import ControlNetImagePreview from './ControlNetImagePreview';
|
import ControlNetImagePreview from './ControlNetImagePreview';
|
||||||
@ -32,10 +18,9 @@ import { v4 as uuidv4 } from 'uuid';
|
|||||||
import { useToggle } from 'react-use';
|
import { useToggle } from 'react-use';
|
||||||
import ParamControlNetProcessorSelect from './parameters/ParamControlNetProcessorSelect';
|
import ParamControlNetProcessorSelect from './parameters/ParamControlNetProcessorSelect';
|
||||||
import ControlNetProcessorComponent from './ControlNetProcessorComponent';
|
import ControlNetProcessorComponent from './ControlNetProcessorComponent';
|
||||||
import ControlNetPreprocessButton from './ControlNetPreprocessButton';
|
|
||||||
import IAIButton from 'common/components/IAIButton';
|
|
||||||
import IAISwitch from 'common/components/IAISwitch';
|
import IAISwitch from 'common/components/IAISwitch';
|
||||||
import { ChevronDownIcon, ChevronUpIcon } from '@chakra-ui/icons';
|
import { ChevronUpIcon } from '@chakra-ui/icons';
|
||||||
|
import ParamControlNetShouldAutoConfig from './ParamControlNetShouldAutoConfig';
|
||||||
|
|
||||||
const expandedControlImageSx: ChakraProps['sx'] = { maxH: 96 };
|
const expandedControlImageSx: ChakraProps['sx'] = { maxH: 96 };
|
||||||
|
|
||||||
@ -55,6 +40,7 @@ const ControlNet = (props: ControlNetProps) => {
|
|||||||
processedControlImage,
|
processedControlImage,
|
||||||
processorNode,
|
processorNode,
|
||||||
processorType,
|
processorType,
|
||||||
|
shouldAutoConfig,
|
||||||
} = props.controlNet;
|
} = props.controlNet;
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const [isExpanded, toggleIsExpanded] = useToggle(false);
|
const [isExpanded, toggleIsExpanded] = useToggle(false);
|
||||||
@ -81,6 +67,7 @@ const ControlNet = (props: ControlNetProps) => {
|
|||||||
p: 3,
|
p: 3,
|
||||||
bg: 'base.850',
|
bg: 'base.850',
|
||||||
borderRadius: 'base',
|
borderRadius: 'base',
|
||||||
|
position: 'relative',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Flex sx={{ gap: 2 }}>
|
<Flex sx={{ gap: 2 }}>
|
||||||
@ -119,7 +106,7 @@ const ControlNet = (props: ControlNetProps) => {
|
|||||||
/>
|
/>
|
||||||
<IAIIconButton
|
<IAIIconButton
|
||||||
size="sm"
|
size="sm"
|
||||||
aria-label="Expand"
|
aria-label="Show All Options"
|
||||||
onClick={toggleIsExpanded}
|
onClick={toggleIsExpanded}
|
||||||
variant="link"
|
variant="link"
|
||||||
icon={
|
icon={
|
||||||
@ -134,6 +121,19 @@ const ControlNet = (props: ControlNetProps) => {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
{!shouldAutoConfig && (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: 'absolute',
|
||||||
|
w: 1.5,
|
||||||
|
h: 1.5,
|
||||||
|
borderRadius: 'full',
|
||||||
|
bg: 'error.200',
|
||||||
|
top: 4,
|
||||||
|
insetInlineEnd: 4,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
{isEnabled && (
|
{isEnabled && (
|
||||||
<>
|
<>
|
||||||
@ -192,6 +192,10 @@ const ControlNet = (props: ControlNetProps) => {
|
|||||||
controlNetId={controlNetId}
|
controlNetId={controlNetId}
|
||||||
processorNode={processorNode}
|
processorNode={processorNode}
|
||||||
/>
|
/>
|
||||||
|
<ParamControlNetShouldAutoConfig
|
||||||
|
controlNetId={controlNetId}
|
||||||
|
shouldAutoConfig={shouldAutoConfig}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
|
import IAISwitch from 'common/components/IAISwitch';
|
||||||
|
import { controlNetAutoConfigToggled } from 'features/controlNet/store/controlNetSlice';
|
||||||
|
import { memo, useCallback } from 'react';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
controlNetId: string;
|
||||||
|
shouldAutoConfig: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ParamControlNetShouldAutoConfig = (props: Props) => {
|
||||||
|
const { controlNetId, shouldAutoConfig } = props;
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const handleShouldAutoConfigChanged = useCallback(() => {
|
||||||
|
dispatch(controlNetAutoConfigToggled({ controlNetId }));
|
||||||
|
}, [controlNetId, dispatch]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IAISwitch
|
||||||
|
label="Auto configure processor"
|
||||||
|
aria-label="Auto configure processor"
|
||||||
|
isChecked={shouldAutoConfig}
|
||||||
|
onChange={handleShouldAutoConfigChanged}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(ParamControlNetShouldAutoConfig);
|
@ -7,12 +7,12 @@ import {
|
|||||||
import { controlNetModelChanged } from 'features/controlNet/store/controlNetSlice';
|
import { controlNetModelChanged } from 'features/controlNet/store/controlNetSlice';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
|
|
||||||
type ParamIsControlNetModelProps = {
|
type ParamControlNetModelProps = {
|
||||||
controlNetId: string;
|
controlNetId: string;
|
||||||
model: ControlNetModel;
|
model: ControlNetModel;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ParamIsControlNetModel = (props: ParamIsControlNetModelProps) => {
|
const ParamControlNetModel = (props: ParamControlNetModelProps) => {
|
||||||
const { controlNetId, model } = props;
|
const { controlNetId, model } = props;
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
@ -38,4 +38,4 @@ const ParamIsControlNetModel = (props: ParamIsControlNetModelProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(ParamIsControlNetModel);
|
export default memo(ParamControlNetModel);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { PayloadAction, isAnyOf } from '@reduxjs/toolkit';
|
import { PayloadAction } from '@reduxjs/toolkit';
|
||||||
import { createSlice } from '@reduxjs/toolkit';
|
import { createSlice } from '@reduxjs/toolkit';
|
||||||
import { RootState } from 'app/store/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { ImageDTO } from 'services/api';
|
import { ImageDTO } from 'services/api';
|
||||||
@ -30,6 +30,7 @@ export const initialControlNet: Omit<ControlNetConfig, 'controlNetId'> = {
|
|||||||
processorType: 'canny_image_processor',
|
processorType: 'canny_image_processor',
|
||||||
processorNode: CONTROLNET_PROCESSORS.canny_image_processor
|
processorNode: CONTROLNET_PROCESSORS.canny_image_processor
|
||||||
.default as RequiredCannyImageProcessorInvocation,
|
.default as RequiredCannyImageProcessorInvocation,
|
||||||
|
shouldAutoConfig: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ControlNetConfig = {
|
export type ControlNetConfig = {
|
||||||
@ -43,6 +44,7 @@ export type ControlNetConfig = {
|
|||||||
processedControlImage: ImageDTO | null;
|
processedControlImage: ImageDTO | null;
|
||||||
processorType: ControlNetProcessorType;
|
processorType: ControlNetProcessorType;
|
||||||
processorNode: RequiredControlNetProcessorNode;
|
processorNode: RequiredControlNetProcessorNode;
|
||||||
|
shouldAutoConfig: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ControlNetState = {
|
export type ControlNetState = {
|
||||||
@ -140,8 +142,9 @@ export const controlNetSlice = createSlice({
|
|||||||
) => {
|
) => {
|
||||||
const { controlNetId, model } = action.payload;
|
const { controlNetId, model } = action.payload;
|
||||||
state.controlNets[controlNetId].model = model;
|
state.controlNets[controlNetId].model = model;
|
||||||
|
state.controlNets[controlNetId].processedControlImage = null;
|
||||||
|
|
||||||
if (!state.controlNets[controlNetId].controlImage) {
|
if (state.controlNets[controlNetId].shouldAutoConfig) {
|
||||||
const processorType = CONTROLNET_MODEL_MAP[model];
|
const processorType = CONTROLNET_MODEL_MAP[model];
|
||||||
if (processorType) {
|
if (processorType) {
|
||||||
state.controlNets[controlNetId].processorType = processorType;
|
state.controlNets[controlNetId].processorType = processorType;
|
||||||
@ -192,6 +195,7 @@ export const controlNetSlice = createSlice({
|
|||||||
...processorNode,
|
...processorNode,
|
||||||
...changes,
|
...changes,
|
||||||
};
|
};
|
||||||
|
state.controlNets[controlNetId].shouldAutoConfig = false;
|
||||||
},
|
},
|
||||||
controlNetProcessorTypeChanged: (
|
controlNetProcessorTypeChanged: (
|
||||||
state,
|
state,
|
||||||
@ -201,10 +205,40 @@ export const controlNetSlice = createSlice({
|
|||||||
}>
|
}>
|
||||||
) => {
|
) => {
|
||||||
const { controlNetId, processorType } = action.payload;
|
const { controlNetId, processorType } = action.payload;
|
||||||
|
state.controlNets[controlNetId].processedControlImage = null;
|
||||||
state.controlNets[controlNetId].processorType = processorType;
|
state.controlNets[controlNetId].processorType = processorType;
|
||||||
state.controlNets[controlNetId].processorNode = CONTROLNET_PROCESSORS[
|
state.controlNets[controlNetId].processorNode = CONTROLNET_PROCESSORS[
|
||||||
processorType
|
processorType
|
||||||
].default as RequiredControlNetProcessorNode;
|
].default as RequiredControlNetProcessorNode;
|
||||||
|
state.controlNets[controlNetId].shouldAutoConfig = false;
|
||||||
|
},
|
||||||
|
controlNetAutoConfigToggled: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{
|
||||||
|
controlNetId: string;
|
||||||
|
}>
|
||||||
|
) => {
|
||||||
|
const { controlNetId } = action.payload;
|
||||||
|
const newShouldAutoConfig =
|
||||||
|
!state.controlNets[controlNetId].shouldAutoConfig;
|
||||||
|
|
||||||
|
if (newShouldAutoConfig) {
|
||||||
|
// manage the processor for the user
|
||||||
|
const processorType =
|
||||||
|
CONTROLNET_MODEL_MAP[state.controlNets[controlNetId].model];
|
||||||
|
if (processorType) {
|
||||||
|
state.controlNets[controlNetId].processorType = processorType;
|
||||||
|
state.controlNets[controlNetId].processorNode = CONTROLNET_PROCESSORS[
|
||||||
|
processorType
|
||||||
|
].default as RequiredControlNetProcessorNode;
|
||||||
|
} else {
|
||||||
|
state.controlNets[controlNetId].processorType = 'none';
|
||||||
|
state.controlNets[controlNetId].processorNode = CONTROLNET_PROCESSORS
|
||||||
|
.none.default as RequiredControlNetProcessorNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state.controlNets[controlNetId].shouldAutoConfig = newShouldAutoConfig;
|
||||||
},
|
},
|
||||||
controlNetReset: () => {
|
controlNetReset: () => {
|
||||||
return { ...initialControlNetState };
|
return { ...initialControlNetState };
|
||||||
@ -274,6 +308,7 @@ export const {
|
|||||||
controlNetProcessorParamsChanged,
|
controlNetProcessorParamsChanged,
|
||||||
controlNetProcessorTypeChanged,
|
controlNetProcessorTypeChanged,
|
||||||
controlNetReset,
|
controlNetReset,
|
||||||
|
controlNetAutoConfigToggled,
|
||||||
} = controlNetSlice.actions;
|
} = controlNetSlice.actions;
|
||||||
|
|
||||||
export default controlNetSlice.reducer;
|
export default controlNetSlice.reducer;
|
||||||
|
Loading…
Reference in New Issue
Block a user