mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat: add multi-select to gallery
multi-select actions include: - drag to board to move all to that board - right click to add all to board or delete all backend changes: - add routes for changing board for list of image names, deleting list of images - change image-specific routes to `images/i/{image_name}` to not clobber other routes (like `images/upload`, `images/delete`) - subclass pydantic `BaseModel` as `BaseModelExcludeNull`, which excludes null values when calling `dict()` on the model. this fixes inconsistent types related to JSON parsing null values into `null` instead of `undefined` - remove `board_id` from `remove_image_from_board` frontend changes: - multi-selection stuff uses `ImageDTO[]` as payloads, for dnd and other mutations. this gives us access to image `board_id`s when hitting routes, and enables efficient cache updates. - consolidate change board and delete image modals to handle single and multiples - board totals are now re-fetched on mutation and not kept in sync manually - was way too tedious to do this - fixed warning about nested `<p>` elements - closes #4088 , need to handle case when `autoAddBoardId` is `"none"` - add option to show gallery image delete button on every gallery image frontend refactors/organisation: - make typegen script js instead of ts - enable `noUncheckedIndexedAccess` to help avoid bugs when indexing into arrays, many small changes needed to satisfy TS after this - move all image-related endpoints into `endpoints/images.ts`, its a big file now, but this fixes a number of circular dependency issues that were otherwise felt impossible to resolve
This commit is contained in:
@ -3,6 +3,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { FaCopy, FaTrash } from 'react-icons/fa';
|
||||
import {
|
||||
ControlNetConfig,
|
||||
controlNetDuplicated,
|
||||
controlNetRemoved,
|
||||
controlNetToggled,
|
||||
@ -27,18 +28,27 @@ import ParamControlNetProcessorSelect from './parameters/ParamControlNetProcesso
|
||||
import ParamControlNetResizeMode from './parameters/ParamControlNetResizeMode';
|
||||
|
||||
type ControlNetProps = {
|
||||
controlNetId: string;
|
||||
controlNet: ControlNetConfig;
|
||||
};
|
||||
|
||||
const ControlNet = (props: ControlNetProps) => {
|
||||
const { controlNetId } = props;
|
||||
const { controlNet } = props;
|
||||
const { controlNetId } = controlNet;
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const selector = createSelector(
|
||||
stateSelector,
|
||||
({ controlNet }) => {
|
||||
const { isEnabled, shouldAutoConfig } =
|
||||
controlNet.controlNets[controlNetId];
|
||||
const cn = controlNet.controlNets[controlNetId];
|
||||
|
||||
if (!cn) {
|
||||
return {
|
||||
isEnabled: false,
|
||||
shouldAutoConfig: false,
|
||||
};
|
||||
}
|
||||
|
||||
const { isEnabled, shouldAutoConfig } = cn;
|
||||
|
||||
return { isEnabled, shouldAutoConfig };
|
||||
},
|
||||
@ -96,7 +106,7 @@ const ControlNet = (props: ControlNetProps) => {
|
||||
transitionDuration: '0.1s',
|
||||
}}
|
||||
>
|
||||
<ParamControlNetModel controlNetId={controlNetId} />
|
||||
<ParamControlNetModel controlNet={controlNet} />
|
||||
</Box>
|
||||
<IAIIconButton
|
||||
size="sm"
|
||||
@ -171,8 +181,8 @@ const ControlNet = (props: ControlNetProps) => {
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
<ParamControlNetWeight controlNetId={controlNetId} />
|
||||
<ParamControlNetBeginEnd controlNetId={controlNetId} />
|
||||
<ParamControlNetWeight controlNet={controlNet} />
|
||||
<ParamControlNetBeginEnd controlNet={controlNet} />
|
||||
</Flex>
|
||||
{!isExpanded && (
|
||||
<Flex
|
||||
@ -184,22 +194,22 @@ const ControlNet = (props: ControlNetProps) => {
|
||||
aspectRatio: '1/1',
|
||||
}}
|
||||
>
|
||||
<ControlNetImagePreview controlNetId={controlNetId} height={28} />
|
||||
<ControlNetImagePreview controlNet={controlNet} height={28} />
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
<Flex sx={{ gap: 2 }}>
|
||||
<ParamControlNetControlMode controlNetId={controlNetId} />
|
||||
<ParamControlNetResizeMode controlNetId={controlNetId} />
|
||||
<ParamControlNetControlMode controlNet={controlNet} />
|
||||
<ParamControlNetResizeMode controlNet={controlNet} />
|
||||
</Flex>
|
||||
<ParamControlNetProcessorSelect controlNetId={controlNetId} />
|
||||
<ParamControlNetProcessorSelect controlNet={controlNet} />
|
||||
</Flex>
|
||||
|
||||
{isExpanded && (
|
||||
<>
|
||||
<ControlNetImagePreview controlNetId={controlNetId} height="392px" />
|
||||
<ParamControlNetShouldAutoConfig controlNetId={controlNetId} />
|
||||
<ControlNetProcessorComponent controlNetId={controlNetId} />
|
||||
<ControlNetImagePreview controlNet={controlNet} height="392px" />
|
||||
<ParamControlNetShouldAutoConfig controlNet={controlNet} />
|
||||
<ControlNetProcessorComponent controlNet={controlNet} />
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
|
@ -12,50 +12,41 @@ import IAIDndImage from 'common/components/IAIDndImage';
|
||||
import { memo, useCallback, useMemo, useState } from 'react';
|
||||
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
|
||||
import { PostUploadAction } from 'services/api/types';
|
||||
import { controlNetImageChanged } from '../store/controlNetSlice';
|
||||
import {
|
||||
ControlNetConfig,
|
||||
controlNetImageChanged,
|
||||
} from '../store/controlNetSlice';
|
||||
|
||||
type Props = {
|
||||
controlNetId: string;
|
||||
controlNet: ControlNetConfig;
|
||||
height: SystemStyleObject['h'];
|
||||
};
|
||||
|
||||
const selector = createSelector(
|
||||
stateSelector,
|
||||
({ controlNet }) => {
|
||||
const { pendingControlImages } = controlNet;
|
||||
|
||||
return {
|
||||
pendingControlImages,
|
||||
};
|
||||
},
|
||||
defaultSelectorOptions
|
||||
);
|
||||
|
||||
const ControlNetImagePreview = (props: Props) => {
|
||||
const { height, controlNetId } = props;
|
||||
const { height } = props;
|
||||
const {
|
||||
controlImage: controlImageName,
|
||||
processedControlImage: processedControlImageName,
|
||||
processorType,
|
||||
isEnabled,
|
||||
controlNetId,
|
||||
} = props.controlNet;
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createSelector(
|
||||
stateSelector,
|
||||
({ controlNet }) => {
|
||||
const { pendingControlImages } = controlNet;
|
||||
const {
|
||||
controlImage,
|
||||
processedControlImage,
|
||||
processorType,
|
||||
isEnabled,
|
||||
} = controlNet.controlNets[controlNetId];
|
||||
|
||||
return {
|
||||
controlImageName: controlImage,
|
||||
processedControlImageName: processedControlImage,
|
||||
processorType,
|
||||
isEnabled,
|
||||
pendingControlImages,
|
||||
};
|
||||
},
|
||||
defaultSelectorOptions
|
||||
),
|
||||
[controlNetId]
|
||||
);
|
||||
|
||||
const {
|
||||
controlImageName,
|
||||
processedControlImageName,
|
||||
processorType,
|
||||
pendingControlImages,
|
||||
isEnabled,
|
||||
} = useAppSelector(selector);
|
||||
const { pendingControlImages } = useAppSelector(selector);
|
||||
|
||||
const [isMouseOverImage, setIsMouseOverImage] = useState(false);
|
||||
|
||||
|
@ -1,8 +1,5 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { memo } from 'react';
|
||||
import { ControlNetConfig } from '../store/controlNetSlice';
|
||||
import CannyProcessor from './processors/CannyProcessor';
|
||||
import ContentShuffleProcessor from './processors/ContentShuffleProcessor';
|
||||
import HedProcessor from './processors/HedProcessor';
|
||||
@ -17,28 +14,11 @@ import PidiProcessor from './processors/PidiProcessor';
|
||||
import ZoeDepthProcessor from './processors/ZoeDepthProcessor';
|
||||
|
||||
export type ControlNetProcessorProps = {
|
||||
controlNetId: string;
|
||||
controlNet: ControlNetConfig;
|
||||
};
|
||||
|
||||
const ControlNetProcessorComponent = (props: ControlNetProcessorProps) => {
|
||||
const { controlNetId } = props;
|
||||
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createSelector(
|
||||
stateSelector,
|
||||
({ controlNet }) => {
|
||||
const { isEnabled, processorNode } =
|
||||
controlNet.controlNets[controlNetId];
|
||||
|
||||
return { isEnabled, processorNode };
|
||||
},
|
||||
defaultSelectorOptions
|
||||
),
|
||||
[controlNetId]
|
||||
);
|
||||
|
||||
const { isEnabled, processorNode } = useAppSelector(selector);
|
||||
const { controlNetId, isEnabled, processorNode } = props.controlNet;
|
||||
|
||||
if (processorNode.type === 'canny_image_processor') {
|
||||
return (
|
||||
|
@ -1,34 +1,19 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import IAISwitch from 'common/components/IAISwitch';
|
||||
import { controlNetAutoConfigToggled } from 'features/controlNet/store/controlNetSlice';
|
||||
import {
|
||||
ControlNetConfig,
|
||||
controlNetAutoConfigToggled,
|
||||
} from 'features/controlNet/store/controlNetSlice';
|
||||
import { selectIsBusy } from 'features/system/store/systemSelectors';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { memo, useCallback } from 'react';
|
||||
|
||||
type Props = {
|
||||
controlNetId: string;
|
||||
controlNet: ControlNetConfig;
|
||||
};
|
||||
|
||||
const ParamControlNetShouldAutoConfig = (props: Props) => {
|
||||
const { controlNetId } = props;
|
||||
const { controlNetId, isEnabled, shouldAutoConfig } = props.controlNet;
|
||||
const dispatch = useAppDispatch();
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createSelector(
|
||||
stateSelector,
|
||||
({ controlNet }) => {
|
||||
const { isEnabled, shouldAutoConfig } =
|
||||
controlNet.controlNets[controlNetId];
|
||||
return { isEnabled, shouldAutoConfig };
|
||||
},
|
||||
defaultSelectorOptions
|
||||
),
|
||||
[controlNetId]
|
||||
);
|
||||
|
||||
const { isEnabled, shouldAutoConfig } = useAppSelector(selector);
|
||||
const isBusy = useAppSelector(selectIsBusy);
|
||||
|
||||
const handleShouldAutoConfigChanged = useCallback(() => {
|
||||
|
@ -9,48 +9,39 @@ import {
|
||||
RangeSliderTrack,
|
||||
Tooltip,
|
||||
} from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import {
|
||||
ControlNetConfig,
|
||||
controlNetBeginStepPctChanged,
|
||||
controlNetEndStepPctChanged,
|
||||
} from 'features/controlNet/store/controlNetSlice';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { memo, useCallback } from 'react';
|
||||
|
||||
type Props = {
|
||||
controlNetId: string;
|
||||
controlNet: ControlNetConfig;
|
||||
};
|
||||
|
||||
const formatPct = (v: number) => `${Math.round(v * 100)}%`;
|
||||
|
||||
const ParamControlNetBeginEnd = (props: Props) => {
|
||||
const { controlNetId } = props;
|
||||
const { beginStepPct, endStepPct, isEnabled, controlNetId } =
|
||||
props.controlNet;
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createSelector(
|
||||
stateSelector,
|
||||
({ controlNet }) => {
|
||||
const { beginStepPct, endStepPct, isEnabled } =
|
||||
controlNet.controlNets[controlNetId];
|
||||
return { beginStepPct, endStepPct, isEnabled };
|
||||
},
|
||||
defaultSelectorOptions
|
||||
),
|
||||
[controlNetId]
|
||||
);
|
||||
|
||||
const { beginStepPct, endStepPct, isEnabled } = useAppSelector(selector);
|
||||
|
||||
const handleStepPctChanged = useCallback(
|
||||
(v: number[]) => {
|
||||
dispatch(
|
||||
controlNetBeginStepPctChanged({ controlNetId, beginStepPct: v[0] })
|
||||
controlNetBeginStepPctChanged({
|
||||
controlNetId,
|
||||
beginStepPct: v[0] as number,
|
||||
})
|
||||
);
|
||||
dispatch(
|
||||
controlNetEndStepPctChanged({
|
||||
controlNetId,
|
||||
endStepPct: v[1] as number,
|
||||
})
|
||||
);
|
||||
dispatch(controlNetEndStepPctChanged({ controlNetId, endStepPct: v[1] }));
|
||||
},
|
||||
[controlNetId, dispatch]
|
||||
);
|
||||
|
@ -1,16 +1,14 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
||||
import {
|
||||
ControlModes,
|
||||
ControlNetConfig,
|
||||
controlNetControlModeChanged,
|
||||
} from 'features/controlNet/store/controlNetSlice';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
type ParamControlNetControlModeProps = {
|
||||
controlNetId: string;
|
||||
controlNet: ControlNetConfig;
|
||||
};
|
||||
|
||||
const CONTROL_MODE_DATA = [
|
||||
@ -23,23 +21,8 @@ const CONTROL_MODE_DATA = [
|
||||
export default function ParamControlNetControlMode(
|
||||
props: ParamControlNetControlModeProps
|
||||
) {
|
||||
const { controlNetId } = props;
|
||||
const { controlMode, isEnabled, controlNetId } = props.controlNet;
|
||||
const dispatch = useAppDispatch();
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createSelector(
|
||||
stateSelector,
|
||||
({ controlNet }) => {
|
||||
const { controlMode, isEnabled } =
|
||||
controlNet.controlNets[controlNetId];
|
||||
return { controlMode, isEnabled };
|
||||
},
|
||||
defaultSelectorOptions
|
||||
),
|
||||
[controlNetId]
|
||||
);
|
||||
|
||||
const { controlMode, isEnabled } = useAppSelector(selector);
|
||||
|
||||
const handleControlModeChange = useCallback(
|
||||
(controlMode: ControlModes) => {
|
||||
|
@ -5,7 +5,10 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import IAIMantineSearchableSelect from 'common/components/IAIMantineSearchableSelect';
|
||||
import IAIMantineSelectItemWithTooltip from 'common/components/IAIMantineSelectItemWithTooltip';
|
||||
import { controlNetModelChanged } from 'features/controlNet/store/controlNetSlice';
|
||||
import {
|
||||
ControlNetConfig,
|
||||
controlNetModelChanged,
|
||||
} from 'features/controlNet/store/controlNetSlice';
|
||||
import { MODEL_TYPE_MAP } from 'features/parameters/types/constants';
|
||||
import { modelIdToControlNetModelParam } from 'features/parameters/util/modelIdToControlNetModelParam';
|
||||
import { selectIsBusy } from 'features/system/store/systemSelectors';
|
||||
@ -14,30 +17,24 @@ import { memo, useCallback, useMemo } from 'react';
|
||||
import { useGetControlNetModelsQuery } from 'services/api/endpoints/models';
|
||||
|
||||
type ParamControlNetModelProps = {
|
||||
controlNetId: string;
|
||||
controlNet: ControlNetConfig;
|
||||
};
|
||||
|
||||
const selector = createSelector(
|
||||
stateSelector,
|
||||
({ generation }) => {
|
||||
const { model } = generation;
|
||||
return { mainModel: model };
|
||||
},
|
||||
defaultSelectorOptions
|
||||
);
|
||||
|
||||
const ParamControlNetModel = (props: ParamControlNetModelProps) => {
|
||||
const { controlNetId } = props;
|
||||
const { controlNetId, model: controlNetModel, isEnabled } = props.controlNet;
|
||||
const dispatch = useAppDispatch();
|
||||
const isBusy = useAppSelector(selectIsBusy);
|
||||
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createSelector(
|
||||
stateSelector,
|
||||
({ generation, controlNet }) => {
|
||||
const { model } = generation;
|
||||
const controlNetModel = controlNet.controlNets[controlNetId]?.model;
|
||||
const isEnabled = controlNet.controlNets[controlNetId]?.isEnabled;
|
||||
return { mainModel: model, controlNetModel, isEnabled };
|
||||
},
|
||||
defaultSelectorOptions
|
||||
),
|
||||
[controlNetId]
|
||||
);
|
||||
|
||||
const { mainModel, controlNetModel, isEnabled } = useAppSelector(selector);
|
||||
const { mainModel } = useAppSelector(selector);
|
||||
|
||||
const { data: controlNetModels } = useGetControlNetModelsQuery();
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import IAIMantineSearchableSelect, {
|
||||
IAISelectDataType,
|
||||
@ -9,13 +8,16 @@ import IAIMantineSearchableSelect, {
|
||||
import { configSelector } from 'features/system/store/configSelectors';
|
||||
import { selectIsBusy } from 'features/system/store/systemSelectors';
|
||||
import { map } from 'lodash-es';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { CONTROLNET_PROCESSORS } from '../../store/constants';
|
||||
import { controlNetProcessorTypeChanged } from '../../store/controlNetSlice';
|
||||
import {
|
||||
ControlNetConfig,
|
||||
controlNetProcessorTypeChanged,
|
||||
} from '../../store/controlNetSlice';
|
||||
import { ControlNetProcessorType } from '../../store/types';
|
||||
|
||||
type ParamControlNetProcessorSelectProps = {
|
||||
controlNetId: string;
|
||||
controlNet: ControlNetConfig;
|
||||
};
|
||||
|
||||
const selector = createSelector(
|
||||
@ -52,23 +54,9 @@ const ParamControlNetProcessorSelect = (
|
||||
props: ParamControlNetProcessorSelectProps
|
||||
) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { controlNetId } = props;
|
||||
const processorNodeSelector = useMemo(
|
||||
() =>
|
||||
createSelector(
|
||||
stateSelector,
|
||||
({ controlNet }) => {
|
||||
const { isEnabled, processorNode } =
|
||||
controlNet.controlNets[controlNetId];
|
||||
return { isEnabled, processorNode };
|
||||
},
|
||||
defaultSelectorOptions
|
||||
),
|
||||
[controlNetId]
|
||||
);
|
||||
const { controlNetId, isEnabled, processorNode } = props.controlNet;
|
||||
const isBusy = useAppSelector(selectIsBusy);
|
||||
const controlNetProcessors = useAppSelector(selector);
|
||||
const { isEnabled, processorNode } = useAppSelector(processorNodeSelector);
|
||||
|
||||
const handleProcessorTypeChanged = useCallback(
|
||||
(v: string | null) => {
|
||||
|
@ -1,16 +1,14 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
||||
import {
|
||||
ControlNetConfig,
|
||||
ResizeModes,
|
||||
controlNetResizeModeChanged,
|
||||
} from 'features/controlNet/store/controlNetSlice';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
type ParamControlNetResizeModeProps = {
|
||||
controlNetId: string;
|
||||
controlNet: ControlNetConfig;
|
||||
};
|
||||
|
||||
const RESIZE_MODE_DATA = [
|
||||
@ -22,23 +20,8 @@ const RESIZE_MODE_DATA = [
|
||||
export default function ParamControlNetResizeMode(
|
||||
props: ParamControlNetResizeModeProps
|
||||
) {
|
||||
const { controlNetId } = props;
|
||||
const { resizeMode, isEnabled, controlNetId } = props.controlNet;
|
||||
const dispatch = useAppDispatch();
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createSelector(
|
||||
stateSelector,
|
||||
({ controlNet }) => {
|
||||
const { resizeMode, isEnabled } =
|
||||
controlNet.controlNets[controlNetId];
|
||||
return { resizeMode, isEnabled };
|
||||
},
|
||||
defaultSelectorOptions
|
||||
),
|
||||
[controlNetId]
|
||||
);
|
||||
|
||||
const { resizeMode, isEnabled } = useAppSelector(selector);
|
||||
|
||||
const handleResizeModeChange = useCallback(
|
||||
(resizeMode: ResizeModes) => {
|
||||
|
@ -1,32 +1,18 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import IAISlider from 'common/components/IAISlider';
|
||||
import { controlNetWeightChanged } from 'features/controlNet/store/controlNetSlice';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import {
|
||||
ControlNetConfig,
|
||||
controlNetWeightChanged,
|
||||
} from 'features/controlNet/store/controlNetSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
|
||||
type ParamControlNetWeightProps = {
|
||||
controlNetId: string;
|
||||
controlNet: ControlNetConfig;
|
||||
};
|
||||
|
||||
const ParamControlNetWeight = (props: ParamControlNetWeightProps) => {
|
||||
const { controlNetId } = props;
|
||||
const { weight, isEnabled, controlNetId } = props.controlNet;
|
||||
const dispatch = useAppDispatch();
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createSelector(
|
||||
stateSelector,
|
||||
({ controlNet }) => {
|
||||
const { weight, isEnabled } = controlNet.controlNets[controlNetId];
|
||||
return { weight, isEnabled };
|
||||
},
|
||||
defaultSelectorOptions
|
||||
),
|
||||
[controlNetId]
|
||||
);
|
||||
|
||||
const { weight, isEnabled } = useAppSelector(selector);
|
||||
const handleWeightChanged = useCallback(
|
||||
(weight: number) => {
|
||||
dispatch(controlNetWeightChanged({ controlNetId, weight }));
|
||||
|
@ -4,7 +4,7 @@ import {
|
||||
} from './types';
|
||||
|
||||
type ControlNetProcessorsDict = Record<
|
||||
string,
|
||||
ControlNetProcessorType,
|
||||
{
|
||||
type: ControlNetProcessorType | 'none';
|
||||
label: string;
|
||||
|
@ -96,8 +96,11 @@ export const controlNetSlice = createSlice({
|
||||
}>
|
||||
) => {
|
||||
const { sourceControlNetId, newControlNetId } = action.payload;
|
||||
|
||||
const newControlnet = cloneDeep(state.controlNets[sourceControlNetId]);
|
||||
const oldControlNet = state.controlNets[sourceControlNetId];
|
||||
if (!oldControlNet) {
|
||||
return;
|
||||
}
|
||||
const newControlnet = cloneDeep(oldControlNet);
|
||||
newControlnet.controlNetId = newControlNetId;
|
||||
state.controlNets[newControlNetId] = newControlnet;
|
||||
},
|
||||
@ -124,8 +127,11 @@ export const controlNetSlice = createSlice({
|
||||
action: PayloadAction<{ controlNetId: string }>
|
||||
) => {
|
||||
const { controlNetId } = action.payload;
|
||||
state.controlNets[controlNetId].isEnabled =
|
||||
!state.controlNets[controlNetId].isEnabled;
|
||||
const cn = state.controlNets[controlNetId];
|
||||
if (!cn) {
|
||||
return;
|
||||
}
|
||||
cn.isEnabled = !cn.isEnabled;
|
||||
},
|
||||
controlNetImageChanged: (
|
||||
state,
|
||||
@ -135,12 +141,14 @@ export const controlNetSlice = createSlice({
|
||||
}>
|
||||
) => {
|
||||
const { controlNetId, controlImage } = action.payload;
|
||||
state.controlNets[controlNetId].controlImage = controlImage;
|
||||
state.controlNets[controlNetId].processedControlImage = null;
|
||||
if (
|
||||
controlImage !== null &&
|
||||
state.controlNets[controlNetId].processorType !== 'none'
|
||||
) {
|
||||
const cn = state.controlNets[controlNetId];
|
||||
if (!cn) {
|
||||
return;
|
||||
}
|
||||
|
||||
cn.controlImage = controlImage;
|
||||
cn.processedControlImage = null;
|
||||
if (controlImage !== null && cn.processorType !== 'none') {
|
||||
state.pendingControlImages.push(controlNetId);
|
||||
}
|
||||
},
|
||||
@ -152,8 +160,12 @@ export const controlNetSlice = createSlice({
|
||||
}>
|
||||
) => {
|
||||
const { controlNetId, processedControlImage } = action.payload;
|
||||
state.controlNets[controlNetId].processedControlImage =
|
||||
processedControlImage;
|
||||
const cn = state.controlNets[controlNetId];
|
||||
if (!cn) {
|
||||
return;
|
||||
}
|
||||
|
||||
cn.processedControlImage = processedControlImage;
|
||||
state.pendingControlImages = state.pendingControlImages.filter(
|
||||
(id) => id !== controlNetId
|
||||
);
|
||||
@ -166,10 +178,15 @@ export const controlNetSlice = createSlice({
|
||||
}>
|
||||
) => {
|
||||
const { controlNetId, model } = action.payload;
|
||||
state.controlNets[controlNetId].model = model;
|
||||
state.controlNets[controlNetId].processedControlImage = null;
|
||||
const cn = state.controlNets[controlNetId];
|
||||
if (!cn) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.controlNets[controlNetId].shouldAutoConfig) {
|
||||
cn.model = model;
|
||||
cn.processedControlImage = null;
|
||||
|
||||
if (cn.shouldAutoConfig) {
|
||||
let processorType: ControlNetProcessorType | undefined = undefined;
|
||||
|
||||
for (const modelSubstring in CONTROLNET_MODEL_DEFAULT_PROCESSORS) {
|
||||
@ -180,14 +197,13 @@ export const controlNetSlice = createSlice({
|
||||
}
|
||||
|
||||
if (processorType) {
|
||||
state.controlNets[controlNetId].processorType = processorType;
|
||||
state.controlNets[controlNetId].processorNode = CONTROLNET_PROCESSORS[
|
||||
processorType
|
||||
].default as RequiredControlNetProcessorNode;
|
||||
cn.processorType = processorType;
|
||||
cn.processorNode = CONTROLNET_PROCESSORS[processorType]
|
||||
.default as RequiredControlNetProcessorNode;
|
||||
} else {
|
||||
state.controlNets[controlNetId].processorType = 'none';
|
||||
state.controlNets[controlNetId].processorNode = CONTROLNET_PROCESSORS
|
||||
.none.default as RequiredControlNetProcessorNode;
|
||||
cn.processorType = 'none';
|
||||
cn.processorNode = CONTROLNET_PROCESSORS.none
|
||||
.default as RequiredControlNetProcessorNode;
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -196,28 +212,48 @@ export const controlNetSlice = createSlice({
|
||||
action: PayloadAction<{ controlNetId: string; weight: number }>
|
||||
) => {
|
||||
const { controlNetId, weight } = action.payload;
|
||||
state.controlNets[controlNetId].weight = weight;
|
||||
const cn = state.controlNets[controlNetId];
|
||||
if (!cn) {
|
||||
return;
|
||||
}
|
||||
|
||||
cn.weight = weight;
|
||||
},
|
||||
controlNetBeginStepPctChanged: (
|
||||
state,
|
||||
action: PayloadAction<{ controlNetId: string; beginStepPct: number }>
|
||||
) => {
|
||||
const { controlNetId, beginStepPct } = action.payload;
|
||||
state.controlNets[controlNetId].beginStepPct = beginStepPct;
|
||||
const cn = state.controlNets[controlNetId];
|
||||
if (!cn) {
|
||||
return;
|
||||
}
|
||||
|
||||
cn.beginStepPct = beginStepPct;
|
||||
},
|
||||
controlNetEndStepPctChanged: (
|
||||
state,
|
||||
action: PayloadAction<{ controlNetId: string; endStepPct: number }>
|
||||
) => {
|
||||
const { controlNetId, endStepPct } = action.payload;
|
||||
state.controlNets[controlNetId].endStepPct = endStepPct;
|
||||
const cn = state.controlNets[controlNetId];
|
||||
if (!cn) {
|
||||
return;
|
||||
}
|
||||
|
||||
cn.endStepPct = endStepPct;
|
||||
},
|
||||
controlNetControlModeChanged: (
|
||||
state,
|
||||
action: PayloadAction<{ controlNetId: string; controlMode: ControlModes }>
|
||||
) => {
|
||||
const { controlNetId, controlMode } = action.payload;
|
||||
state.controlNets[controlNetId].controlMode = controlMode;
|
||||
const cn = state.controlNets[controlNetId];
|
||||
if (!cn) {
|
||||
return;
|
||||
}
|
||||
|
||||
cn.controlMode = controlMode;
|
||||
},
|
||||
controlNetResizeModeChanged: (
|
||||
state,
|
||||
@ -227,7 +263,12 @@ export const controlNetSlice = createSlice({
|
||||
}>
|
||||
) => {
|
||||
const { controlNetId, resizeMode } = action.payload;
|
||||
state.controlNets[controlNetId].resizeMode = resizeMode;
|
||||
const cn = state.controlNets[controlNetId];
|
||||
if (!cn) {
|
||||
return;
|
||||
}
|
||||
|
||||
cn.resizeMode = resizeMode;
|
||||
},
|
||||
controlNetProcessorParamsChanged: (
|
||||
state,
|
||||
@ -240,12 +281,17 @@ export const controlNetSlice = createSlice({
|
||||
}>
|
||||
) => {
|
||||
const { controlNetId, changes } = action.payload;
|
||||
const processorNode = state.controlNets[controlNetId].processorNode;
|
||||
state.controlNets[controlNetId].processorNode = {
|
||||
const cn = state.controlNets[controlNetId];
|
||||
if (!cn) {
|
||||
return;
|
||||
}
|
||||
|
||||
const processorNode = cn.processorNode;
|
||||
cn.processorNode = {
|
||||
...processorNode,
|
||||
...changes,
|
||||
};
|
||||
state.controlNets[controlNetId].shouldAutoConfig = false;
|
||||
cn.shouldAutoConfig = false;
|
||||
},
|
||||
controlNetProcessorTypeChanged: (
|
||||
state,
|
||||
@ -255,12 +301,16 @@ export const controlNetSlice = createSlice({
|
||||
}>
|
||||
) => {
|
||||
const { controlNetId, processorType } = action.payload;
|
||||
state.controlNets[controlNetId].processedControlImage = null;
|
||||
state.controlNets[controlNetId].processorType = processorType;
|
||||
state.controlNets[controlNetId].processorNode = CONTROLNET_PROCESSORS[
|
||||
processorType
|
||||
].default as RequiredControlNetProcessorNode;
|
||||
state.controlNets[controlNetId].shouldAutoConfig = false;
|
||||
const cn = state.controlNets[controlNetId];
|
||||
if (!cn) {
|
||||
return;
|
||||
}
|
||||
|
||||
cn.processedControlImage = null;
|
||||
cn.processorType = processorType;
|
||||
cn.processorNode = CONTROLNET_PROCESSORS[processorType]
|
||||
.default as RequiredControlNetProcessorNode;
|
||||
cn.shouldAutoConfig = false;
|
||||
},
|
||||
controlNetAutoConfigToggled: (
|
||||
state,
|
||||
@ -269,37 +319,36 @@ export const controlNetSlice = createSlice({
|
||||
}>
|
||||
) => {
|
||||
const { controlNetId } = action.payload;
|
||||
const newShouldAutoConfig =
|
||||
!state.controlNets[controlNetId].shouldAutoConfig;
|
||||
const cn = state.controlNets[controlNetId];
|
||||
if (!cn) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newShouldAutoConfig = !cn.shouldAutoConfig;
|
||||
|
||||
if (newShouldAutoConfig) {
|
||||
// manage the processor for the user
|
||||
let processorType: ControlNetProcessorType | undefined = undefined;
|
||||
|
||||
for (const modelSubstring in CONTROLNET_MODEL_DEFAULT_PROCESSORS) {
|
||||
if (
|
||||
state.controlNets[controlNetId].model?.model_name.includes(
|
||||
modelSubstring
|
||||
)
|
||||
) {
|
||||
if (cn.model?.model_name.includes(modelSubstring)) {
|
||||
processorType = CONTROLNET_MODEL_DEFAULT_PROCESSORS[modelSubstring];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (processorType) {
|
||||
state.controlNets[controlNetId].processorType = processorType;
|
||||
state.controlNets[controlNetId].processorNode = CONTROLNET_PROCESSORS[
|
||||
processorType
|
||||
].default as RequiredControlNetProcessorNode;
|
||||
cn.processorType = processorType;
|
||||
cn.processorNode = CONTROLNET_PROCESSORS[processorType]
|
||||
.default as RequiredControlNetProcessorNode;
|
||||
} else {
|
||||
state.controlNets[controlNetId].processorType = 'none';
|
||||
state.controlNets[controlNetId].processorNode = CONTROLNET_PROCESSORS
|
||||
.none.default as RequiredControlNetProcessorNode;
|
||||
cn.processorType = 'none';
|
||||
cn.processorNode = CONTROLNET_PROCESSORS.none
|
||||
.default as RequiredControlNetProcessorNode;
|
||||
}
|
||||
}
|
||||
|
||||
state.controlNets[controlNetId].shouldAutoConfig = newShouldAutoConfig;
|
||||
cn.shouldAutoConfig = newShouldAutoConfig;
|
||||
},
|
||||
controlNetReset: () => {
|
||||
return { ...initialControlNetState };
|
||||
@ -307,9 +356,11 @@ export const controlNetSlice = createSlice({
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(controlNetImageProcessed, (state, action) => {
|
||||
if (
|
||||
state.controlNets[action.payload.controlNetId].controlImage !== null
|
||||
) {
|
||||
const cn = state.controlNets[action.payload.controlNetId];
|
||||
if (!cn) {
|
||||
return;
|
||||
}
|
||||
if (cn.controlImage !== null) {
|
||||
state.pendingControlImages.push(action.payload.controlNetId);
|
||||
}
|
||||
});
|
||||
|
Reference in New Issue
Block a user