mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): normalize all actions to accept an entityIdentifier
Previously, canvas actions specific to an entity type only needed the id of that entity type. This allowed you to pass in the id of an entity of the wrong type. All actions for a specific entity now take a full entity identifier, and the entity identifier type can be narrowed. `selectEntity` and `selectEntityOrThrow` now need a full entity identifier, and narrow their return values to a specific entity type _if_ the entity identifier is narrowed. The types for canvas entities are updated with optional type parameters for this purpose. All reducers, actions and components have been updated.
This commit is contained in:
parent
f86b50d18a
commit
a3179e7a3f
@ -2,6 +2,7 @@ import { logger } from 'app/logging/logger';
|
||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||
import type { AppDispatch, RootState } from 'app/store/store';
|
||||
import { entityDeleted, ipaImageChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { getEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { imageDeletionConfirmed } from 'features/deleteImageModal/store/actions';
|
||||
import { isModalOpenChanged } from 'features/deleteImageModal/store/slice';
|
||||
import { selectListImagesQueryArgs } from 'features/gallery/store/gallerySelectors';
|
||||
@ -51,9 +52,9 @@ const deleteNodesImages = (state: RootState, dispatch: AppDispatch, imageDTO: Im
|
||||
// };
|
||||
|
||||
const deleteIPAdapterImages = (state: RootState, dispatch: AppDispatch, imageDTO: ImageDTO) => {
|
||||
state.canvasV2.ipAdapters.entities.forEach(({ id, ipAdapter }) => {
|
||||
if (ipAdapter.image?.image_name === imageDTO.image_name) {
|
||||
dispatch(ipaImageChanged({ id, imageDTO: null }));
|
||||
state.canvasV2.ipAdapters.entities.forEach((entity) => {
|
||||
if (entity.ipAdapter.image?.image_name === imageDTO.image_name) {
|
||||
dispatch(ipaImageChanged({ entityIdentifier: getEntityIdentifier(entity), imageDTO: null }));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -51,7 +51,9 @@ export const addImageDroppedListener = (startAppListening: AppStartListening) =>
|
||||
activeData.payload.imageDTO
|
||||
) {
|
||||
const { id } = overData.context;
|
||||
dispatch(ipaImageChanged({ id, imageDTO: activeData.payload.imageDTO }));
|
||||
dispatch(
|
||||
ipaImageChanged({ entityIdentifier: { id, type: 'ip_adapter' }, imageDTO: activeData.payload.imageDTO })
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -64,7 +66,13 @@ export const addImageDroppedListener = (startAppListening: AppStartListening) =>
|
||||
activeData.payload.imageDTO
|
||||
) {
|
||||
const { id, ipAdapterId } = overData.context;
|
||||
dispatch(rgIPAdapterImageChanged({ id, ipAdapterId, imageDTO: activeData.payload.imageDTO }));
|
||||
dispatch(
|
||||
rgIPAdapterImageChanged({
|
||||
entityIdentifier: { id, type: 'regional_guidance' },
|
||||
ipAdapterId,
|
||||
imageDTO: activeData.payload.imageDTO,
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -89,14 +89,16 @@ export const addImageUploadedFulfilledListener = (startAppListening: AppStartLis
|
||||
|
||||
if (postUploadAction?.type === 'SET_IPA_IMAGE') {
|
||||
const { id } = postUploadAction;
|
||||
dispatch(ipaImageChanged({ id, imageDTO }));
|
||||
dispatch(ipaImageChanged({ entityIdentifier: { id, type: 'ip_adapter' }, imageDTO }));
|
||||
toast({ ...DEFAULT_UPLOADED_TOAST, description: t('toast.setControlImage') });
|
||||
return;
|
||||
}
|
||||
|
||||
if (postUploadAction?.type === 'SET_RG_IP_ADAPTER_IMAGE') {
|
||||
const { id, ipAdapterId } = postUploadAction;
|
||||
dispatch(rgIPAdapterImageChanged({ id, ipAdapterId, imageDTO }));
|
||||
dispatch(
|
||||
rgIPAdapterImageChanged({ entityIdentifier: { id, type: 'regional_guidance' }, ipAdapterId, imageDTO })
|
||||
);
|
||||
toast({ ...DEFAULT_UPLOADED_TOAST, description: t('toast.setControlImage') });
|
||||
return;
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import {
|
||||
rgIPAdapterModelChanged,
|
||||
vaeSelected,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { getEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { calculateNewSize } from 'features/parameters/components/DocumentSize/calculateNewSize';
|
||||
import { postProcessingModelChanged, upscaleModelChanged } from 'features/parameters/store/upscaleSlice';
|
||||
import { zParameterModel, zParameterVAEModel } from 'features/parameters/types/parameterSchemas';
|
||||
@ -178,7 +179,7 @@ const handleControlAdapterModels: ModelHandler = (models, state, dispatch, _log)
|
||||
if (isModelAvailable) {
|
||||
return;
|
||||
}
|
||||
dispatch(controlLayerModelChanged({ id: entity.id, modelConfig: null }));
|
||||
dispatch(controlLayerModelChanged({ entityIdentifier: getEntityIdentifier(entity), modelConfig: null }));
|
||||
});
|
||||
};
|
||||
|
||||
@ -189,16 +190,18 @@ const handleIPAdapterModels: ModelHandler = (models, state, dispatch, _log) => {
|
||||
if (isModelAvailable) {
|
||||
return;
|
||||
}
|
||||
dispatch(ipaModelChanged({ id: entity.id, modelConfig: null }));
|
||||
dispatch(ipaModelChanged({ entityIdentifier: getEntityIdentifier(entity), modelConfig: null }));
|
||||
});
|
||||
|
||||
state.canvasV2.regions.entities.forEach(({ id, ipAdapters }) => {
|
||||
ipAdapters.forEach(({ id: ipAdapterId, model }) => {
|
||||
state.canvasV2.regions.entities.forEach((entity) => {
|
||||
entity.ipAdapters.forEach(({ id: ipAdapterId, model }) => {
|
||||
const isModelAvailable = ipaModels.some((m) => m.key === model?.key);
|
||||
if (isModelAvailable) {
|
||||
return;
|
||||
}
|
||||
dispatch(rgIPAdapterModelChanged({ id, ipAdapterId, modelConfig: null }));
|
||||
dispatch(
|
||||
rgIPAdapterModelChanged({ entityIdentifier: getEntityIdentifier(entity), ipAdapterId, modelConfig: null })
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -2,7 +2,7 @@ import { useStore } from '@nanostores/react';
|
||||
import { $isConnected } from 'app/hooks/useSocketIO';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
|
||||
import { getShouldProcessPrompt } from 'features/dynamicPrompts/util/getShouldProcessPrompt';
|
||||
import { $templates, selectNodesSlice } from 'features/nodes/store/nodesSlice';
|
||||
|
@ -1,15 +1,15 @@
|
||||
import { Badge } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { selectControlLayerEntityOrThrow } from 'features/controlLayers/store/controlLayersReducers';
|
||||
import { selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const ControlLayerBadges = memo(() => {
|
||||
const { id } = useEntityIdentifierContext();
|
||||
const entityIdentifier = useEntityIdentifierContext('control_layer');
|
||||
const { t } = useTranslation();
|
||||
const withTransparencyEffect = useAppSelector(
|
||||
(s) => selectControlLayerEntityOrThrow(s.canvasV2, id).withTransparencyEffect
|
||||
(s) => selectEntityOrThrow(s.canvasV2, entityIdentifier).withTransparencyEffect
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -18,35 +18,35 @@ import type { ControlNetModelConfig, T2IAdapterModelConfig } from 'services/api/
|
||||
|
||||
export const ControlLayerControlAdapter = memo(() => {
|
||||
const dispatch = useAppDispatch();
|
||||
const entityIdentifier = useEntityIdentifierContext();
|
||||
const entityIdentifier = useEntityIdentifierContext('control_layer');
|
||||
const controlAdapter = useControlLayerControlAdapter(entityIdentifier);
|
||||
|
||||
const onChangeBeginEndStepPct = useCallback(
|
||||
(beginEndStepPct: [number, number]) => {
|
||||
dispatch(controlLayerBeginEndStepPctChanged({ id: entityIdentifier.id, beginEndStepPct }));
|
||||
dispatch(controlLayerBeginEndStepPctChanged({ entityIdentifier, beginEndStepPct }));
|
||||
},
|
||||
[dispatch, entityIdentifier.id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
|
||||
const onChangeControlMode = useCallback(
|
||||
(controlMode: ControlModeV2) => {
|
||||
dispatch(controlLayerControlModeChanged({ id: entityIdentifier.id, controlMode }));
|
||||
dispatch(controlLayerControlModeChanged({ entityIdentifier, controlMode }));
|
||||
},
|
||||
[dispatch, entityIdentifier.id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
|
||||
const onChangeWeight = useCallback(
|
||||
(weight: number) => {
|
||||
dispatch(controlLayerWeightChanged({ id: entityIdentifier.id, weight }));
|
||||
dispatch(controlLayerWeightChanged({ entityIdentifier, weight }));
|
||||
},
|
||||
[dispatch, entityIdentifier.id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
|
||||
const onChangeModel = useCallback(
|
||||
(modelConfig: ControlNetModelConfig | T2IAdapterModelConfig) => {
|
||||
dispatch(controlLayerModelChanged({ id: entityIdentifier.id, modelConfig }));
|
||||
dispatch(controlLayerModelChanged({ entityIdentifier, modelConfig }));
|
||||
},
|
||||
[dispatch, entityIdentifier.id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -3,7 +3,7 @@ import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasEntityGroupList } from 'features/controlLayers/components/common/CanvasEntityGroupList';
|
||||
import { ControlLayer } from 'features/controlLayers/components/ControlLayer/ControlLayer';
|
||||
import { mapId } from 'features/controlLayers/konva/util';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
|
||||
const selectEntityIds = createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
|
@ -9,11 +9,11 @@ import { PiLightningBold } from 'react-icons/pi';
|
||||
export const ControlLayerMenuItemsControlToRaster = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const entityIdentifier = useEntityIdentifierContext();
|
||||
const entityIdentifier = useEntityIdentifierContext('control_layer');
|
||||
|
||||
const convertControlLayerToRasterLayer = useCallback(() => {
|
||||
dispatch(controlLayerConvertedToRasterLayer({ id: entityIdentifier.id }));
|
||||
}, [dispatch, entityIdentifier.id]);
|
||||
dispatch(controlLayerConvertedToRasterLayer({ entityIdentifier }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
|
||||
return (
|
||||
<MenuItem onClick={convertControlLayerToRasterLayer} icon={<PiLightningBold />}>
|
||||
|
@ -2,11 +2,8 @@ import { MenuItem } from '@invoke-ai/ui-library';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import {
|
||||
controlLayerWithTransparencyEffectToggled,
|
||||
selectCanvasV2Slice,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectControlLayerEntityOrThrow } from 'features/controlLayers/store/controlLayersReducers';
|
||||
import { controlLayerWithTransparencyEffectToggled } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice, selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiDropHalfBold } from 'react-icons/pi';
|
||||
@ -14,18 +11,18 @@ import { PiDropHalfBold } from 'react-icons/pi';
|
||||
export const ControlLayerMenuItemsTransparencyEffect = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const entityIdentifier = useEntityIdentifierContext();
|
||||
const entityIdentifier = useEntityIdentifierContext('control_layer');
|
||||
const selectWithTransparencyEffect = useMemo(
|
||||
() =>
|
||||
createSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
const entity = selectControlLayerEntityOrThrow(canvasV2, entityIdentifier.id);
|
||||
const entity = selectEntityOrThrow(canvasV2, entityIdentifier);
|
||||
return entity.withTransparencyEffect;
|
||||
}),
|
||||
[entityIdentifier.id]
|
||||
[entityIdentifier]
|
||||
);
|
||||
const withTransparencyEffect = useAppSelector(selectWithTransparencyEffect);
|
||||
const onToggle = useCallback(() => {
|
||||
dispatch(controlLayerWithTransparencyEffectToggled({ id: entityIdentifier.id }));
|
||||
dispatch(controlLayerWithTransparencyEffectToggled({ entityIdentifier }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
|
||||
return (
|
||||
|
@ -4,7 +4,7 @@ import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasEntityGroupList } from 'features/controlLayers/components/common/CanvasEntityGroupList';
|
||||
import { IPAdapter } from 'features/controlLayers/components/IPAdapter/IPAdapter';
|
||||
import { mapId } from 'features/controlLayers/konva/util';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
|
||||
const selectEntityIds = createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
|
@ -13,7 +13,7 @@ import {
|
||||
ipaModelChanged,
|
||||
ipaWeightChanged,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectIPAdapterEntityOrThrow } from 'features/controlLayers/store/ipAdaptersReducers';
|
||||
import { selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import type { CLIPVisionModelV2, IPMethodV2 } from 'features/controlLayers/store/types';
|
||||
import type { IPAImageDropData } from 'features/dnd/types';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
@ -24,53 +24,59 @@ import { IPAdapterModel } from './IPAdapterModel';
|
||||
|
||||
export const IPAdapterSettings = memo(() => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { id } = useEntityIdentifierContext();
|
||||
const ipAdapter = useAppSelector((s) => selectIPAdapterEntityOrThrow(s.canvasV2, id).ipAdapter);
|
||||
const entityIdentifier = useEntityIdentifierContext('ip_adapter');
|
||||
const ipAdapter = useAppSelector((s) => selectEntityOrThrow(s.canvasV2, entityIdentifier).ipAdapter);
|
||||
|
||||
const onChangeBeginEndStepPct = useCallback(
|
||||
(beginEndStepPct: [number, number]) => {
|
||||
dispatch(ipaBeginEndStepPctChanged({ id, beginEndStepPct }));
|
||||
dispatch(ipaBeginEndStepPctChanged({ entityIdentifier, beginEndStepPct }));
|
||||
},
|
||||
[dispatch, id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
|
||||
const onChangeWeight = useCallback(
|
||||
(weight: number) => {
|
||||
dispatch(ipaWeightChanged({ id, weight }));
|
||||
dispatch(ipaWeightChanged({ entityIdentifier, weight }));
|
||||
},
|
||||
[dispatch, id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
|
||||
const onChangeIPMethod = useCallback(
|
||||
(method: IPMethodV2) => {
|
||||
dispatch(ipaMethodChanged({ id, method }));
|
||||
dispatch(ipaMethodChanged({ entityIdentifier, method }));
|
||||
},
|
||||
[dispatch, id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
|
||||
const onChangeModel = useCallback(
|
||||
(modelConfig: IPAdapterModelConfig) => {
|
||||
dispatch(ipaModelChanged({ id, modelConfig }));
|
||||
dispatch(ipaModelChanged({ entityIdentifier, modelConfig }));
|
||||
},
|
||||
[dispatch, id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
|
||||
const onChangeCLIPVisionModel = useCallback(
|
||||
(clipVisionModel: CLIPVisionModelV2) => {
|
||||
dispatch(ipaCLIPVisionModelChanged({ id, clipVisionModel }));
|
||||
dispatch(ipaCLIPVisionModelChanged({ entityIdentifier, clipVisionModel }));
|
||||
},
|
||||
[dispatch, id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
|
||||
const onChangeImage = useCallback(
|
||||
(imageDTO: ImageDTO | null) => {
|
||||
dispatch(ipaImageChanged({ id, imageDTO }));
|
||||
dispatch(ipaImageChanged({ entityIdentifier, imageDTO }));
|
||||
},
|
||||
[dispatch, id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
|
||||
const droppableData = useMemo<IPAImageDropData>(() => ({ actionType: 'SET_IPA_IMAGE', context: { id }, id }), [id]);
|
||||
const postUploadAction = useMemo<IPALayerImagePostUploadAction>(() => ({ type: 'SET_IPA_IMAGE', id }), [id]);
|
||||
const droppableData = useMemo<IPAImageDropData>(
|
||||
() => ({ actionType: 'SET_IPA_IMAGE', context: { id: entityIdentifier.id }, id: entityIdentifier.id }),
|
||||
[entityIdentifier.id]
|
||||
);
|
||||
const postUploadAction = useMemo<IPALayerImagePostUploadAction>(
|
||||
() => ({ type: 'SET_IPA_IMAGE', id: entityIdentifier.id }),
|
||||
[entityIdentifier.id]
|
||||
);
|
||||
|
||||
return (
|
||||
<CanvasEntitySettingsWrapper>
|
||||
@ -95,7 +101,7 @@ export const IPAdapterSettings = memo(() => {
|
||||
<IPAdapterImagePreview
|
||||
image={ipAdapter.image ?? null}
|
||||
onChangeImage={onChangeImage}
|
||||
ipAdapterId={id}
|
||||
ipAdapterId={entityIdentifier.id}
|
||||
droppableData={droppableData}
|
||||
postUploadAction={postUploadAction}
|
||||
/>
|
||||
|
@ -3,7 +3,7 @@ import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasEntityGroupList } from 'features/controlLayers/components/common/CanvasEntityGroupList';
|
||||
import { InpaintMask } from 'features/controlLayers/components/InpaintMask/InpaintMask';
|
||||
import { mapId } from 'features/controlLayers/konva/util';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
|
||||
const selectEntityIds = createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
|
@ -5,7 +5,7 @@ import { rgbColorToString } from 'common/util/colorCodeTransformers';
|
||||
import { MaskFillStyle } from 'features/controlLayers/components/common/MaskFillStyle';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { inpaintMaskFillColorChanged, inpaintMaskFillStyleChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectInpaintMaskEntityOrThrow } from 'features/controlLayers/store/inpaintMaskReducers';
|
||||
import { selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import type { FillStyle, RgbColor } from 'features/controlLayers/store/types';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -13,8 +13,8 @@ import { useTranslation } from 'react-i18next';
|
||||
export const InpaintMaskMaskFillColorPicker = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const entityIdentifier = useEntityIdentifierContext();
|
||||
const fill = useAppSelector((s) => selectInpaintMaskEntityOrThrow(s.canvasV2, entityIdentifier.id).fill);
|
||||
const entityIdentifier = useEntityIdentifierContext('inpaint_mask');
|
||||
const fill = useAppSelector((s) => selectEntityOrThrow(s.canvasV2, entityIdentifier).fill);
|
||||
|
||||
const onChangeFillColor = useCallback(
|
||||
(color: RgbColor) => {
|
||||
|
@ -3,7 +3,7 @@ import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasEntityGroupList } from 'features/controlLayers/components/common/CanvasEntityGroupList';
|
||||
import { RasterLayer } from 'features/controlLayers/components/RasterLayer/RasterLayer';
|
||||
import { mapId } from 'features/controlLayers/konva/util';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
|
||||
const selectEntityIds = createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
|
@ -9,11 +9,11 @@ import { PiLightningBold } from 'react-icons/pi';
|
||||
export const RasterLayerMenuItemsRasterToControl = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const entityIdentifier = useEntityIdentifierContext();
|
||||
const entityIdentifier = useEntityIdentifierContext('raster_layer');
|
||||
|
||||
const convertRasterLayerToControlLayer = useCallback(() => {
|
||||
dispatch(rasterLayerConvertedToControlLayer({ id: entityIdentifier.id }));
|
||||
}, [dispatch, entityIdentifier.id]);
|
||||
dispatch(rasterLayerConvertedToControlLayer({ entityIdentifier }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
|
||||
return (
|
||||
<MenuItem onClick={convertRasterLayerToControlLayer} icon={<PiLightningBold />}>
|
||||
|
@ -1,44 +1,42 @@
|
||||
import { Button, Flex } from '@invoke-ai/ui-library';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import {
|
||||
rgIPAdapterAdded,
|
||||
rgNegativePromptChanged,
|
||||
rgPositivePromptChanged,
|
||||
selectCanvasV2Slice,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice, selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiPlusBold } from 'react-icons/pi';
|
||||
|
||||
type AddPromptButtonProps = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
export const RegionalGuidanceAddPromptsIPAdapterButtons = ({ id }: AddPromptButtonProps) => {
|
||||
export const RegionalGuidanceAddPromptsIPAdapterButtons = () => {
|
||||
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const selectValidActions = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
const rg = canvasV2.regions.entities.find((rg) => rg.id === id);
|
||||
const entity = selectEntityOrThrow(canvasV2, entityIdentifier);
|
||||
return {
|
||||
canAddPositivePrompt: rg?.positivePrompt === null,
|
||||
canAddNegativePrompt: rg?.negativePrompt === null,
|
||||
canAddPositivePrompt: entity?.positivePrompt === null,
|
||||
canAddNegativePrompt: entity?.negativePrompt === null,
|
||||
};
|
||||
}),
|
||||
[id]
|
||||
[entityIdentifier]
|
||||
);
|
||||
const validActions = useAppSelector(selectValidActions);
|
||||
const addPositivePrompt = useCallback(() => {
|
||||
dispatch(rgPositivePromptChanged({ id, prompt: '' }));
|
||||
}, [dispatch, id]);
|
||||
dispatch(rgPositivePromptChanged({ entityIdentifier, prompt: '' }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
const addNegativePrompt = useCallback(() => {
|
||||
dispatch(rgNegativePromptChanged({ id, prompt: '' }));
|
||||
}, [dispatch, id]);
|
||||
dispatch(rgNegativePromptChanged({ entityIdentifier, prompt: '' }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
const addIPAdapter = useCallback(() => {
|
||||
dispatch(rgIPAdapterAdded({ id }));
|
||||
}, [dispatch, id]);
|
||||
dispatch(rgIPAdapterAdded({ entityIdentifier }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
|
||||
return (
|
||||
<Flex w="full" p={2} justifyContent="space-between">
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { Badge } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { selectRegionalGuidanceEntityOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||
import { selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const RegionalGuidanceBadges = memo(() => {
|
||||
const { id } = useEntityIdentifierContext();
|
||||
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
|
||||
const { t } = useTranslation();
|
||||
const autoNegative = useAppSelector((s) => selectRegionalGuidanceEntityOrThrow(s.canvasV2, id).autoNegative);
|
||||
const autoNegative = useAppSelector((s) => selectEntityOrThrow(s.canvasV2, entityIdentifier).autoNegative);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -3,7 +3,7 @@ import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasEntityGroupList } from 'features/controlLayers/components/common/CanvasEntityGroupList';
|
||||
import { RegionalGuidance } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidance';
|
||||
import { mapId } from 'features/controlLayers/konva/util';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
|
||||
const selectEntityIds = createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
|
@ -5,6 +5,7 @@ import { Weight } from 'features/controlLayers/components/common/Weight';
|
||||
import { IPAdapterImagePreview } from 'features/controlLayers/components/IPAdapter/IPAdapterImagePreview';
|
||||
import { IPAdapterMethod } from 'features/controlLayers/components/IPAdapter/IPAdapterMethod';
|
||||
import { IPAdapterModel } from 'features/controlLayers/components/IPAdapter/IPAdapterModel';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import {
|
||||
rgIPAdapterBeginEndStepPctChanged,
|
||||
rgIPAdapterCLIPVisionModelChanged,
|
||||
@ -14,7 +15,7 @@ import {
|
||||
rgIPAdapterModelChanged,
|
||||
rgIPAdapterWeightChanged,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectRegionalGuidanceEntityOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||
import { selectRegionalGuidanceIPAdapter } from 'features/controlLayers/store/selectors';
|
||||
import type { CLIPVisionModelV2, IPMethodV2 } from 'features/controlLayers/store/types';
|
||||
import type { RGIPAdapterImageDropData } from 'features/dnd/types';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
@ -23,71 +24,75 @@ import type { ImageDTO, IPAdapterModelConfig, RGIPAdapterImagePostUploadAction }
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
type Props = {
|
||||
id: string;
|
||||
ipAdapterId: string;
|
||||
ipAdapterNumber: number;
|
||||
};
|
||||
|
||||
export const RegionalGuidanceIPAdapterSettings = memo(({ id, ipAdapterId, ipAdapterNumber }: Props) => {
|
||||
export const RegionalGuidanceIPAdapterSettings = memo(({ ipAdapterId, ipAdapterNumber }: Props) => {
|
||||
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
|
||||
const dispatch = useAppDispatch();
|
||||
const onDeleteIPAdapter = useCallback(() => {
|
||||
dispatch(rgIPAdapterDeleted({ id, ipAdapterId }));
|
||||
}, [dispatch, ipAdapterId, id]);
|
||||
dispatch(rgIPAdapterDeleted({ entityIdentifier, ipAdapterId }));
|
||||
}, [dispatch, entityIdentifier, ipAdapterId]);
|
||||
const ipAdapter = useAppSelector((s) => {
|
||||
const ipa = selectRegionalGuidanceEntityOrThrow(s.canvasV2, id).ipAdapters.find((ipa) => ipa.id === ipAdapterId);
|
||||
const ipa = selectRegionalGuidanceIPAdapter(s.canvasV2, entityIdentifier, ipAdapterId);
|
||||
assert(ipa, `Regional GuidanceIP Adapter with id ${ipAdapterId} not found`);
|
||||
return ipa;
|
||||
});
|
||||
|
||||
const onChangeBeginEndStepPct = useCallback(
|
||||
(beginEndStepPct: [number, number]) => {
|
||||
dispatch(rgIPAdapterBeginEndStepPctChanged({ id, ipAdapterId, beginEndStepPct }));
|
||||
dispatch(rgIPAdapterBeginEndStepPctChanged({ entityIdentifier, ipAdapterId, beginEndStepPct }));
|
||||
},
|
||||
[dispatch, ipAdapterId, id]
|
||||
[dispatch, entityIdentifier, ipAdapterId]
|
||||
);
|
||||
|
||||
const onChangeWeight = useCallback(
|
||||
(weight: number) => {
|
||||
dispatch(rgIPAdapterWeightChanged({ id, ipAdapterId, weight }));
|
||||
dispatch(rgIPAdapterWeightChanged({ entityIdentifier, ipAdapterId, weight }));
|
||||
},
|
||||
[dispatch, ipAdapterId, id]
|
||||
[dispatch, entityIdentifier, ipAdapterId]
|
||||
);
|
||||
|
||||
const onChangeIPMethod = useCallback(
|
||||
(method: IPMethodV2) => {
|
||||
dispatch(rgIPAdapterMethodChanged({ id, ipAdapterId, method }));
|
||||
dispatch(rgIPAdapterMethodChanged({ entityIdentifier, ipAdapterId, method }));
|
||||
},
|
||||
[dispatch, ipAdapterId, id]
|
||||
[dispatch, entityIdentifier, ipAdapterId]
|
||||
);
|
||||
|
||||
const onChangeModel = useCallback(
|
||||
(modelConfig: IPAdapterModelConfig) => {
|
||||
dispatch(rgIPAdapterModelChanged({ id, ipAdapterId, modelConfig }));
|
||||
dispatch(rgIPAdapterModelChanged({ entityIdentifier, ipAdapterId, modelConfig }));
|
||||
},
|
||||
[dispatch, ipAdapterId, id]
|
||||
[dispatch, entityIdentifier, ipAdapterId]
|
||||
);
|
||||
|
||||
const onChangeCLIPVisionModel = useCallback(
|
||||
(clipVisionModel: CLIPVisionModelV2) => {
|
||||
dispatch(rgIPAdapterCLIPVisionModelChanged({ id, ipAdapterId, clipVisionModel }));
|
||||
dispatch(rgIPAdapterCLIPVisionModelChanged({ entityIdentifier, ipAdapterId, clipVisionModel }));
|
||||
},
|
||||
[dispatch, ipAdapterId, id]
|
||||
[dispatch, entityIdentifier, ipAdapterId]
|
||||
);
|
||||
|
||||
const onChangeImage = useCallback(
|
||||
(imageDTO: ImageDTO | null) => {
|
||||
dispatch(rgIPAdapterImageChanged({ id, ipAdapterId, imageDTO }));
|
||||
dispatch(rgIPAdapterImageChanged({ entityIdentifier, ipAdapterId, imageDTO }));
|
||||
},
|
||||
[dispatch, ipAdapterId, id]
|
||||
[dispatch, entityIdentifier, ipAdapterId]
|
||||
);
|
||||
|
||||
const droppableData = useMemo<RGIPAdapterImageDropData>(
|
||||
() => ({ actionType: 'SET_RG_IP_ADAPTER_IMAGE', context: { id, ipAdapterId }, id }),
|
||||
[ipAdapterId, id]
|
||||
() => ({
|
||||
actionType: 'SET_RG_IP_ADAPTER_IMAGE',
|
||||
context: { id: entityIdentifier.id, ipAdapterId },
|
||||
id: entityIdentifier.id,
|
||||
}),
|
||||
[entityIdentifier.id, ipAdapterId]
|
||||
);
|
||||
const postUploadAction = useMemo<RGIPAdapterImagePostUploadAction>(
|
||||
() => ({ type: 'SET_RG_IP_ADAPTER_IMAGE', id, ipAdapterId }),
|
||||
[ipAdapterId, id]
|
||||
() => ({ type: 'SET_RG_IP_ADAPTER_IMAGE', id: entityIdentifier.id, ipAdapterId }),
|
||||
[entityIdentifier.id, ipAdapterId]
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -3,25 +3,23 @@ import { EMPTY_ARRAY } from 'app/store/constants';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { RegionalGuidanceIPAdapterSettings } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceIPAdapterSettings';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectRegionalGuidanceEntityOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { selectCanvasV2Slice, selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { Fragment, memo, useMemo } from 'react';
|
||||
|
||||
type Props = {
|
||||
id: string;
|
||||
};
|
||||
export const RegionalGuidanceIPAdapters = memo(() => {
|
||||
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
|
||||
|
||||
export const RegionalGuidanceIPAdapters = memo(({ id }: Props) => {
|
||||
const selectIPAdapterIds = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
const ipAdapterIds = selectRegionalGuidanceEntityOrThrow(canvasV2, id).ipAdapters.map(({ id }) => id);
|
||||
const ipAdapterIds = selectEntityOrThrow(canvasV2, entityIdentifier).ipAdapters.map(({ id }) => id);
|
||||
if (ipAdapterIds.length === 0) {
|
||||
return EMPTY_ARRAY;
|
||||
}
|
||||
return ipAdapterIds;
|
||||
}),
|
||||
[id]
|
||||
[entityIdentifier]
|
||||
);
|
||||
|
||||
const ipAdapterIds = useAppSelector(selectIPAdapterIds);
|
||||
@ -35,7 +33,7 @@ export const RegionalGuidanceIPAdapters = memo(({ id }: Props) => {
|
||||
{ipAdapterIds.map((ipAdapterId, index) => (
|
||||
<Fragment key={ipAdapterId}>
|
||||
{index > 0 && <Divider />}
|
||||
<RegionalGuidanceIPAdapterSettings id={id} ipAdapterId={ipAdapterId} ipAdapterNumber={index + 1} />
|
||||
<RegionalGuidanceIPAdapterSettings ipAdapterId={ipAdapterId} ipAdapterNumber={index + 1} />
|
||||
</Fragment>
|
||||
))}
|
||||
</>
|
||||
|
@ -5,27 +5,27 @@ import { rgbColorToString } from 'common/util/colorCodeTransformers';
|
||||
import { MaskFillStyle } from 'features/controlLayers/components/common/MaskFillStyle';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { rgFillColorChanged, rgFillStyleChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectRegionalGuidanceEntityOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||
import { selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import type { FillStyle, RgbColor } from 'features/controlLayers/store/types';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const RegionalGuidanceMaskFillColorPicker = memo(() => {
|
||||
const entityIdentifier = useEntityIdentifierContext();
|
||||
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const fill = useAppSelector((s) => selectRegionalGuidanceEntityOrThrow(s.canvasV2, entityIdentifier.id).fill);
|
||||
const fill = useAppSelector((s) => selectEntityOrThrow(s.canvasV2, entityIdentifier).fill);
|
||||
const onChangeFillColor = useCallback(
|
||||
(color: RgbColor) => {
|
||||
dispatch(rgFillColorChanged({ id: entityIdentifier.id, color }));
|
||||
dispatch(rgFillColorChanged({ entityIdentifier, color }));
|
||||
},
|
||||
[dispatch, entityIdentifier.id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
const onChangeFillStyle = useCallback(
|
||||
(style: FillStyle) => {
|
||||
dispatch(rgFillStyleChanged({ id: entityIdentifier.id, style }));
|
||||
dispatch(rgFillStyleChanged({ entityIdentifier, style }));
|
||||
},
|
||||
[dispatch, entityIdentifier.id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
return (
|
||||
<Popover isLazy>
|
||||
|
@ -6,36 +6,36 @@ import {
|
||||
rgIPAdapterAdded,
|
||||
rgNegativePromptChanged,
|
||||
rgPositivePromptChanged,
|
||||
selectCanvasV2Slice,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const RegionalGuidanceMenuItemsAddPromptsAndIPAdapter = memo(() => {
|
||||
const { id } = useEntityIdentifierContext();
|
||||
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const selectValidActions = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
const rg = canvasV2.regions.entities.find((rg) => rg.id === id);
|
||||
const entity = selectEntity(canvasV2, entityIdentifier);
|
||||
return {
|
||||
canAddPositivePrompt: rg?.positivePrompt === null,
|
||||
canAddNegativePrompt: rg?.negativePrompt === null,
|
||||
canAddPositivePrompt: entity?.positivePrompt === null,
|
||||
canAddNegativePrompt: entity?.negativePrompt === null,
|
||||
};
|
||||
}),
|
||||
[id]
|
||||
[entityIdentifier]
|
||||
);
|
||||
const validActions = useAppSelector(selectValidActions);
|
||||
const addPositivePrompt = useCallback(() => {
|
||||
dispatch(rgPositivePromptChanged({ id: id, prompt: '' }));
|
||||
}, [dispatch, id]);
|
||||
dispatch(rgPositivePromptChanged({ entityIdentifier, prompt: '' }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
const addNegativePrompt = useCallback(() => {
|
||||
dispatch(rgNegativePromptChanged({ id: id, prompt: '' }));
|
||||
}, [dispatch, id]);
|
||||
dispatch(rgNegativePromptChanged({ entityIdentifier, prompt: '' }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
const addIPAdapter = useCallback(() => {
|
||||
dispatch(rgIPAdapterAdded({ id }));
|
||||
}, [dispatch, id]);
|
||||
dispatch(rgIPAdapterAdded({ entityIdentifier }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -2,19 +2,19 @@ import { MenuItem } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { rgAutoNegativeToggled } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectRegionalGuidanceEntityOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||
import { selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiSelectionInverseBold } from 'react-icons/pi';
|
||||
|
||||
export const RegionalGuidanceMenuItemsAutoNegative = memo(() => {
|
||||
const { id } = useEntityIdentifierContext();
|
||||
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const autoNegative = useAppSelector((s) => selectRegionalGuidanceEntityOrThrow(s.canvasV2, id).autoNegative);
|
||||
const autoNegative = useAppSelector((s) => selectEntityOrThrow(s.canvasV2, entityIdentifier).autoNegative);
|
||||
const onClick = useCallback(() => {
|
||||
dispatch(rgAutoNegativeToggled({ id }));
|
||||
}, [dispatch, id]);
|
||||
dispatch(rgAutoNegativeToggled({ entityIdentifier }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
|
||||
return (
|
||||
<MenuItem icon={<PiSelectionInverseBold />} onClick={onClick}>
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { Box, Textarea } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { RegionalGuidanceDeletePromptButton } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceDeletePromptButton';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { rgNegativePromptChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectRegionalGuidanceEntityOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||
import { selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
||||
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
|
||||
import { PromptPopover } from 'features/prompt/PromptPopover';
|
||||
@ -10,24 +11,21 @@ import { usePrompt } from 'features/prompt/usePrompt';
|
||||
import { memo, useCallback, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
type Props = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
export const RegionalGuidanceNegativePrompt = memo(({ id }: Props) => {
|
||||
const prompt = useAppSelector((s) => selectRegionalGuidanceEntityOrThrow(s.canvasV2, id).negativePrompt ?? '');
|
||||
export const RegionalGuidanceNegativePrompt = memo(() => {
|
||||
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
|
||||
const prompt = useAppSelector((s) => selectEntityOrThrow(s.canvasV2, entityIdentifier).negativePrompt ?? '');
|
||||
const dispatch = useAppDispatch();
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const { t } = useTranslation();
|
||||
const _onChange = useCallback(
|
||||
(v: string) => {
|
||||
dispatch(rgNegativePromptChanged({ id, prompt: v }));
|
||||
dispatch(rgNegativePromptChanged({ entityIdentifier, prompt: v }));
|
||||
},
|
||||
[dispatch, id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
const onDeletePrompt = useCallback(() => {
|
||||
dispatch(rgNegativePromptChanged({ id, prompt: null }));
|
||||
}, [dispatch, id]);
|
||||
dispatch(rgNegativePromptChanged({ entityIdentifier, prompt: null }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
const { onChange, isOpen, onClose, onOpen, onSelect, onKeyDown } = usePrompt({
|
||||
prompt,
|
||||
textareaRef,
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { Box, Textarea } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { RegionalGuidanceDeletePromptButton } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceDeletePromptButton';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { rgPositivePromptChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectRegionalGuidanceEntityOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||
import { selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
||||
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
|
||||
import { PromptPopover } from 'features/prompt/PromptPopover';
|
||||
@ -10,24 +11,21 @@ import { usePrompt } from 'features/prompt/usePrompt';
|
||||
import { memo, useCallback, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
type Props = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
export const RegionalGuidancePositivePrompt = memo(({ id }: Props) => {
|
||||
const prompt = useAppSelector((s) => selectRegionalGuidanceEntityOrThrow(s.canvasV2, id).positivePrompt ?? '');
|
||||
export const RegionalGuidancePositivePrompt = memo(() => {
|
||||
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
|
||||
const prompt = useAppSelector((s) => selectEntityOrThrow(s.canvasV2, entityIdentifier).positivePrompt ?? '');
|
||||
const dispatch = useAppDispatch();
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const { t } = useTranslation();
|
||||
const _onChange = useCallback(
|
||||
(v: string) => {
|
||||
dispatch(rgPositivePromptChanged({ id, prompt: v }));
|
||||
dispatch(rgPositivePromptChanged({ entityIdentifier, prompt: v }));
|
||||
},
|
||||
[dispatch, id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
const onDeletePrompt = useCallback(() => {
|
||||
dispatch(rgPositivePromptChanged({ id, prompt: null }));
|
||||
}, [dispatch, id]);
|
||||
dispatch(rgPositivePromptChanged({ entityIdentifier, prompt: null }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
const { onChange, isOpen, onClose, onOpen, onSelect, onKeyDown } = usePrompt({
|
||||
prompt,
|
||||
textareaRef,
|
||||
|
@ -3,7 +3,7 @@ import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasEntitySettingsWrapper } from 'features/controlLayers/components/common/CanvasEntitySettingsWrapper';
|
||||
import { RegionalGuidanceAddPromptsIPAdapterButtons } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceAddPromptsIPAdapterButtons';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { selectRegionalGuidanceEntityOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||
import { selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
|
||||
import { RegionalGuidanceIPAdapters } from './RegionalGuidanceIPAdapters';
|
||||
@ -11,35 +11,31 @@ import { RegionalGuidanceNegativePrompt } from './RegionalGuidanceNegativePrompt
|
||||
import { RegionalGuidancePositivePrompt } from './RegionalGuidancePositivePrompt';
|
||||
|
||||
export const RegionalGuidanceSettings = memo(() => {
|
||||
const { id } = useEntityIdentifierContext();
|
||||
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
|
||||
const hasPositivePrompt = useAppSelector(
|
||||
(s) => selectRegionalGuidanceEntityOrThrow(s.canvasV2, id).positivePrompt !== null
|
||||
(s) => selectEntityOrThrow(s.canvasV2, entityIdentifier).positivePrompt !== null
|
||||
);
|
||||
const hasNegativePrompt = useAppSelector(
|
||||
(s) => selectRegionalGuidanceEntityOrThrow(s.canvasV2, id).negativePrompt !== null
|
||||
);
|
||||
const hasIPAdapters = useAppSelector(
|
||||
(s) => selectRegionalGuidanceEntityOrThrow(s.canvasV2, id).ipAdapters.length > 0
|
||||
(s) => selectEntityOrThrow(s.canvasV2, entityIdentifier).negativePrompt !== null
|
||||
);
|
||||
const hasIPAdapters = useAppSelector((s) => selectEntityOrThrow(s.canvasV2, entityIdentifier).ipAdapters.length > 0);
|
||||
|
||||
return (
|
||||
<CanvasEntitySettingsWrapper>
|
||||
{!hasPositivePrompt && !hasNegativePrompt && !hasIPAdapters && (
|
||||
<RegionalGuidanceAddPromptsIPAdapterButtons id={id} />
|
||||
)}
|
||||
{!hasPositivePrompt && !hasNegativePrompt && !hasIPAdapters && <RegionalGuidanceAddPromptsIPAdapterButtons />}
|
||||
{hasPositivePrompt && (
|
||||
<>
|
||||
<RegionalGuidancePositivePrompt id={id} />
|
||||
<RegionalGuidancePositivePrompt />
|
||||
{(hasNegativePrompt || hasIPAdapters) && <Divider />}
|
||||
</>
|
||||
)}
|
||||
{hasNegativePrompt && (
|
||||
<>
|
||||
<RegionalGuidanceNegativePrompt id={id} />
|
||||
<RegionalGuidanceNegativePrompt />
|
||||
{hasIPAdapters && <Divider />}
|
||||
</>
|
||||
)}
|
||||
{hasIPAdapters && <RegionalGuidanceIPAdapters id={id} />}
|
||||
{hasIPAdapters && <RegionalGuidanceIPAdapters />}
|
||||
</CanvasEntitySettingsWrapper>
|
||||
);
|
||||
});
|
||||
|
@ -7,8 +7,8 @@ import {
|
||||
entityArrangedForwardOne,
|
||||
entityArrangedToBack,
|
||||
entityArrangedToFront,
|
||||
selectCanvasV2Slice,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import type { CanvasEntityIdentifier, CanvasV2State } from 'features/controlLayers/store/types';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
@ -15,7 +15,8 @@ import {
|
||||
} from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { snapToNearest } from 'features/controlLayers/konva/util';
|
||||
import { entityOpacityChanged, selectEntity } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { entityOpacityChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import { isDrawableEntity } from 'features/controlLayers/store/types';
|
||||
import { clamp, round } from 'lodash-es';
|
||||
import type { KeyboardEvent } from 'react';
|
||||
|
@ -5,7 +5,7 @@ import { rgbColorToString } from 'common/util/colorCodeTransformers';
|
||||
import { useEntityAdapter } from 'features/controlLayers/contexts/EntityAdapterContext';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { TRANSPARENCY_CHECKER_PATTERN } from 'features/controlLayers/konva/constants';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import { memo, useEffect, useMemo, useRef } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
|
@ -1,11 +1,16 @@
|
||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import type { CanvasEntityIdentifier, CanvasEntityType } from 'features/controlLayers/store/types';
|
||||
import { createContext, useContext } from 'react';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
export const EntityIdentifierContext = createContext<CanvasEntityIdentifier | null>(null);
|
||||
|
||||
export const useEntityIdentifierContext = (): CanvasEntityIdentifier => {
|
||||
export const useEntityIdentifierContext = <T extends CanvasEntityType | undefined = CanvasEntityType>(
|
||||
type?: T
|
||||
): CanvasEntityIdentifier<T extends undefined ? CanvasEntityType : T> => {
|
||||
const entityIdentifier = useContext(EntityIdentifierContext);
|
||||
assert(entityIdentifier, 'useEntityIdentifier must be used within a EntityIdentifierProvider');
|
||||
return entityIdentifier;
|
||||
if (type) {
|
||||
assert(entityIdentifier.type === type, 'useEntityIdentifier must be used with the correct type');
|
||||
}
|
||||
return entityIdentifier as CanvasEntityIdentifier<T extends undefined ? CanvasEntityType : T>;
|
||||
};
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
|
||||
import { entityDeleted, selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { entityDeleted } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
|
||||
import { entityReset, selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { entityReset } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import { type CanvasEntityIdentifier, isDrawableEntity } from 'features/controlLayers/store/types';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { rgbColorToString } from 'common/util/colorCodeTransformers';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useEntityObjectCount } from 'features/controlLayers/hooks/useEntityObjectCount';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { createMemoizedAppSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectControlLayerEntityOrThrow } from 'features/controlLayers/store/controlLayersReducers';
|
||||
import { selectCanvasV2Slice, selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import type {
|
||||
CanvasEntityIdentifier,
|
||||
ControlNetConfig,
|
||||
@ -14,11 +13,11 @@ import { zModelIdentifierField } from 'features/nodes/types/common';
|
||||
import { useMemo } from 'react';
|
||||
import { useControlNetAndT2IAdapterModels, useIPAdapterModels } from 'services/api/hooks/modelsByType';
|
||||
|
||||
export const useControlLayerControlAdapter = (entityIdentifier: CanvasEntityIdentifier) => {
|
||||
export const useControlLayerControlAdapter = (entityIdentifier: CanvasEntityIdentifier<'control_layer'>) => {
|
||||
const selectControlAdapter = useMemo(
|
||||
() =>
|
||||
createMemoizedAppSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
const layer = selectControlLayerEntityOrThrow(canvasV2, entityIdentifier.id);
|
||||
const layer = selectEntityOrThrow(canvasV2, entityIdentifier);
|
||||
return layer.controlAdapter;
|
||||
}),
|
||||
[entityIdentifier]
|
||||
|
@ -25,10 +25,10 @@ import {
|
||||
entitySelected,
|
||||
eraserWidthChanged,
|
||||
fillChanged,
|
||||
selectAllRenderableEntities,
|
||||
toolBufferChanged,
|
||||
toolChanged,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectAllRenderableEntities } from 'features/controlLayers/store/selectors';
|
||||
import type {
|
||||
CanvasControlLayerState,
|
||||
CanvasEntityIdentifier,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { createAction, createSlice } from '@reduxjs/toolkit';
|
||||
import type { PersistConfig, RootState } from 'app/store/store';
|
||||
import type { PersistConfig } from 'app/store/store';
|
||||
import { moveOneToEnd, moveOneToStart, moveToEnd, moveToStart } from 'common/util/arrayUtils';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
@ -13,6 +13,7 @@ import { lorasReducers } from 'features/controlLayers/store/lorasReducers';
|
||||
import { paramsReducers } from 'features/controlLayers/store/paramsReducers';
|
||||
import { rasterLayersReducers } from 'features/controlLayers/store/rasterLayersReducers';
|
||||
import { regionsReducers } from 'features/controlLayers/store/regionsReducers';
|
||||
import { selectAllEntities, selectAllEntitiesOfType, selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import { sessionReducers } from 'features/controlLayers/store/sessionReducers';
|
||||
import { settingsReducers } from 'features/controlLayers/store/settingsReducers';
|
||||
import { toolReducers } from 'features/controlLayers/store/toolReducers';
|
||||
@ -25,12 +26,7 @@ import { atom } from 'nanostores';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
import type {
|
||||
CanvasControlLayerState,
|
||||
CanvasEntityIdentifier,
|
||||
CanvasEntityState,
|
||||
CanvasInpaintMaskState,
|
||||
CanvasRasterLayerState,
|
||||
CanvasRegionalGuidanceState,
|
||||
CanvasV2State,
|
||||
Coordinate,
|
||||
EntityBrushLineAddedPayload,
|
||||
@ -143,60 +139,6 @@ const initialState: CanvasV2State = {
|
||||
},
|
||||
};
|
||||
|
||||
export function selectEntity(state: CanvasV2State, { id, type }: CanvasEntityIdentifier) {
|
||||
switch (type) {
|
||||
case 'raster_layer':
|
||||
return state.rasterLayers.entities.find((entity) => entity.id === id);
|
||||
case 'control_layer':
|
||||
return state.controlLayers.entities.find((entity) => entity.id === id);
|
||||
case 'inpaint_mask':
|
||||
return state.inpaintMasks.entities.find((entity) => entity.id === id);
|
||||
case 'regional_guidance':
|
||||
return state.regions.entities.find((entity) => entity.id === id);
|
||||
case 'ip_adapter':
|
||||
return state.ipAdapters.entities.find((entity) => entity.id === id);
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function selectAllEntitiesOfType(state: CanvasV2State, type: CanvasEntityState['type']): CanvasEntityState[] {
|
||||
switch (type) {
|
||||
case 'raster_layer':
|
||||
return state.rasterLayers.entities;
|
||||
case 'control_layer':
|
||||
return state.controlLayers.entities;
|
||||
case 'inpaint_mask':
|
||||
return state.inpaintMasks.entities;
|
||||
case 'regional_guidance':
|
||||
return state.regions.entities;
|
||||
case 'ip_adapter':
|
||||
return state.ipAdapters.entities;
|
||||
}
|
||||
}
|
||||
|
||||
function selectAllEntities(state: CanvasV2State): CanvasEntityState[] {
|
||||
// These are in the same order as they are displayed in the list!
|
||||
return [
|
||||
...state.inpaintMasks.entities.toReversed(),
|
||||
...state.regions.entities.toReversed(),
|
||||
...state.ipAdapters.entities.toReversed(),
|
||||
...state.controlLayers.entities.toReversed(),
|
||||
...state.rasterLayers.entities.toReversed(),
|
||||
];
|
||||
}
|
||||
|
||||
export function selectAllRenderableEntities(
|
||||
state: CanvasV2State
|
||||
): (CanvasRasterLayerState | CanvasControlLayerState | CanvasInpaintMaskState | CanvasRegionalGuidanceState)[] {
|
||||
return [
|
||||
...state.rasterLayers.entities,
|
||||
...state.controlLayers.entities,
|
||||
...state.inpaintMasks.entities,
|
||||
...state.regions.entities,
|
||||
];
|
||||
}
|
||||
|
||||
export const canvasV2Slice = createSlice({
|
||||
name: 'canvasV2',
|
||||
initialState,
|
||||
@ -217,7 +159,7 @@ export const canvasV2Slice = createSlice({
|
||||
const { entityIdentifier } = action.payload;
|
||||
state.selectedEntityIdentifier = entityIdentifier;
|
||||
},
|
||||
entityNameChanged: (state, action: PayloadAction<EntityIdentifierPayload & { name: string | null }>) => {
|
||||
entityNameChanged: (state, action: PayloadAction<EntityIdentifierPayload<{ name: string | null }>>) => {
|
||||
const { entityIdentifier, name } = action.payload;
|
||||
const entity = selectEntity(state, entityIdentifier);
|
||||
if (!entity) {
|
||||
@ -617,8 +559,6 @@ export const {
|
||||
sessionModeChanged,
|
||||
} = canvasV2Slice.actions;
|
||||
|
||||
export const selectCanvasV2Slice = (state: RootState) => state.canvasV2;
|
||||
|
||||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||
const migrate = (state: any): any => {
|
||||
return state;
|
||||
|
@ -1,10 +1,10 @@
|
||||
import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import { selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import { zModelIdentifierField } from 'features/nodes/types/common';
|
||||
import { merge, omit } from 'lodash-es';
|
||||
import type { ControlNetModelConfig, T2IAdapterModelConfig } from 'services/api/types';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
import type {
|
||||
CanvasControlLayerState,
|
||||
@ -12,18 +12,11 @@ import type {
|
||||
CanvasV2State,
|
||||
ControlModeV2,
|
||||
ControlNetConfig,
|
||||
EntityIdentifierPayload,
|
||||
T2IAdapterConfig,
|
||||
} from './types';
|
||||
import { getEntityIdentifier, initialControlNet } from './types';
|
||||
|
||||
const selectControlLayerEntity = (state: CanvasV2State, id: string) =>
|
||||
state.controlLayers.entities.find((entity) => entity.id === id);
|
||||
export const selectControlLayerEntityOrThrow = (state: CanvasV2State, id: string) => {
|
||||
const layer = selectControlLayerEntity(state, id);
|
||||
assert(layer, `Layer with id ${id} not found`);
|
||||
return layer;
|
||||
};
|
||||
|
||||
export const controlLayersReducers = {
|
||||
controlLayerAdded: {
|
||||
reducer: (
|
||||
@ -58,9 +51,9 @@ export const controlLayersReducers = {
|
||||
state.selectedEntityIdentifier = { type: 'control_layer', id: data.id };
|
||||
},
|
||||
controlLayerConvertedToRasterLayer: {
|
||||
reducer: (state, action: PayloadAction<{ id: string; newId: string }>) => {
|
||||
const { id, newId } = action.payload;
|
||||
const layer = selectControlLayerEntity(state, id);
|
||||
reducer: (state, action: PayloadAction<EntityIdentifierPayload<{ newId: string }, 'control_layer'>>) => {
|
||||
const { entityIdentifier, newId } = action.payload;
|
||||
const layer = selectEntity(state, entityIdentifier);
|
||||
if (!layer) {
|
||||
return;
|
||||
}
|
||||
@ -73,26 +66,30 @@ export const controlLayersReducers = {
|
||||
};
|
||||
|
||||
// Remove the control layer
|
||||
state.controlLayers.entities = state.controlLayers.entities.filter((layer) => layer.id !== id);
|
||||
state.controlLayers.entities = state.controlLayers.entities.filter((layer) => layer.id !== entityIdentifier.id);
|
||||
|
||||
// Add the new raster layer
|
||||
state.rasterLayers.entities.push(rasterLayerState);
|
||||
|
||||
state.selectedEntityIdentifier = { type: rasterLayerState.type, id: rasterLayerState.id };
|
||||
},
|
||||
prepare: (payload: { id: string }) => ({
|
||||
prepare: (payload: EntityIdentifierPayload<void, 'control_layer'>) => ({
|
||||
payload: { ...payload, newId: getPrefixedId('raster_layer') },
|
||||
}),
|
||||
},
|
||||
controlLayerModelChanged: (
|
||||
state,
|
||||
action: PayloadAction<{
|
||||
id: string;
|
||||
action: PayloadAction<
|
||||
EntityIdentifierPayload<
|
||||
{
|
||||
modelConfig: ControlNetModelConfig | T2IAdapterModelConfig | null;
|
||||
}>
|
||||
},
|
||||
'control_layer'
|
||||
>
|
||||
>
|
||||
) => {
|
||||
const { id, modelConfig } = action.payload;
|
||||
const layer = selectControlLayerEntity(state, id);
|
||||
const { entityIdentifier, modelConfig } = action.payload;
|
||||
const layer = selectEntity(state, entityIdentifier);
|
||||
if (!layer || !layer.controlAdapter) {
|
||||
return;
|
||||
}
|
||||
@ -118,17 +115,23 @@ export const controlLayersReducers = {
|
||||
layer.controlAdapter = t2iAdapterConfig;
|
||||
}
|
||||
},
|
||||
controlLayerControlModeChanged: (state, action: PayloadAction<{ id: string; controlMode: ControlModeV2 }>) => {
|
||||
const { id, controlMode } = action.payload;
|
||||
const layer = selectControlLayerEntity(state, id);
|
||||
controlLayerControlModeChanged: (
|
||||
state,
|
||||
action: PayloadAction<EntityIdentifierPayload<{ controlMode: ControlModeV2 }, 'control_layer'>>
|
||||
) => {
|
||||
const { entityIdentifier, controlMode } = action.payload;
|
||||
const layer = selectEntity(state, entityIdentifier);
|
||||
if (!layer || !layer.controlAdapter || layer.controlAdapter.type !== 'controlnet') {
|
||||
return;
|
||||
}
|
||||
layer.controlAdapter.controlMode = controlMode;
|
||||
},
|
||||
controlLayerWeightChanged: (state, action: PayloadAction<{ id: string; weight: number }>) => {
|
||||
const { id, weight } = action.payload;
|
||||
const layer = selectControlLayerEntity(state, id);
|
||||
controlLayerWeightChanged: (
|
||||
state,
|
||||
action: PayloadAction<EntityIdentifierPayload<{ weight: number }, 'control_layer'>>
|
||||
) => {
|
||||
const { entityIdentifier, weight } = action.payload;
|
||||
const layer = selectEntity(state, entityIdentifier);
|
||||
if (!layer || !layer.controlAdapter) {
|
||||
return;
|
||||
}
|
||||
@ -136,18 +139,21 @@ export const controlLayersReducers = {
|
||||
},
|
||||
controlLayerBeginEndStepPctChanged: (
|
||||
state,
|
||||
action: PayloadAction<{ id: string; beginEndStepPct: [number, number] }>
|
||||
action: PayloadAction<EntityIdentifierPayload<{ beginEndStepPct: [number, number] }, 'control_layer'>>
|
||||
) => {
|
||||
const { id, beginEndStepPct } = action.payload;
|
||||
const layer = selectControlLayerEntity(state, id);
|
||||
const { entityIdentifier, beginEndStepPct } = action.payload;
|
||||
const layer = selectEntity(state, entityIdentifier);
|
||||
if (!layer || !layer.controlAdapter) {
|
||||
return;
|
||||
}
|
||||
layer.controlAdapter.beginEndStepPct = beginEndStepPct;
|
||||
},
|
||||
controlLayerWithTransparencyEffectToggled: (state, action: PayloadAction<{ id: string }>) => {
|
||||
const { id } = action.payload;
|
||||
const layer = selectControlLayerEntity(state, id);
|
||||
controlLayerWithTransparencyEffectToggled: (
|
||||
state,
|
||||
action: PayloadAction<EntityIdentifierPayload<void, 'control_layer'>>
|
||||
) => {
|
||||
const { entityIdentifier } = action.payload;
|
||||
const layer = selectEntity(state, entityIdentifier);
|
||||
if (!layer) {
|
||||
return;
|
||||
}
|
||||
|
@ -1,23 +1,15 @@
|
||||
import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import {
|
||||
type CanvasInpaintMaskState,
|
||||
type CanvasV2State,
|
||||
type EntityIdentifierPayload,
|
||||
type FillStyle,
|
||||
getEntityIdentifier,
|
||||
type RgbColor,
|
||||
import { selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import type {
|
||||
CanvasInpaintMaskState,
|
||||
CanvasV2State,
|
||||
EntityIdentifierPayload,
|
||||
FillStyle,
|
||||
RgbColor,
|
||||
} from 'features/controlLayers/store/types';
|
||||
import { getEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { merge } from 'lodash-es';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
const selectInpaintMaskEntity = (state: CanvasV2State, id: string) =>
|
||||
state.inpaintMasks.entities.find((layer) => layer.id === id);
|
||||
export const selectInpaintMaskEntityOrThrow = (state: CanvasV2State, id: string) => {
|
||||
const entity = selectInpaintMaskEntity(state, id);
|
||||
assert(entity, `Inpaint mask with id ${id} not found`);
|
||||
return entity;
|
||||
};
|
||||
|
||||
export const inpaintMaskReducers = {
|
||||
inpaintMaskAdded: {
|
||||
@ -54,17 +46,23 @@ export const inpaintMaskReducers = {
|
||||
state.inpaintMasks.entities = [data];
|
||||
state.selectedEntityIdentifier = { type: 'inpaint_mask', id: data.id };
|
||||
},
|
||||
inpaintMaskFillColorChanged: (state, action: PayloadAction<EntityIdentifierPayload<{ color: RgbColor }>>) => {
|
||||
inpaintMaskFillColorChanged: (
|
||||
state,
|
||||
action: PayloadAction<EntityIdentifierPayload<{ color: RgbColor }, 'inpaint_mask'>>
|
||||
) => {
|
||||
const { color, entityIdentifier } = action.payload;
|
||||
const entity = selectInpaintMaskEntity(state, entityIdentifier.id);
|
||||
const entity = selectEntity(state, entityIdentifier);
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
entity.fill.color = color;
|
||||
},
|
||||
inpaintMaskFillStyleChanged: (state, action: PayloadAction<EntityIdentifierPayload<{ style: FillStyle }>>) => {
|
||||
inpaintMaskFillStyleChanged: (
|
||||
state,
|
||||
action: PayloadAction<EntityIdentifierPayload<{ style: FillStyle }, 'inpaint_mask'>>
|
||||
) => {
|
||||
const { style, entityIdentifier } = action.payload;
|
||||
const entity = selectInpaintMaskEntity(state, entityIdentifier.id);
|
||||
const entity = selectEntity(state, entityIdentifier);
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
|
@ -1,23 +1,20 @@
|
||||
import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import { selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import { zModelIdentifierField } from 'features/nodes/types/common';
|
||||
import { merge } from 'lodash-es';
|
||||
import type { ImageDTO, IPAdapterModelConfig } from 'services/api/types';
|
||||
import { assert } from 'tsafe';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import type { CanvasIPAdapterState, CanvasV2State, CLIPVisionModelV2, IPMethodV2 } from './types';
|
||||
import type {
|
||||
CanvasIPAdapterState,
|
||||
CanvasV2State,
|
||||
CLIPVisionModelV2,
|
||||
EntityIdentifierPayload,
|
||||
IPMethodV2,
|
||||
} from './types';
|
||||
import { getEntityIdentifier, imageDTOToImageWithDims, initialIPAdapter } from './types';
|
||||
|
||||
const selectIPAdapterEntity = (state: CanvasV2State, id: string) =>
|
||||
state.ipAdapters.entities.find((ipa) => ipa.id === id);
|
||||
export const selectIPAdapterEntityOrThrow = (state: CanvasV2State, id: string) => {
|
||||
const entity = selectIPAdapterEntity(state, id);
|
||||
assert(entity, `IP Adapter with id ${id} not found`);
|
||||
return entity;
|
||||
};
|
||||
|
||||
export const ipAdaptersReducers = {
|
||||
ipaAdded: {
|
||||
reducer: (
|
||||
@ -47,52 +44,61 @@ export const ipAdaptersReducers = {
|
||||
state.ipAdapters.entities.push(data);
|
||||
state.selectedEntityIdentifier = { type: 'ip_adapter', id: data.id };
|
||||
},
|
||||
ipaImageChanged: {
|
||||
reducer: (state, action: PayloadAction<{ id: string; imageDTO: ImageDTO | null }>) => {
|
||||
const { id, imageDTO } = action.payload;
|
||||
const entity = selectIPAdapterEntity(state, id);
|
||||
ipaImageChanged: (
|
||||
state,
|
||||
action: PayloadAction<EntityIdentifierPayload<{ imageDTO: ImageDTO | null }, 'ip_adapter'>>
|
||||
) => {
|
||||
const { entityIdentifier, imageDTO } = action.payload;
|
||||
const entity = selectEntity(state, entityIdentifier);
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
entity.ipAdapter.image = imageDTO ? imageDTOToImageWithDims(imageDTO) : null;
|
||||
},
|
||||
prepare: (payload: { id: string; imageDTO: ImageDTO | null }) => ({ payload: { ...payload, objectId: uuidv4() } }),
|
||||
},
|
||||
ipaMethodChanged: (state, action: PayloadAction<{ id: string; method: IPMethodV2 }>) => {
|
||||
const { id, method } = action.payload;
|
||||
const entity = selectIPAdapterEntity(state, id);
|
||||
ipaMethodChanged: (state, action: PayloadAction<EntityIdentifierPayload<{ method: IPMethodV2 }, 'ip_adapter'>>) => {
|
||||
const { entityIdentifier, method } = action.payload;
|
||||
const entity = selectEntity(state, entityIdentifier);
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
entity.ipAdapter.method = method;
|
||||
},
|
||||
ipaModelChanged: (state, action: PayloadAction<{ id: string; modelConfig: IPAdapterModelConfig | null }>) => {
|
||||
const { id, modelConfig } = action.payload;
|
||||
const entity = selectIPAdapterEntity(state, id);
|
||||
ipaModelChanged: (
|
||||
state,
|
||||
action: PayloadAction<EntityIdentifierPayload<{ modelConfig: IPAdapterModelConfig | null }, 'ip_adapter'>>
|
||||
) => {
|
||||
const { entityIdentifier, modelConfig } = action.payload;
|
||||
const entity = selectEntity(state, entityIdentifier);
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
entity.ipAdapter.model = modelConfig ? zModelIdentifierField.parse(modelConfig) : null;
|
||||
},
|
||||
ipaCLIPVisionModelChanged: (state, action: PayloadAction<{ id: string; clipVisionModel: CLIPVisionModelV2 }>) => {
|
||||
const { id, clipVisionModel } = action.payload;
|
||||
const entity = selectIPAdapterEntity(state, id);
|
||||
ipaCLIPVisionModelChanged: (
|
||||
state,
|
||||
action: PayloadAction<EntityIdentifierPayload<{ clipVisionModel: CLIPVisionModelV2 }, 'ip_adapter'>>
|
||||
) => {
|
||||
const { entityIdentifier, clipVisionModel } = action.payload;
|
||||
const entity = selectEntity(state, entityIdentifier);
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
entity.ipAdapter.clipVisionModel = clipVisionModel;
|
||||
},
|
||||
ipaWeightChanged: (state, action: PayloadAction<{ id: string; weight: number }>) => {
|
||||
const { id, weight } = action.payload;
|
||||
const entity = selectIPAdapterEntity(state, id);
|
||||
ipaWeightChanged: (state, action: PayloadAction<EntityIdentifierPayload<{ weight: number }, 'ip_adapter'>>) => {
|
||||
const { entityIdentifier, weight } = action.payload;
|
||||
const entity = selectEntity(state, entityIdentifier);
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
entity.ipAdapter.weight = weight;
|
||||
},
|
||||
ipaBeginEndStepPctChanged: (state, action: PayloadAction<{ id: string; beginEndStepPct: [number, number] }>) => {
|
||||
const { id, beginEndStepPct } = action.payload;
|
||||
const entity = selectIPAdapterEntity(state, id);
|
||||
ipaBeginEndStepPctChanged: (
|
||||
state,
|
||||
action: PayloadAction<EntityIdentifierPayload<{ beginEndStepPct: [number, number] }, 'ip_adapter'>>
|
||||
) => {
|
||||
const { entityIdentifier, beginEndStepPct } = action.payload;
|
||||
const entity = selectEntity(state, entityIdentifier);
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
|
@ -1,14 +1,12 @@
|
||||
import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import { selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import { merge } from 'lodash-es';
|
||||
|
||||
import type { CanvasControlLayerState, CanvasRasterLayerState, CanvasV2State } from './types';
|
||||
import type { CanvasControlLayerState, CanvasRasterLayerState, CanvasV2State, EntityIdentifierPayload } from './types';
|
||||
import { getEntityIdentifier, initialControlNet } from './types';
|
||||
|
||||
const selectRasterLayerEntity = (state: CanvasV2State, id: string) =>
|
||||
state.rasterLayers.entities.find((layer) => layer.id === id);
|
||||
|
||||
export const rasterLayersReducers = {
|
||||
rasterLayerAdded: {
|
||||
reducer: (
|
||||
@ -38,12 +36,12 @@ export const rasterLayersReducers = {
|
||||
rasterLayerRecalled: (state, action: PayloadAction<{ data: CanvasRasterLayerState }>) => {
|
||||
const { data } = action.payload;
|
||||
state.rasterLayers.entities.push(data);
|
||||
state.selectedEntityIdentifier = { type: 'raster_layer', id: data.id };
|
||||
state.selectedEntityIdentifier = getEntityIdentifier(data);
|
||||
},
|
||||
rasterLayerConvertedToControlLayer: {
|
||||
reducer: (state, action: PayloadAction<{ id: string; newId: string }>) => {
|
||||
const { id, newId } = action.payload;
|
||||
const layer = selectRasterLayerEntity(state, id);
|
||||
reducer: (state, action: PayloadAction<EntityIdentifierPayload<{ newId: string }, 'raster_layer'>>) => {
|
||||
const { entityIdentifier, newId } = action.payload;
|
||||
const layer = selectEntity(state, entityIdentifier);
|
||||
if (!layer) {
|
||||
return;
|
||||
}
|
||||
@ -58,14 +56,14 @@ export const rasterLayersReducers = {
|
||||
};
|
||||
|
||||
// Remove the raster layer
|
||||
state.rasterLayers.entities = state.rasterLayers.entities.filter((layer) => layer.id !== id);
|
||||
state.rasterLayers.entities = state.rasterLayers.entities.filter((layer) => layer.id !== entityIdentifier.id);
|
||||
|
||||
// Add the converted control layer
|
||||
state.controlLayers.entities.push(controlLayerState);
|
||||
|
||||
state.selectedEntityIdentifier = { type: controlLayerState.type, id: controlLayerState.id };
|
||||
},
|
||||
prepare: (payload: { id: string }) => ({
|
||||
prepare: (payload: EntityIdentifierPayload<void, 'raster_layer'>) => ({
|
||||
payload: { ...payload, newId: getPrefixedId('control_layer') },
|
||||
}),
|
||||
},
|
||||
|
@ -1,9 +1,11 @@
|
||||
import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import { selectEntity, selectRegionalGuidanceIPAdapter } from 'features/controlLayers/store/selectors';
|
||||
import type {
|
||||
CanvasV2State,
|
||||
CLIPVisionModelV2,
|
||||
EntityIdentifierPayload,
|
||||
FillStyle,
|
||||
IPMethodV2,
|
||||
RegionalGuidanceIPAdapterConfig,
|
||||
@ -17,22 +19,6 @@ import { assert } from 'tsafe';
|
||||
|
||||
import type { CanvasRegionalGuidanceState } from './types';
|
||||
|
||||
const selectRegionalGuidanceEntity = (state: CanvasV2State, id: string) => {
|
||||
return state.regions.entities.find((rg) => rg.id === id);
|
||||
};
|
||||
const selectRegionalGuidanceIPAdapter = (state: CanvasV2State, id: string, ipAdapterId: string) => {
|
||||
const entity = state.regions.entities.find((rg) => rg.id === id);
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
return entity.ipAdapters.find((ipa) => ipa.id === ipAdapterId);
|
||||
};
|
||||
export const selectRegionalGuidanceEntityOrThrow = (state: CanvasV2State, id: string) => {
|
||||
const rg = selectRegionalGuidanceEntity(state, id);
|
||||
assert(rg, `Region with id ${id} not found`);
|
||||
return rg;
|
||||
};
|
||||
|
||||
const DEFAULT_MASK_COLORS: RgbColor[] = [
|
||||
{ r: 121, g: 157, b: 219 }, // rgb(121, 157, 219)
|
||||
{ r: 131, g: 214, b: 131 }, // rgb(131, 214, 131)
|
||||
@ -94,42 +80,54 @@ export const regionsReducers = {
|
||||
state.regions.entities.push(data);
|
||||
state.selectedEntityIdentifier = { type: 'regional_guidance', id: data.id };
|
||||
},
|
||||
rgPositivePromptChanged: (state, action: PayloadAction<{ id: string; prompt: string | null }>) => {
|
||||
const { id, prompt } = action.payload;
|
||||
const entity = selectRegionalGuidanceEntity(state, id);
|
||||
rgPositivePromptChanged: (
|
||||
state,
|
||||
action: PayloadAction<EntityIdentifierPayload<{ prompt: string | null }, 'regional_guidance'>>
|
||||
) => {
|
||||
const { entityIdentifier, prompt } = action.payload;
|
||||
const entity = selectEntity(state, entityIdentifier);
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
entity.positivePrompt = prompt;
|
||||
},
|
||||
rgNegativePromptChanged: (state, action: PayloadAction<{ id: string; prompt: string | null }>) => {
|
||||
const { id, prompt } = action.payload;
|
||||
const entity = selectRegionalGuidanceEntity(state, id);
|
||||
rgNegativePromptChanged: (
|
||||
state,
|
||||
action: PayloadAction<EntityIdentifierPayload<{ prompt: string | null }, 'regional_guidance'>>
|
||||
) => {
|
||||
const { entityIdentifier, prompt } = action.payload;
|
||||
const entity = selectEntity(state, entityIdentifier);
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
entity.negativePrompt = prompt;
|
||||
},
|
||||
rgFillColorChanged: (state, action: PayloadAction<{ id: string; color: RgbColor }>) => {
|
||||
const { id, color } = action.payload;
|
||||
const entity = selectRegionalGuidanceEntity(state, id);
|
||||
rgFillColorChanged: (
|
||||
state,
|
||||
action: PayloadAction<EntityIdentifierPayload<{ color: RgbColor }, 'regional_guidance'>>
|
||||
) => {
|
||||
const { entityIdentifier, color } = action.payload;
|
||||
const entity = selectEntity(state, entityIdentifier);
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
entity.fill.color = color;
|
||||
},
|
||||
rgFillStyleChanged: (state, action: PayloadAction<{ id: string; style: FillStyle }>) => {
|
||||
const { id, style } = action.payload;
|
||||
const entity = selectRegionalGuidanceEntity(state, id);
|
||||
rgFillStyleChanged: (
|
||||
state,
|
||||
action: PayloadAction<EntityIdentifierPayload<{ style: FillStyle }, 'regional_guidance'>>
|
||||
) => {
|
||||
const { entityIdentifier, style } = action.payload;
|
||||
const entity = selectEntity(state, entityIdentifier);
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
entity.fill.style = style;
|
||||
},
|
||||
|
||||
rgAutoNegativeToggled: (state, action: PayloadAction<{ id: string }>) => {
|
||||
const { id } = action.payload;
|
||||
const rg = selectRegionalGuidanceEntity(state, id);
|
||||
rgAutoNegativeToggled: (state, action: PayloadAction<EntityIdentifierPayload<void, 'regional_guidance'>>) => {
|
||||
const { entityIdentifier } = action.payload;
|
||||
const rg = selectEntity(state, entityIdentifier);
|
||||
if (!rg) {
|
||||
return;
|
||||
}
|
||||
@ -138,10 +136,15 @@ export const regionsReducers = {
|
||||
rgIPAdapterAdded: {
|
||||
reducer: (
|
||||
state,
|
||||
action: PayloadAction<{ id: string; ipAdapterId: string; overrides?: Partial<RegionalGuidanceIPAdapterConfig> }>
|
||||
action: PayloadAction<
|
||||
EntityIdentifierPayload<
|
||||
{ ipAdapterId: string; overrides?: Partial<RegionalGuidanceIPAdapterConfig> },
|
||||
'regional_guidance'
|
||||
>
|
||||
>
|
||||
) => {
|
||||
const { id, overrides, ipAdapterId } = action.payload;
|
||||
const entity = selectRegionalGuidanceEntity(state, id);
|
||||
const { entityIdentifier, overrides, ipAdapterId } = action.payload;
|
||||
const entity = selectEntity(state, entityIdentifier);
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
@ -149,13 +152,18 @@ export const regionsReducers = {
|
||||
merge(ipAdapter, overrides);
|
||||
entity.ipAdapters.push(ipAdapter);
|
||||
},
|
||||
prepare: (payload: { id: string; overrides?: Partial<RegionalGuidanceIPAdapterConfig> }) => ({
|
||||
prepare: (
|
||||
payload: EntityIdentifierPayload<{ overrides?: Partial<RegionalGuidanceIPAdapterConfig> }, 'regional_guidance'>
|
||||
) => ({
|
||||
payload: { ...payload, ipAdapterId: getPrefixedId('regional_guidance_ip_adapter') },
|
||||
}),
|
||||
},
|
||||
rgIPAdapterDeleted: (state, action: PayloadAction<{ id: string; ipAdapterId: string }>) => {
|
||||
const { id, ipAdapterId } = action.payload;
|
||||
const entity = selectRegionalGuidanceEntity(state, id);
|
||||
rgIPAdapterDeleted: (
|
||||
state,
|
||||
action: PayloadAction<EntityIdentifierPayload<{ ipAdapterId: string }, 'regional_guidance'>>
|
||||
) => {
|
||||
const { entityIdentifier, ipAdapterId } = action.payload;
|
||||
const entity = selectEntity(state, entityIdentifier);
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
@ -163,18 +171,23 @@ export const regionsReducers = {
|
||||
},
|
||||
rgIPAdapterImageChanged: (
|
||||
state,
|
||||
action: PayloadAction<{ id: string; ipAdapterId: string; imageDTO: ImageDTO | null }>
|
||||
action: PayloadAction<
|
||||
EntityIdentifierPayload<{ ipAdapterId: string; imageDTO: ImageDTO | null }, 'regional_guidance'>
|
||||
>
|
||||
) => {
|
||||
const { id, ipAdapterId, imageDTO } = action.payload;
|
||||
const ipAdapter = selectRegionalGuidanceIPAdapter(state, id, ipAdapterId);
|
||||
const { entityIdentifier, ipAdapterId, imageDTO } = action.payload;
|
||||
const ipAdapter = selectRegionalGuidanceIPAdapter(state, entityIdentifier, ipAdapterId);
|
||||
if (!ipAdapter) {
|
||||
return;
|
||||
}
|
||||
ipAdapter.image = imageDTO ? imageDTOToImageWithDims(imageDTO) : null;
|
||||
},
|
||||
rgIPAdapterWeightChanged: (state, action: PayloadAction<{ id: string; ipAdapterId: string; weight: number }>) => {
|
||||
const { id, ipAdapterId, weight } = action.payload;
|
||||
const ipAdapter = selectRegionalGuidanceIPAdapter(state, id, ipAdapterId);
|
||||
rgIPAdapterWeightChanged: (
|
||||
state,
|
||||
action: PayloadAction<EntityIdentifierPayload<{ ipAdapterId: string; weight: number }, 'regional_guidance'>>
|
||||
) => {
|
||||
const { entityIdentifier, ipAdapterId, weight } = action.payload;
|
||||
const ipAdapter = selectRegionalGuidanceIPAdapter(state, entityIdentifier, ipAdapterId);
|
||||
if (!ipAdapter) {
|
||||
return;
|
||||
}
|
||||
@ -182,18 +195,23 @@ export const regionsReducers = {
|
||||
},
|
||||
rgIPAdapterBeginEndStepPctChanged: (
|
||||
state,
|
||||
action: PayloadAction<{ id: string; ipAdapterId: string; beginEndStepPct: [number, number] }>
|
||||
action: PayloadAction<
|
||||
EntityIdentifierPayload<{ ipAdapterId: string; beginEndStepPct: [number, number] }, 'regional_guidance'>
|
||||
>
|
||||
) => {
|
||||
const { id, ipAdapterId, beginEndStepPct } = action.payload;
|
||||
const ipAdapter = selectRegionalGuidanceIPAdapter(state, id, ipAdapterId);
|
||||
const { entityIdentifier, ipAdapterId, beginEndStepPct } = action.payload;
|
||||
const ipAdapter = selectRegionalGuidanceIPAdapter(state, entityIdentifier, ipAdapterId);
|
||||
if (!ipAdapter) {
|
||||
return;
|
||||
}
|
||||
ipAdapter.beginEndStepPct = beginEndStepPct;
|
||||
},
|
||||
rgIPAdapterMethodChanged: (state, action: PayloadAction<{ id: string; ipAdapterId: string; method: IPMethodV2 }>) => {
|
||||
const { id, ipAdapterId, method } = action.payload;
|
||||
const ipAdapter = selectRegionalGuidanceIPAdapter(state, id, ipAdapterId);
|
||||
rgIPAdapterMethodChanged: (
|
||||
state,
|
||||
action: PayloadAction<EntityIdentifierPayload<{ ipAdapterId: string; method: IPMethodV2 }, 'regional_guidance'>>
|
||||
) => {
|
||||
const { entityIdentifier, ipAdapterId, method } = action.payload;
|
||||
const ipAdapter = selectRegionalGuidanceIPAdapter(state, entityIdentifier, ipAdapterId);
|
||||
if (!ipAdapter) {
|
||||
return;
|
||||
}
|
||||
@ -201,14 +219,18 @@ export const regionsReducers = {
|
||||
},
|
||||
rgIPAdapterModelChanged: (
|
||||
state,
|
||||
action: PayloadAction<{
|
||||
id: string;
|
||||
action: PayloadAction<
|
||||
EntityIdentifierPayload<
|
||||
{
|
||||
ipAdapterId: string;
|
||||
modelConfig: IPAdapterModelConfig | null;
|
||||
}>
|
||||
},
|
||||
'regional_guidance'
|
||||
>
|
||||
>
|
||||
) => {
|
||||
const { id, ipAdapterId, modelConfig } = action.payload;
|
||||
const ipAdapter = selectRegionalGuidanceIPAdapter(state, id, ipAdapterId);
|
||||
const { entityIdentifier, ipAdapterId, modelConfig } = action.payload;
|
||||
const ipAdapter = selectRegionalGuidanceIPAdapter(state, entityIdentifier, ipAdapterId);
|
||||
if (!ipAdapter) {
|
||||
return;
|
||||
}
|
||||
@ -216,10 +238,12 @@ export const regionsReducers = {
|
||||
},
|
||||
rgIPAdapterCLIPVisionModelChanged: (
|
||||
state,
|
||||
action: PayloadAction<{ id: string; ipAdapterId: string; clipVisionModel: CLIPVisionModelV2 }>
|
||||
action: PayloadAction<
|
||||
EntityIdentifierPayload<{ ipAdapterId: string; clipVisionModel: CLIPVisionModelV2 }, 'regional_guidance'>
|
||||
>
|
||||
) => {
|
||||
const { id, ipAdapterId, clipVisionModel } = action.payload;
|
||||
const ipAdapter = selectRegionalGuidanceIPAdapter(state, id, ipAdapterId);
|
||||
const { entityIdentifier, ipAdapterId, clipVisionModel } = action.payload;
|
||||
const ipAdapter = selectRegionalGuidanceIPAdapter(state, entityIdentifier, ipAdapterId);
|
||||
if (!ipAdapter) {
|
||||
return;
|
||||
}
|
||||
|
@ -1,7 +1,32 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import type {
|
||||
CanvasControlLayerState,
|
||||
CanvasEntityIdentifier,
|
||||
CanvasEntityState,
|
||||
CanvasInpaintMaskState,
|
||||
CanvasRasterLayerState,
|
||||
CanvasRegionalGuidanceState,
|
||||
CanvasV2State,
|
||||
} from 'features/controlLayers/store/types';
|
||||
import { getOptimalDimension } from 'features/parameters/util/optimalDimension';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
/**
|
||||
* Selects the canvasV2 slice from the root state
|
||||
*/
|
||||
export const selectCanvasV2Slice = (state: RootState) => state.canvasV2;
|
||||
|
||||
/**
|
||||
* Selects the total canvas entity count:
|
||||
* - Regions
|
||||
* - IP adapters
|
||||
* - Raster layers
|
||||
* - Control layers
|
||||
* - Inpaint masks
|
||||
*
|
||||
* It does not check for validity of the entities.
|
||||
*/
|
||||
export const selectEntityCount = createSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
return (
|
||||
canvasV2.regions.entities.length +
|
||||
@ -12,6 +37,134 @@ export const selectEntityCount = createSelector(selectCanvasV2Slice, (canvasV2)
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Selects the optimal dimension for the canvas based on the currently-model
|
||||
*/
|
||||
export const selectOptimalDimension = createSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
return getOptimalDimension(canvasV2.params.model);
|
||||
});
|
||||
|
||||
/**
|
||||
* Selects a single entity from the canvasV2 slice. If the entity identifier is narrowed to a specific type, the
|
||||
* return type will be narrowed as well.
|
||||
*/
|
||||
export function selectEntity<T extends CanvasEntityIdentifier>(
|
||||
state: CanvasV2State,
|
||||
entityIdentifier: T
|
||||
): Extract<CanvasEntityState, T> | undefined {
|
||||
const { id, type } = entityIdentifier;
|
||||
|
||||
let entity: CanvasEntityState | undefined = undefined;
|
||||
|
||||
switch (type) {
|
||||
case 'raster_layer':
|
||||
entity = state.rasterLayers.entities.find((entity) => entity.id === id);
|
||||
break;
|
||||
case 'control_layer':
|
||||
entity = state.controlLayers.entities.find((entity) => entity.id === id);
|
||||
break;
|
||||
case 'inpaint_mask':
|
||||
entity = state.inpaintMasks.entities.find((entity) => entity.id === id);
|
||||
break;
|
||||
case 'regional_guidance':
|
||||
entity = state.regions.entities.find((entity) => entity.id === id);
|
||||
break;
|
||||
case 'ip_adapter':
|
||||
entity = state.ipAdapters.entities.find((entity) => entity.id === id);
|
||||
break;
|
||||
}
|
||||
|
||||
// This cast is safe, but TS seems to be unable to infer the type
|
||||
return entity as Extract<CanvasEntityState, T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selected an entity from the canvasV2 slice. If the entity is not found, an error is thrown.
|
||||
* Wrapper around {@link selectEntity}.
|
||||
*/
|
||||
export function selectEntityOrThrow<T extends CanvasEntityIdentifier>(
|
||||
state: CanvasV2State,
|
||||
entityIdentifier: T
|
||||
): Extract<CanvasEntityState, T> {
|
||||
const entity = selectEntity(state, entityIdentifier);
|
||||
assert(entity, `Entity with id ${entityIdentifier.id} not found`);
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects all entities of the given type.
|
||||
*/
|
||||
export function selectAllEntitiesOfType<T extends CanvasEntityState['type']>(
|
||||
state: CanvasV2State,
|
||||
type: T
|
||||
): Extract<CanvasEntityState, { type: T }>[] {
|
||||
let entities: CanvasEntityState[] = [];
|
||||
|
||||
switch (type) {
|
||||
case 'raster_layer':
|
||||
entities = state.rasterLayers.entities;
|
||||
break;
|
||||
case 'control_layer':
|
||||
entities = state.controlLayers.entities;
|
||||
break;
|
||||
case 'inpaint_mask':
|
||||
entities = state.inpaintMasks.entities;
|
||||
break;
|
||||
case 'regional_guidance':
|
||||
entities = state.regions.entities;
|
||||
break;
|
||||
case 'ip_adapter':
|
||||
entities = state.ipAdapters.entities;
|
||||
break;
|
||||
}
|
||||
|
||||
// This cast is safe, but TS seems to be unable to infer the type
|
||||
return entities as Extract<CanvasEntityState, { type: T }>[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects all entities, in the order they are displayed in the list.
|
||||
*/
|
||||
export function selectAllEntities(state: CanvasV2State): CanvasEntityState[] {
|
||||
// These are in the same order as they are displayed in the list!
|
||||
return [
|
||||
...state.inpaintMasks.entities.toReversed(),
|
||||
...state.regions.entities.toReversed(),
|
||||
...state.ipAdapters.entities.toReversed(),
|
||||
...state.controlLayers.entities.toReversed(),
|
||||
...state.rasterLayers.entities.toReversed(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects all _renderable_ entities:
|
||||
* - Raster layers
|
||||
* - Control layers
|
||||
* - Inpaint masks
|
||||
* - Regional guidance
|
||||
*/
|
||||
export function selectAllRenderableEntities(
|
||||
state: CanvasV2State
|
||||
): (CanvasRasterLayerState | CanvasControlLayerState | CanvasInpaintMaskState | CanvasRegionalGuidanceState)[] {
|
||||
return [
|
||||
...state.rasterLayers.entities,
|
||||
...state.controlLayers.entities,
|
||||
...state.inpaintMasks.entities,
|
||||
...state.regions.entities,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the IP adapter for the specific Regional Guidance layer.
|
||||
*/
|
||||
export function selectRegionalGuidanceIPAdapter(
|
||||
state: CanvasV2State,
|
||||
entityIdentifier: CanvasEntityIdentifier<'regional_guidance'>,
|
||||
ipAdapterId: string
|
||||
) {
|
||||
const entity = selectEntity(state, entityIdentifier);
|
||||
if (!entity) {
|
||||
return undefined;
|
||||
}
|
||||
return entity.ipAdapters.find((ipAdapter) => ipAdapter.id === ipAdapterId);
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import type { SerializableObject } from 'common/types';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import { zModelIdentifierField } from 'features/nodes/types/common';
|
||||
import type { AspectRatioState } from 'features/parameters/components/DocumentSize/types';
|
||||
@ -692,7 +693,9 @@ export type CanvasEntityState =
|
||||
| CanvasRegionalGuidanceState
|
||||
| CanvasInpaintMaskState
|
||||
| CanvasIPAdapterState;
|
||||
export type CanvasEntityIdentifier = Pick<CanvasEntityState, 'id' | 'type'>;
|
||||
|
||||
export type CanvasEntityType = CanvasEntityState['type'];
|
||||
export type CanvasEntityIdentifier<T extends CanvasEntityType = CanvasEntityType> = { id: string; type: T };
|
||||
|
||||
export type LoRA = {
|
||||
id: string;
|
||||
@ -819,7 +822,17 @@ export type StageAttrs = {
|
||||
scale: number;
|
||||
};
|
||||
|
||||
export type EntityIdentifierPayload<T = object> = { entityIdentifier: CanvasEntityIdentifier } & T;
|
||||
export type EntityIdentifierPayload<
|
||||
T extends SerializableObject | void = void,
|
||||
U extends CanvasEntityType = CanvasEntityType,
|
||||
> = T extends void
|
||||
? {
|
||||
entityIdentifier: CanvasEntityIdentifier<U>;
|
||||
}
|
||||
: {
|
||||
entityIdentifier: CanvasEntityIdentifier<U>;
|
||||
} & T;
|
||||
|
||||
export type EntityMovedPayload = EntityIdentifierPayload<{ position: Coordinate }>;
|
||||
export type EntityBrushLineAddedPayload = EntityIdentifierPayload<{ brushLine: CanvasBrushLineState }>;
|
||||
export type EntityEraserLineAddedPayload = EntityIdentifierPayload<{ eraserLine: CanvasEraserLineState }>;
|
||||
@ -858,6 +871,8 @@ export function isDrawableEntity(
|
||||
return isDrawableEntityType(entity.type);
|
||||
}
|
||||
|
||||
export const getEntityIdentifier = (entity: CanvasEntityState): CanvasEntityIdentifier => {
|
||||
export const getEntityIdentifier = <T extends CanvasEntityType>(
|
||||
entity: Extract<CanvasEntityState, { type: T }>
|
||||
): CanvasEntityIdentifier<T> => {
|
||||
return { id: entity.id, type: entity.type };
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { ConfirmationAlertDialog, Divider, Flex, FormControl, FormLabel, Switch, Text } from '@invoke-ai/ui-library';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { imageDeletionConfirmed } from 'features/deleteImageModal/store/actions';
|
||||
import { getImageUsage, selectImageUsage } from 'features/deleteImageModal/store/selectors';
|
||||
import {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import type { CanvasV2State } from 'features/controlLayers/store/types';
|
||||
import { selectDeleteImageModalSlice } from 'features/deleteImageModal/store/slice';
|
||||
import { selectNodesSlice } from 'features/nodes/store/nodesSlice';
|
||||
|
@ -13,7 +13,7 @@ import {
|
||||
import { skipToken } from '@reduxjs/toolkit/query';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import ImageUsageMessage from 'features/deleteImageModal/components/ImageUsageMessage';
|
||||
import { getImageUsage } from 'features/deleteImageModal/store/selectors';
|
||||
import type { ImageUsage } from 'features/deleteImageModal/store/types';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Flex } from '@invoke-ai/ui-library';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { LoRACard } from 'features/lora/components/LoRACard';
|
||||
import { memo } from 'react';
|
||||
|
||||
|
@ -4,7 +4,8 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
|
||||
import { useGroupedModelCombobox } from 'common/hooks/useGroupedModelCombobox';
|
||||
import { loraAdded, selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { loraAdded } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLoRAModels } from 'services/api/hooks/modelsByType';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Flex } from '@invoke-ai/ui-library';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { ParamNegativePrompt } from 'features/parameters/components/Core/ParamNegativePrompt';
|
||||
import { ParamPositivePrompt } from 'features/parameters/components/Core/ParamPositivePrompt';
|
||||
import { ParamSDXLNegativeStylePrompt } from 'features/sdxl/components/SDXLPrompts/ParamSDXLNegativeStylePrompt';
|
||||
|
@ -2,7 +2,7 @@ import { Divider, Flex, ListItem, Text, Tooltip, UnorderedList } from '@invoke-a
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useIsReadyToEnqueue } from 'common/hooks/useIsReadyToEnqueue';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
|
||||
import { getShouldProcessPrompt } from 'features/dynamicPrompts/util/getShouldProcessPrompt';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
|
@ -3,7 +3,7 @@ import { Flex, FormControlGroup, StandaloneAccordion } from '@invoke-ai/ui-libra
|
||||
import { skipToken } from '@reduxjs/toolkit/query';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import ParamCFGRescaleMultiplier from 'features/parameters/components/Advanced/ParamCFGRescaleMultiplier';
|
||||
import ParamClipSkip from 'features/parameters/components/Advanced/ParamClipSkip';
|
||||
import ParamSeamlessXAxis from 'features/parameters/components/Seamless/ParamSeamlessXAxis';
|
||||
|
@ -3,7 +3,7 @@ import { Box, Expander, Flex, FormControlGroup, StandaloneAccordion } from '@inv
|
||||
import { EMPTY_ARRAY } from 'app/store/constants';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { LoRAList } from 'features/lora/components/LoRAList';
|
||||
import LoRASelect from 'features/lora/components/LoRASelect';
|
||||
import ParamCFGScale from 'features/parameters/components/Core/ParamCFGScale';
|
||||
|
@ -2,7 +2,7 @@ import type { FormLabelProps } from '@invoke-ai/ui-library';
|
||||
import { Expander, Flex, FormControlGroup, StandaloneAccordion } from '@invoke-ai/ui-library';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { HrfSettings } from 'features/hrf/components/HrfSettings';
|
||||
import { selectHrfSlice } from 'features/hrf/store/hrfSlice';
|
||||
import ParamScaleBeforeProcessing from 'features/parameters/components/Canvas/InfillAndScaling/ParamScaleBeforeProcessing';
|
||||
|
@ -2,7 +2,7 @@ import type { FormLabelProps } from '@invoke-ai/ui-library';
|
||||
import { Flex, FormControlGroup, StandaloneAccordion, Text } from '@invoke-ai/ui-library';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import ParamSDXLRefinerCFGScale from 'features/sdxl/components/SDXLRefiner/ParamSDXLRefinerCFGScale';
|
||||
import ParamSDXLRefinerModelSelect from 'features/sdxl/components/SDXLRefiner/ParamSDXLRefinerModelSelect';
|
||||
import ParamSDXLRefinerNegativeAestheticScore from 'features/sdxl/components/SDXLRefiner/ParamSDXLRefinerNegativeAestheticScore';
|
||||
|
Loading…
Reference in New Issue
Block a user