mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
refactor(ui): canvas v2 (wip)
merge all canvas state reducers into one big slice (but with the logic split across files so it's not hell)
This commit is contained in:
parent
b4daf29bd8
commit
4071e96245
@ -1,7 +1,7 @@
|
|||||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||||
import { resetCanvas } from 'features/canvas/store/canvasSlice';
|
import { resetCanvas } from 'features/canvas/store/canvasSlice';
|
||||||
import { controlAdaptersReset } from 'features/controlAdapters/store/controlAdaptersSlice';
|
import { controlAdaptersReset } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||||
import { allLayersDeleted } from 'features/controlLayers/store/controlLayersSlice';
|
import { allLayersDeleted } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { getImageUsage } from 'features/deleteImageModal/store/selectors';
|
import { getImageUsage } from 'features/deleteImageModal/store/selectors';
|
||||||
import { nodeEditorReset } from 'features/nodes/store/nodesSlice';
|
import { nodeEditorReset } from 'features/nodes/store/nodesSlice';
|
||||||
import { imagesApi } from 'services/api/endpoints/images';
|
import { imagesApi } from 'services/api/endpoints/images';
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
controlAdapterProcessorConfigChanged,
|
controlAdapterProcessorConfigChanged,
|
||||||
controlAdapterProcessorPendingBatchIdChanged,
|
controlAdapterProcessorPendingBatchIdChanged,
|
||||||
controlAdapterRecalled,
|
controlAdapterRecalled,
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
} from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { isControlAdapterLayer } from 'features/controlLayers/store/types';
|
import { isControlAdapterLayer } from 'features/controlLayers/store/types';
|
||||||
import { CA_PROCESSOR_DATA } from 'features/controlLayers/util/controlAdapters';
|
import { CA_PROCESSOR_DATA } from 'features/controlLayers/util/controlAdapters';
|
||||||
import { toast } from 'features/toast/toast';
|
import { toast } from 'features/toast/toast';
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
selectControlAdapterAll,
|
selectControlAdapterAll,
|
||||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||||
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
|
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
|
||||||
import { layerDeleted } from 'features/controlLayers/store/controlLayersSlice';
|
import { layerDeleted } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import {
|
import {
|
||||||
isControlAdapterLayer,
|
isControlAdapterLayer,
|
||||||
isInitialImageLayer,
|
isInitialImageLayer,
|
||||||
|
@ -13,7 +13,7 @@ import {
|
|||||||
layerImageAdded,
|
layerImageAdded,
|
||||||
ipAdapterImageChanged,
|
ipAdapterImageChanged,
|
||||||
regionalGuidanceIPAdapterImageChanged,
|
regionalGuidanceIPAdapterImageChanged,
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
} from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import type { TypesafeDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
import type { TypesafeDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
||||||
import { isValidDrop } from 'features/dnd/util/isValidDrop';
|
import { isValidDrop } from 'features/dnd/util/isValidDrop';
|
||||||
import {
|
import {
|
||||||
|
@ -1,19 +1,8 @@
|
|||||||
import { logger } from 'app/logging/logger';
|
import { logger } from 'app/logging/logger';
|
||||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||||
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
import { caImageChanged, ipaImageChanged, rgIPAdapterImageChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import {
|
|
||||||
controlAdapterImageChanged,
|
|
||||||
controlAdapterIsEnabledChanged,
|
|
||||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
|
||||||
import {
|
|
||||||
controlAdapterImageChanged,
|
|
||||||
iiLayerImageChanged,
|
|
||||||
ipAdapterImageChanged,
|
|
||||||
regionalGuidanceIPAdapterImageChanged,
|
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
|
||||||
import { selectListBoardsQueryArgs } from 'features/gallery/store/gallerySelectors';
|
import { selectListBoardsQueryArgs } from 'features/gallery/store/gallerySelectors';
|
||||||
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
|
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
|
||||||
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
|
||||||
import { upscaleInitialImageChanged } from 'features/parameters/store/upscaleSlice';
|
import { upscaleInitialImageChanged } from 'features/parameters/store/upscaleSlice';
|
||||||
import { toast } from 'features/toast/toast';
|
import { toast } from 'features/toast/toast';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
@ -81,15 +70,6 @@ export const addImageUploadedFulfilledListener = (startAppListening: AppStartLis
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (postUploadAction?.type === 'SET_CANVAS_INITIAL_IMAGE') {
|
|
||||||
dispatch(setInitialCanvasImage(imageDTO, selectOptimalDimension(state)));
|
|
||||||
toast({
|
|
||||||
...DEFAULT_UPLOADED_TOAST,
|
|
||||||
description: t('toast.setAsCanvasInitialImage'),
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (postUploadAction?.type === 'SET_UPSCALE_INITIAL_IMAGE') {
|
if (postUploadAction?.type === 'SET_UPSCALE_INITIAL_IMAGE') {
|
||||||
dispatch(upscaleInitialImageChanged(imageDTO));
|
dispatch(upscaleInitialImageChanged(imageDTO));
|
||||||
toast({
|
toast({
|
||||||
@ -99,57 +79,27 @@ export const addImageUploadedFulfilledListener = (startAppListening: AppStartLis
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (postUploadAction?.type === 'SET_CONTROL_ADAPTER_IMAGE') {
|
if (postUploadAction?.type === 'SET_CA_IMAGE') {
|
||||||
const { id } = postUploadAction;
|
const { id } = postUploadAction;
|
||||||
dispatch(
|
dispatch(caImageChanged({ id, imageDTO }));
|
||||||
controlAdapterIsEnabledChanged({
|
|
||||||
id,
|
|
||||||
isEnabled: true,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
dispatch(
|
|
||||||
controlAdapterImageChanged({
|
|
||||||
id,
|
|
||||||
controlImage: imageDTO.image_name,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
toast({
|
|
||||||
...DEFAULT_UPLOADED_TOAST,
|
|
||||||
description: t('toast.setControlImage'),
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (postUploadAction?.type === 'SET_CA_LAYER_IMAGE') {
|
|
||||||
const { layerId } = postUploadAction;
|
|
||||||
dispatch(controlAdapterImageChanged({ layerId, imageDTO }));
|
|
||||||
toast({
|
toast({
|
||||||
...DEFAULT_UPLOADED_TOAST,
|
...DEFAULT_UPLOADED_TOAST,
|
||||||
description: t('toast.setControlImage'),
|
description: t('toast.setControlImage'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (postUploadAction?.type === 'SET_IPA_LAYER_IMAGE') {
|
if (postUploadAction?.type === 'SET_IPA_IMAGE') {
|
||||||
const { layerId } = postUploadAction;
|
const { id } = postUploadAction;
|
||||||
dispatch(ipAdapterImageChanged({ layerId, imageDTO }));
|
dispatch(ipaImageChanged({ id, imageDTO }));
|
||||||
toast({
|
toast({
|
||||||
...DEFAULT_UPLOADED_TOAST,
|
...DEFAULT_UPLOADED_TOAST,
|
||||||
description: t('toast.setControlImage'),
|
description: t('toast.setControlImage'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (postUploadAction?.type === 'SET_RG_LAYER_IP_ADAPTER_IMAGE') {
|
if (postUploadAction?.type === 'SET_RG_IP_ADAPTER_IMAGE') {
|
||||||
const { layerId, ipAdapterId } = postUploadAction;
|
const { id, ipAdapterId } = postUploadAction;
|
||||||
dispatch(regionalGuidanceIPAdapterImageChanged({ layerId, ipAdapterId, imageDTO }));
|
dispatch(rgIPAdapterImageChanged({ id, ipAdapterId, imageDTO }));
|
||||||
toast({
|
|
||||||
...DEFAULT_UPLOADED_TOAST,
|
|
||||||
description: t('toast.setControlImage'),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (postUploadAction?.type === 'SET_II_LAYER_IMAGE') {
|
|
||||||
const { layerId } = postUploadAction;
|
|
||||||
dispatch(iiLayerImageChanged({ layerId, imageDTO }));
|
|
||||||
toast({
|
toast({
|
||||||
...DEFAULT_UPLOADED_TOAST,
|
...DEFAULT_UPLOADED_TOAST,
|
||||||
description: t('toast.setControlImage'),
|
description: t('toast.setControlImage'),
|
||||||
|
@ -6,7 +6,7 @@ import {
|
|||||||
controlAdapterModelCleared,
|
controlAdapterModelCleared,
|
||||||
selectControlAdapterAll,
|
selectControlAdapterAll,
|
||||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||||
import { heightChanged, widthChanged } from 'features/controlLayers/store/controlLayersSlice';
|
import { heightChanged, widthChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { loraRemoved } from 'features/lora/store/loraSlice';
|
import { loraRemoved } from 'features/lora/store/loraSlice';
|
||||||
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
||||||
import { modelChanged, vaeSelected } from 'features/parameters/store/generationSlice';
|
import { modelChanged, vaeSelected } from 'features/parameters/store/generationSlice';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { isAnyOf } from '@reduxjs/toolkit';
|
import { isAnyOf } from '@reduxjs/toolkit';
|
||||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||||
import { positivePromptChanged } from 'features/controlLayers/store/controlLayersSlice';
|
import { positivePromptChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import {
|
import {
|
||||||
combinatorialToggled,
|
combinatorialToggled,
|
||||||
isErrorChanged,
|
isErrorChanged,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||||
import { heightChanged, widthChanged } from 'features/controlLayers/store/controlLayersSlice';
|
import { heightChanged, widthChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { setDefaultSettings } from 'features/parameters/store/actions';
|
import { setDefaultSettings } from 'features/parameters/store/actions';
|
||||||
import {
|
import {
|
||||||
setCfgRescaleMultiplier,
|
setCfgRescaleMultiplier,
|
||||||
|
@ -5,17 +5,7 @@ import { idbKeyValDriver } from 'app/store/enhancers/reduxRemember/driver';
|
|||||||
import { errorHandler } from 'app/store/enhancers/reduxRemember/errors';
|
import { errorHandler } from 'app/store/enhancers/reduxRemember/errors';
|
||||||
import type { JSONObject } from 'common/types';
|
import type { JSONObject } from 'common/types';
|
||||||
import { changeBoardModalSlice } from 'features/changeBoardModal/store/slice';
|
import { changeBoardModalSlice } from 'features/changeBoardModal/store/slice';
|
||||||
import {
|
import { canvasV2PersistConfig, canvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
controlAdaptersV2PersistConfig,
|
|
||||||
controlAdaptersV2Slice,
|
|
||||||
} from 'features/controlLayers/store/controlAdaptersSlice';
|
|
||||||
import { canvasV2PersistConfig, canvasV2Slice } from 'features/controlLayers/store/controlLayersSlice';
|
|
||||||
import { ipAdaptersPersistConfig, ipAdaptersSlice } from 'features/controlLayers/store/ipAdaptersSlice';
|
|
||||||
import { layersPersistConfig, layersSlice } from 'features/controlLayers/store/layersSlice';
|
|
||||||
import {
|
|
||||||
regionalGuidancePersistConfig,
|
|
||||||
regionalGuidanceSlice,
|
|
||||||
} from 'features/controlLayers/store/regionalGuidanceSlice';
|
|
||||||
import { deleteImageModalSlice } from 'features/deleteImageModal/store/slice';
|
import { deleteImageModalSlice } from 'features/deleteImageModal/store/slice';
|
||||||
import { dynamicPromptsPersistConfig, dynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
|
import { dynamicPromptsPersistConfig, dynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
|
||||||
import { galleryPersistConfig, gallerySlice } from 'features/gallery/store/gallerySlice';
|
import { galleryPersistConfig, gallerySlice } from 'features/gallery/store/gallerySlice';
|
||||||
@ -70,10 +60,6 @@ const allReducers = {
|
|||||||
[workflowSettingsSlice.name]: workflowSettingsSlice.reducer,
|
[workflowSettingsSlice.name]: workflowSettingsSlice.reducer,
|
||||||
[upscaleSlice.name]: upscaleSlice.reducer,
|
[upscaleSlice.name]: upscaleSlice.reducer,
|
||||||
[stylePresetSlice.name]: stylePresetSlice.reducer,
|
[stylePresetSlice.name]: stylePresetSlice.reducer,
|
||||||
[layersSlice.name]: layersSlice.reducer,
|
|
||||||
[controlAdaptersV2Slice.name]: controlAdaptersV2Slice.reducer,
|
|
||||||
[ipAdaptersSlice.name]: ipAdaptersSlice.reducer,
|
|
||||||
[regionalGuidanceSlice.name]: regionalGuidanceSlice.reducer,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const rootReducer = combineReducers(allReducers);
|
const rootReducer = combineReducers(allReducers);
|
||||||
@ -118,10 +104,6 @@ const persistConfigs: { [key in keyof typeof allReducers]?: PersistConfig } = {
|
|||||||
[workflowSettingsPersistConfig.name]: workflowSettingsPersistConfig,
|
[workflowSettingsPersistConfig.name]: workflowSettingsPersistConfig,
|
||||||
[upscalePersistConfig.name]: upscalePersistConfig,
|
[upscalePersistConfig.name]: upscalePersistConfig,
|
||||||
[stylePresetPersistConfig.name]: stylePresetPersistConfig,
|
[stylePresetPersistConfig.name]: stylePresetPersistConfig,
|
||||||
[layersPersistConfig.name]: layersPersistConfig,
|
|
||||||
[controlAdaptersV2PersistConfig.name]: controlAdaptersV2PersistConfig,
|
|
||||||
[ipAdaptersPersistConfig.name]: ipAdaptersPersistConfig,
|
|
||||||
[regionalGuidancePersistConfig.name]: regionalGuidancePersistConfig,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const unserialize: UnserializeFunction = (data, key) => {
|
const unserialize: UnserializeFunction = (data, key) => {
|
||||||
|
@ -2,7 +2,7 @@ import { useStore } from '@nanostores/react';
|
|||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { selectControlAdaptersV2Slice } from 'features/controlLayers/store/controlAdaptersSlice';
|
import { selectControlAdaptersV2Slice } from 'features/controlLayers/store/controlAdaptersSlice';
|
||||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/controlLayersSlice';
|
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { selectIPAdaptersSlice } from 'features/controlLayers/store/ipAdaptersSlice';
|
import { selectIPAdaptersSlice } from 'features/controlLayers/store/ipAdaptersSlice';
|
||||||
import { selectLayersSlice } from 'features/controlLayers/store/layersSlice';
|
import { selectLayersSlice } from 'features/controlLayers/store/layersSlice';
|
||||||
import { selectRegionalGuidanceSlice } from 'features/controlLayers/store/regionalGuidanceSlice';
|
import { selectRegionalGuidanceSlice } from 'features/controlLayers/store/regionalGuidanceSlice';
|
||||||
|
@ -12,7 +12,7 @@ import {
|
|||||||
controlAdapterImageChanged,
|
controlAdapterImageChanged,
|
||||||
selectControlAdaptersSlice,
|
selectControlAdaptersSlice,
|
||||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||||
import { heightChanged, widthChanged } from 'features/controlLayers/store/controlLayersSlice';
|
import { heightChanged, widthChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import type { TypesafeDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
import type { TypesafeDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
||||||
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
||||||
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import { Button, Menu, MenuButton, MenuItem, MenuList } from '@invoke-ai/ui-library';
|
import { Button, Menu, MenuButton, MenuItem, MenuList } from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import { useAddCALayer, useAddIPALayer } from 'features/controlLayers/hooks/addLayerHooks';
|
import { useAddCALayer, useAddIPALayer } from 'features/controlLayers/hooks/addLayerHooks';
|
||||||
import { layerAdded } from 'features/controlLayers/store/layersSlice';
|
import { layerAdded, rgAdded } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { rgAdded } from 'features/controlLayers/store/regionalGuidanceSlice';
|
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { PiPlusBold } from 'react-icons/pi';
|
import { PiPlusBold } from 'react-icons/pi';
|
||||||
|
@ -5,8 +5,8 @@ import { useAddIPAdapterToRGLayer } from 'features/controlLayers/hooks/addLayerH
|
|||||||
import {
|
import {
|
||||||
rgNegativePromptChanged,
|
rgNegativePromptChanged,
|
||||||
rgPositivePromptChanged,
|
rgPositivePromptChanged,
|
||||||
selectRegionalGuidanceSlice,
|
selectCanvasV2Slice,
|
||||||
} from 'features/controlLayers/store/regionalGuidanceSlice';
|
} from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { PiPlusBold } from 'react-icons/pi';
|
import { PiPlusBold } from 'react-icons/pi';
|
||||||
@ -21,8 +21,8 @@ export const AddPromptButtons = ({ id }: AddPromptButtonProps) => {
|
|||||||
const [addIPAdapter, isAddIPAdapterDisabled] = useAddIPAdapterToRGLayer(id);
|
const [addIPAdapter, isAddIPAdapterDisabled] = useAddIPAdapterToRGLayer(id);
|
||||||
const selectValidActions = useMemo(
|
const selectValidActions = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createMemoizedSelector(selectRegionalGuidanceSlice, (regionalGuidanceState) => {
|
createMemoizedSelector(selectCanvasV2Slice, (caState) => {
|
||||||
const rg = regionalGuidanceState.regions.find((rg) => rg.id === id);
|
const rg = caState.regions.find((rg) => rg.id === id);
|
||||||
return {
|
return {
|
||||||
canAddPositivePrompt: rg?.positivePrompt === null,
|
canAddPositivePrompt: rg?.positivePrompt === null,
|
||||||
canAddNegativePrompt: rg?.negativePrompt === null,
|
canAddNegativePrompt: rg?.negativePrompt === null,
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from '@invoke-ai/ui-library';
|
} from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { brushWidthChanged } from 'features/controlLayers/store/controlLayersSlice';
|
import { brushWidthChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
|||||||
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
|
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
|
||||||
import { CAHeader } from 'features/controlLayers/components/ControlAdapter/CAEntityHeader';
|
import { CAHeader } from 'features/controlLayers/components/ControlAdapter/CAEntityHeader';
|
||||||
import { CASettings } from 'features/controlLayers/components/ControlAdapter/CASettings';
|
import { CASettings } from 'features/controlLayers/components/ControlAdapter/CASettings';
|
||||||
import { entitySelected } from 'features/controlLayers/store/controlLayersSlice';
|
import { entitySelected } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
@ -8,9 +8,9 @@ import {
|
|||||||
caMovedForwardOne,
|
caMovedForwardOne,
|
||||||
caMovedToBack,
|
caMovedToBack,
|
||||||
caMovedToFront,
|
caMovedToFront,
|
||||||
selectCAOrThrow,
|
selectCanvasV2Slice,
|
||||||
selectControlAdaptersV2Slice,
|
} from 'features/controlLayers/store/canvasV2Slice';
|
||||||
} from 'features/controlLayers/store/controlAdaptersSlice';
|
import { selectCAOrThrow } from 'features/controlLayers/store/controlAdaptersReducers';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
@ -25,20 +25,17 @@ type Props = {
|
|||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectValidActions = createAppSelector(
|
const selectValidActions = createAppSelector([selectCanvasV2Slice, (canvasV2, id: string) => id], (canvasV2, id) => {
|
||||||
[selectControlAdaptersV2Slice, (caState, id: string) => id],
|
const ca = selectCAOrThrow(canvasV2, id);
|
||||||
(caState, id) => {
|
const caIndex = canvasV2.controlAdapters.indexOf(ca);
|
||||||
const ca = selectCAOrThrow(caState, id);
|
const caCount = canvasV2.controlAdapters.length;
|
||||||
const caIndex = caState.controlAdapters.indexOf(ca);
|
return {
|
||||||
const caCount = caState.controlAdapters.length;
|
canMoveForward: caIndex < caCount - 1,
|
||||||
return {
|
canMoveBackward: caIndex > 0,
|
||||||
canMoveForward: caIndex < caCount - 1,
|
canMoveToFront: caIndex < caCount - 1,
|
||||||
canMoveBackward: caIndex > 0,
|
canMoveToBack: caIndex > 0,
|
||||||
canMoveToFront: caIndex < caCount - 1,
|
};
|
||||||
canMoveToBack: caIndex > 0,
|
});
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const CAActionsMenu = memo(({ id }: Props) => {
|
export const CAActionsMenu = memo(({ id }: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
@ -6,7 +6,8 @@ import { CanvasEntityHeader } from 'features/controlLayers/components/common/Can
|
|||||||
import { CanvasEntityTitle } from 'features/controlLayers/components/common/CanvasEntityTitle';
|
import { CanvasEntityTitle } from 'features/controlLayers/components/common/CanvasEntityTitle';
|
||||||
import { CAActionsMenu } from 'features/controlLayers/components/ControlAdapter/CAActionsMenu';
|
import { CAActionsMenu } from 'features/controlLayers/components/ControlAdapter/CAActionsMenu';
|
||||||
import { CAOpacityAndFilter } from 'features/controlLayers/components/ControlAdapter/CAOpacityAndFilter';
|
import { CAOpacityAndFilter } from 'features/controlLayers/components/ControlAdapter/CAOpacityAndFilter';
|
||||||
import { caDeleted, caIsEnabledToggled, selectCAOrThrow } from 'features/controlLayers/store/controlAdaptersSlice';
|
import { caDeleted, caIsEnabledToggled } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
|
import { selectCAOrThrow } from 'features/controlLayers/store/controlAdaptersReducers';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
@ -18,7 +19,7 @@ type Props = {
|
|||||||
export const CAHeader = memo(({ id, onToggleVisibility }: Props) => {
|
export const CAHeader = memo(({ id, onToggleVisibility }: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const isEnabled = useAppSelector((s) => selectCAOrThrow(s.controlAdaptersV2, id).isEnabled);
|
const isEnabled = useAppSelector((s) => selectCAOrThrow(s.canvasV2, id).isEnabled);
|
||||||
const onToggleIsEnabled = useCallback(() => {
|
const onToggleIsEnabled = useCallback(() => {
|
||||||
dispatch(caIsEnabledToggled({ id }));
|
dispatch(caIsEnabledToggled({ id }));
|
||||||
}, [dispatch, id]);
|
}, [dispatch, id]);
|
||||||
|
@ -3,7 +3,7 @@ import { skipToken } from '@reduxjs/toolkit/query';
|
|||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIDndImage from 'common/components/IAIDndImage';
|
import IAIDndImage from 'common/components/IAIDndImage';
|
||||||
import IAIDndImageIcon from 'common/components/IAIDndImageIcon';
|
import IAIDndImageIcon from 'common/components/IAIDndImageIcon';
|
||||||
import { heightChanged, widthChanged } from 'features/controlLayers/store/controlLayersSlice';
|
import { heightChanged, widthChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import type { ControlAdapterData } from 'features/controlLayers/store/types';
|
import type { ControlAdapterData } from 'features/controlLayers/store/types';
|
||||||
import type { ImageDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
import type { ImageDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
||||||
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import { stopPropagation } from 'common/util/stopPropagation';
|
import { stopPropagation } from 'common/util/stopPropagation';
|
||||||
import { useCALayerOpacity } from 'features/controlLayers/hooks/layerStateHooks';
|
import { useCALayerOpacity } from 'features/controlLayers/hooks/layerStateHooks';
|
||||||
import { caFilterChanged, caOpacityChanged } from 'features/controlLayers/store/controlAdaptersSlice';
|
import { caFilterChanged, caOpacityChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import type { ChangeEvent } from 'react';
|
import type { ChangeEvent } from 'react';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
@ -16,8 +16,8 @@ import {
|
|||||||
caProcessedImageChanged,
|
caProcessedImageChanged,
|
||||||
caProcessorConfigChanged,
|
caProcessorConfigChanged,
|
||||||
caWeightChanged,
|
caWeightChanged,
|
||||||
selectCAOrThrow,
|
} from 'features/controlLayers/store/canvasV2Slice';
|
||||||
} from 'features/controlLayers/store/controlAdaptersSlice';
|
import { selectCAOrThrow } from 'features/controlLayers/store/controlAdaptersReducers';
|
||||||
import type { ControlModeV2, ProcessorConfig } from 'features/controlLayers/store/types';
|
import type { ControlModeV2, ProcessorConfig } from 'features/controlLayers/store/types';
|
||||||
import type { CAImageDropData } from 'features/dnd/types';
|
import type { CAImageDropData } from 'features/dnd/types';
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
@ -40,7 +40,7 @@ export const CASettings = memo(({ id }: Props) => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [isExpanded, toggleIsExpanded] = useToggle(false);
|
const [isExpanded, toggleIsExpanded] = useToggle(false);
|
||||||
|
|
||||||
const controlAdapter = useAppSelector((s) => selectCAOrThrow(s.controlAdaptersV2, id));
|
const controlAdapter = useAppSelector((s) => selectCAOrThrow(s.canvasV2, id));
|
||||||
|
|
||||||
const onChangeBeginEndStepPct = useCallback(
|
const onChangeBeginEndStepPct = useCallback(
|
||||||
(beginEndStepPct: [number, number]) => {
|
(beginEndStepPct: [number, number]) => {
|
||||||
|
@ -11,39 +11,22 @@ import { IPA } from 'features/controlLayers/components/IPAdapter/IPA';
|
|||||||
import { Layer } from 'features/controlLayers/components/Layer/Layer';
|
import { Layer } from 'features/controlLayers/components/Layer/Layer';
|
||||||
import { RG } from 'features/controlLayers/components/RegionalGuidance/RG';
|
import { RG } from 'features/controlLayers/components/RegionalGuidance/RG';
|
||||||
import { mapId } from 'features/controlLayers/konva/util';
|
import { mapId } from 'features/controlLayers/konva/util';
|
||||||
import { selectControlAdaptersV2Slice } from 'features/controlLayers/store/controlAdaptersSlice';
|
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { selectIPAdaptersSlice } from 'features/controlLayers/store/ipAdaptersSlice';
|
import { memo } from 'react';
|
||||||
import { selectLayersSlice } from 'features/controlLayers/store/layersSlice';
|
|
||||||
import { selectRegionalGuidanceSlice } from 'features/controlLayers/store/regionalGuidanceSlice';
|
|
||||||
import { memo, useMemo } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const selectRGIds = createMemoizedSelector(selectRegionalGuidanceSlice, (rgState) => {
|
const selectEntityIds = createMemoizedSelector(selectCanvasV2Slice, (canvasV2State) => {
|
||||||
return rgState.regions.map(mapId).reverse();
|
const rgIds = canvasV2State.regions.map(mapId).reverse();
|
||||||
});
|
const caIds = canvasV2State.controlAdapters.map(mapId).reverse();
|
||||||
|
const ipaIds = canvasV2State.ipAdapters.map(mapId).reverse();
|
||||||
const selectCAIds = createMemoizedSelector(selectControlAdaptersV2Slice, (caState) => {
|
const layerIds = canvasV2State.layers.map(mapId).reverse();
|
||||||
return caState.controlAdapters.map(mapId).reverse();
|
const entityCount = rgIds.length + caIds.length + ipaIds.length + layerIds.length;
|
||||||
});
|
return { rgIds, caIds, ipaIds, layerIds, entityCount };
|
||||||
|
|
||||||
const selectIPAIds = createMemoizedSelector(selectIPAdaptersSlice, (ipaState) => {
|
|
||||||
return ipaState.ipAdapters.map(mapId).reverse();
|
|
||||||
});
|
|
||||||
|
|
||||||
const selectLayerIds = createMemoizedSelector(selectLayersSlice, (layersState) => {
|
|
||||||
return layersState.layers.map(mapId).reverse();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ControlLayersPanelContent = memo(() => {
|
export const ControlLayersPanelContent = memo(() => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const rgIds = useAppSelector(selectRGIds);
|
const { rgIds, caIds, ipaIds, layerIds, entityCount } = useAppSelector(selectEntityIds);
|
||||||
const caIds = useAppSelector(selectCAIds);
|
|
||||||
const ipaIds = useAppSelector(selectIPAIds);
|
|
||||||
const layerIds = useAppSelector(selectLayerIds);
|
|
||||||
const entityCount = useMemo(
|
|
||||||
() => rgIds.length + caIds.length + ipaIds.length + layerIds.length,
|
|
||||||
[rgIds.length, caIds.length, ipaIds.length, layerIds.length]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex flexDir="column" gap={2} w="full" h="full">
|
<Flex flexDir="column" gap={2} w="full" h="full">
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
import { Button } from '@invoke-ai/ui-library';
|
import { Button } from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { caAllDeleted } from 'features/controlLayers/store/controlAdaptersSlice';
|
import { allEntitiesDeleted } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { ipaAllDeleted } from 'features/controlLayers/store/ipAdaptersSlice';
|
|
||||||
import { layerAllDeleted } from 'features/controlLayers/store/layersSlice';
|
|
||||||
import { rgAllDeleted } from 'features/controlLayers/store/regionalGuidanceSlice';
|
|
||||||
import { selectEntityCount } from 'features/controlLayers/store/selectors';
|
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { PiTrashSimpleBold } from 'react-icons/pi';
|
import { PiTrashSimpleBold } from 'react-icons/pi';
|
||||||
@ -12,12 +8,16 @@ import { PiTrashSimpleBold } from 'react-icons/pi';
|
|||||||
export const DeleteAllLayersButton = memo(() => {
|
export const DeleteAllLayersButton = memo(() => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const entityCount = useAppSelector(selectEntityCount);
|
const entityCount = useAppSelector((s) => {
|
||||||
|
return (
|
||||||
|
s.canvasV2.regions.length +
|
||||||
|
s.canvasV2.controlAdapters.length +
|
||||||
|
s.canvasV2.ipAdapters.length +
|
||||||
|
s.canvasV2.layers.length
|
||||||
|
);
|
||||||
|
});
|
||||||
const onClick = useCallback(() => {
|
const onClick = useCallback(() => {
|
||||||
dispatch(caAllDeleted());
|
dispatch(allEntitiesDeleted());
|
||||||
dispatch(rgAllDeleted());
|
|
||||||
dispatch(ipaAllDeleted());
|
|
||||||
dispatch(layerAllDeleted());
|
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from '@invoke-ai/ui-library';
|
} from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { eraserWidthChanged } from 'features/controlLayers/store/controlLayersSlice';
|
import { eraserWidthChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import { Flex, Popover, PopoverBody, PopoverContent, PopoverTrigger } from '@inv
|
|||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIColorPicker from 'common/components/IAIColorPicker';
|
import IAIColorPicker from 'common/components/IAIColorPicker';
|
||||||
import { rgbaColorToString } from 'common/util/colorCodeTransformers';
|
import { rgbaColorToString } from 'common/util/colorCodeTransformers';
|
||||||
import { fillChanged } from 'features/controlLayers/store/controlLayersSlice';
|
import { fillChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import type { RgbaColor } from 'features/controlLayers/store/types';
|
import type { RgbaColor } from 'features/controlLayers/store/types';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Box, Flex, Text } from '@invoke-ai/ui-library';
|
import { Box, Flex, Text } from '@invoke-ai/ui-library';
|
||||||
import { useStore } from '@nanostores/react';
|
import { useStore } from '@nanostores/react';
|
||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { $stageAttrs } from 'features/controlLayers/store/controlLayersSlice';
|
import { $stageAttrs } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { round } from 'lodash-es';
|
import { round } from 'lodash-es';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
|||||||
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
|
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
|
||||||
import { IPAHeader } from 'features/controlLayers/components/IPAdapter/IPAHeader';
|
import { IPAHeader } from 'features/controlLayers/components/IPAdapter/IPAHeader';
|
||||||
import { IPASettings } from 'features/controlLayers/components/IPAdapter/IPASettings';
|
import { IPASettings } from 'features/controlLayers/components/IPAdapter/IPASettings';
|
||||||
import { entitySelected } from 'features/controlLayers/store/controlLayersSlice';
|
import { entitySelected } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
@ -4,7 +4,8 @@ import { CanvasEntityDeleteButton } from 'features/controlLayers/components/comm
|
|||||||
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
|
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
|
||||||
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
||||||
import { CanvasEntityTitle } from 'features/controlLayers/components/common/CanvasEntityTitle';
|
import { CanvasEntityTitle } from 'features/controlLayers/components/common/CanvasEntityTitle';
|
||||||
import { ipaDeleted, ipaIsEnabledToggled, selectIPAOrThrow } from 'features/controlLayers/store/ipAdaptersSlice';
|
import { ipaDeleted, ipaIsEnabledToggled } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
|
import { selectIPAOrThrow } from 'features/controlLayers/store/ipAdaptersReducers';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
@ -16,7 +17,7 @@ type Props = {
|
|||||||
export const IPAHeader = memo(({ id, onToggleVisibility }: Props) => {
|
export const IPAHeader = memo(({ id, onToggleVisibility }: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const isEnabled = useAppSelector((s) => selectIPAOrThrow(s.ipAdapters, id).isEnabled);
|
const isEnabled = useAppSelector((s) => selectIPAOrThrow(s.canvasV2, id).isEnabled);
|
||||||
const onToggleIsEnabled = useCallback(() => {
|
const onToggleIsEnabled = useCallback(() => {
|
||||||
dispatch(ipaIsEnabledToggled({ id }));
|
dispatch(ipaIsEnabledToggled({ id }));
|
||||||
}, [dispatch, id]);
|
}, [dispatch, id]);
|
||||||
|
@ -3,7 +3,7 @@ import { skipToken } from '@reduxjs/toolkit/query';
|
|||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIDndImage from 'common/components/IAIDndImage';
|
import IAIDndImage from 'common/components/IAIDndImage';
|
||||||
import IAIDndImageIcon from 'common/components/IAIDndImageIcon';
|
import IAIDndImageIcon from 'common/components/IAIDndImageIcon';
|
||||||
import { heightChanged, widthChanged } from 'features/controlLayers/store/controlLayersSlice';
|
import { heightChanged, widthChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import type { ImageWithDims } from 'features/controlLayers/store/types';
|
import type { ImageWithDims } from 'features/controlLayers/store/types';
|
||||||
import type { ImageDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
import type { ImageDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
||||||
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
||||||
|
@ -11,8 +11,8 @@ import {
|
|||||||
ipaMethodChanged,
|
ipaMethodChanged,
|
||||||
ipaModelChanged,
|
ipaModelChanged,
|
||||||
ipaWeightChanged,
|
ipaWeightChanged,
|
||||||
selectIPAOrThrow,
|
} from 'features/controlLayers/store/canvasV2Slice';
|
||||||
} from 'features/controlLayers/store/ipAdaptersSlice';
|
import { selectIPAOrThrow } from 'features/controlLayers/store/ipAdaptersReducers';
|
||||||
import type { CLIPVisionModelV2, IPMethodV2 } from 'features/controlLayers/store/types';
|
import type { CLIPVisionModelV2, IPMethodV2 } from 'features/controlLayers/store/types';
|
||||||
import type { IPAImageDropData } from 'features/dnd/types';
|
import type { IPAImageDropData } from 'features/dnd/types';
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
@ -27,7 +27,7 @@ type Props = {
|
|||||||
|
|
||||||
export const IPASettings = memo(({ id }: Props) => {
|
export const IPASettings = memo(({ id }: Props) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const ipAdapter = useAppSelector((s) => selectIPAOrThrow(s.ipAdapters, id));
|
const ipAdapter = useAppSelector((s) => selectIPAOrThrow(s.canvasV2, id));
|
||||||
|
|
||||||
const onChangeBeginEndStepPct = useCallback(
|
const onChangeBeginEndStepPct = useCallback(
|
||||||
(beginEndStepPct: [number, number]) => {
|
(beginEndStepPct: [number, number]) => {
|
||||||
|
@ -3,7 +3,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
|||||||
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
|
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
|
||||||
import { LayerHeader } from 'features/controlLayers/components/Layer/LayerHeader';
|
import { LayerHeader } from 'features/controlLayers/components/Layer/LayerHeader';
|
||||||
import { LayerSettings } from 'features/controlLayers/components/Layer/LayerSettings';
|
import { LayerSettings } from 'features/controlLayers/components/Layer/LayerSettings';
|
||||||
import { entitySelected } from 'features/controlLayers/store/controlLayersSlice';
|
import { entitySelected } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
@ -8,9 +8,9 @@ import {
|
|||||||
layerMovedForwardOne,
|
layerMovedForwardOne,
|
||||||
layerMovedToBack,
|
layerMovedToBack,
|
||||||
layerMovedToFront,
|
layerMovedToFront,
|
||||||
selectLayerOrThrow,
|
selectCanvasV2Slice,
|
||||||
selectLayersSlice,
|
} from 'features/controlLayers/store/canvasV2Slice';
|
||||||
} from 'features/controlLayers/store/layersSlice';
|
import { selectLayerOrThrow } from 'features/controlLayers/store/layersReducers';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
@ -25,20 +25,17 @@ type Props = {
|
|||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectValidActions = createAppSelector(
|
const selectValidActions = createAppSelector([selectCanvasV2Slice, (canvasV2, id: string) => id], (canvasV2, id) => {
|
||||||
[selectLayersSlice, (layersState, id: string) => id],
|
const layer = selectLayerOrThrow(canvasV2, id);
|
||||||
(layersState, id) => {
|
const layerIndex = canvasV2.layers.indexOf(layer);
|
||||||
const layer = selectLayerOrThrow(layersState, id);
|
const layerCount = canvasV2.layers.length;
|
||||||
const layerIndex = layersState.layers.indexOf(layer);
|
return {
|
||||||
const layerCount = layersState.layers.length;
|
canMoveForward: layerIndex < layerCount - 1,
|
||||||
return {
|
canMoveBackward: layerIndex > 0,
|
||||||
canMoveForward: layerIndex < layerCount - 1,
|
canMoveToFront: layerIndex < layerCount - 1,
|
||||||
canMoveBackward: layerIndex > 0,
|
canMoveToBack: layerIndex > 0,
|
||||||
canMoveToFront: layerIndex < layerCount - 1,
|
};
|
||||||
canMoveToBack: layerIndex > 0,
|
});
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const LayerActionsMenu = memo(({ id }: Props) => {
|
export const LayerActionsMenu = memo(({ id }: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
@ -5,7 +5,8 @@ import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/com
|
|||||||
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
||||||
import { CanvasEntityTitle } from 'features/controlLayers/components/common/CanvasEntityTitle';
|
import { CanvasEntityTitle } from 'features/controlLayers/components/common/CanvasEntityTitle';
|
||||||
import { LayerActionsMenu } from 'features/controlLayers/components/Layer/LayerActionsMenu';
|
import { LayerActionsMenu } from 'features/controlLayers/components/Layer/LayerActionsMenu';
|
||||||
import { layerDeleted, layerIsEnabledToggled, selectLayerOrThrow } from 'features/controlLayers/store/layersSlice';
|
import { layerDeleted, layerIsEnabledToggled } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
|
import { selectLayerOrThrow } from 'features/controlLayers/store/layersReducers';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
@ -19,7 +20,7 @@ type Props = {
|
|||||||
export const LayerHeader = memo(({ id, onToggleVisibility }: Props) => {
|
export const LayerHeader = memo(({ id, onToggleVisibility }: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const isEnabled = useAppSelector((s) => selectLayerOrThrow(s.layers, id).isEnabled);
|
const isEnabled = useAppSelector((s) => selectLayerOrThrow(s.canvasV2, id).isEnabled);
|
||||||
const onToggleIsEnabled = useCallback(() => {
|
const onToggleIsEnabled = useCallback(() => {
|
||||||
dispatch(layerIsEnabledToggled({ id }));
|
dispatch(layerIsEnabledToggled({ id }));
|
||||||
}, [dispatch, id]);
|
}, [dispatch, id]);
|
||||||
|
@ -13,7 +13,8 @@ import {
|
|||||||
} from '@invoke-ai/ui-library';
|
} from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { stopPropagation } from 'common/util/stopPropagation';
|
import { stopPropagation } from 'common/util/stopPropagation';
|
||||||
import { layerOpacityChanged, selectLayerOrThrow } from 'features/controlLayers/store/layersSlice';
|
import { layerOpacityChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
|
import { selectLayerOrThrow } from 'features/controlLayers/store/layersReducers';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { PiDropHalfFill } from 'react-icons/pi';
|
import { PiDropHalfFill } from 'react-icons/pi';
|
||||||
@ -28,7 +29,7 @@ const formatPct = (v: number | string) => `${v} %`;
|
|||||||
export const LayerOpacity = memo(({ id }: Props) => {
|
export const LayerOpacity = memo(({ id }: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const opacity = useAppSelector((s) => Math.round(selectLayerOrThrow(s.layers, id).opacity * 100));
|
const opacity = useAppSelector((s) => Math.round(selectLayerOrThrow(s.canvasV2, id).opacity * 100));
|
||||||
const onChangeOpacity = useCallback(
|
const onChangeOpacity = useCallback(
|
||||||
(v: number) => {
|
(v: number) => {
|
||||||
dispatch(layerOpacityChanged({ id, opacity: v / 100 }));
|
dispatch(layerOpacityChanged({ id, opacity: v / 100 }));
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { CompositeNumberInput, CompositeSlider, Flex, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
import { CompositeNumberInput, CompositeSlider, Flex, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { rgGlobalOpacityChanged } from 'features/controlLayers/store/regionalGuidanceSlice';
|
import { rgGlobalOpacityChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ const formatPct = (v: number | string) => `${v} %`;
|
|||||||
export const RGGlobalOpacity = memo(() => {
|
export const RGGlobalOpacity = memo(() => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const opacity = useAppSelector((s) => Math.round(s.regionalGuidance.opacity * 100));
|
const opacity = useAppSelector((s) => Math.round(s.canvasV2.maskFillOpacity * 100));
|
||||||
const onChange = useCallback(
|
const onChange = useCallback(
|
||||||
(v: number) => {
|
(v: number) => {
|
||||||
dispatch(rgGlobalOpacityChanged({ opacity: v / 100 }));
|
dispatch(rgGlobalOpacityChanged({ opacity: v / 100 }));
|
||||||
|
@ -4,8 +4,8 @@ import { rgbColorToString } from 'features/canvas/util/colorToString';
|
|||||||
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
|
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
|
||||||
import { RGHeader } from 'features/controlLayers/components/RegionalGuidance/RGHeader';
|
import { RGHeader } from 'features/controlLayers/components/RegionalGuidance/RGHeader';
|
||||||
import { RGSettings } from 'features/controlLayers/components/RegionalGuidance/RGSettings';
|
import { RGSettings } from 'features/controlLayers/components/RegionalGuidance/RGSettings';
|
||||||
import { entitySelected } from 'features/controlLayers/store/controlLayersSlice';
|
import { entitySelected } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { selectRGOrThrow } from 'features/controlLayers/store/regionalGuidanceSlice';
|
import { selectRGOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -14,7 +14,7 @@ type Props = {
|
|||||||
|
|
||||||
export const RG = memo(({ id }: Props) => {
|
export const RG = memo(({ id }: Props) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const selectedBorderColor = useAppSelector((s) => rgbColorToString(selectRGOrThrow(s.regionalGuidance, id).fill));
|
const selectedBorderColor = useAppSelector((s) => rgbColorToString(selectRGOrThrow(s.canvasV2, id).fill));
|
||||||
const isSelected = useAppSelector((s) => s.canvasV2.selectedEntityIdentifier?.id === id);
|
const isSelected = useAppSelector((s) => s.canvasV2.selectedEntityIdentifier?.id === id);
|
||||||
const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: true });
|
const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: true });
|
||||||
const onSelect = useCallback(() => {
|
const onSelect = useCallback(() => {
|
||||||
|
@ -12,9 +12,9 @@ import {
|
|||||||
rgNegativePromptChanged,
|
rgNegativePromptChanged,
|
||||||
rgPositivePromptChanged,
|
rgPositivePromptChanged,
|
||||||
rgReset,
|
rgReset,
|
||||||
selectRegionalGuidanceSlice,
|
selectCanvasV2Slice,
|
||||||
selectRGOrThrow,
|
} from 'features/controlLayers/store/canvasV2Slice';
|
||||||
} from 'features/controlLayers/store/regionalGuidanceSlice';
|
import { selectRGOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
@ -32,11 +32,11 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const selectActionsValidity = createMemoizedAppSelector(
|
const selectActionsValidity = createMemoizedAppSelector(
|
||||||
[selectRegionalGuidanceSlice, (rgState, id: string) => id],
|
[selectCanvasV2Slice, (canvasV2, id: string) => id],
|
||||||
(rgState, id) => {
|
(canvasV2, id) => {
|
||||||
const rg = selectRGOrThrow(rgState, id);
|
const rg = selectRGOrThrow(canvasV2, id);
|
||||||
const rgIndex = rgState.regions.indexOf(rg);
|
const rgIndex = canvasV2.regions.indexOf(rg);
|
||||||
const rgCount = rgState.regions.length;
|
const rgCount = canvasV2.regions.length;
|
||||||
return {
|
return {
|
||||||
isMoveForwardOneDisabled: rgIndex < rgCount - 1,
|
isMoveForwardOneDisabled: rgIndex < rgCount - 1,
|
||||||
isMoveBackardOneDisabled: rgIndex > 0,
|
isMoveBackardOneDisabled: rgIndex > 0,
|
||||||
|
@ -5,7 +5,8 @@ import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/com
|
|||||||
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
||||||
import { CanvasEntityTitle } from 'features/controlLayers/components/common/CanvasEntityTitle';
|
import { CanvasEntityTitle } from 'features/controlLayers/components/common/CanvasEntityTitle';
|
||||||
import { RGActionsMenu } from 'features/controlLayers/components/RegionalGuidance/RGActionsMenu';
|
import { RGActionsMenu } from 'features/controlLayers/components/RegionalGuidance/RGActionsMenu';
|
||||||
import { rgDeleted, rgIsEnabledToggled, selectRGOrThrow } from 'features/controlLayers/store/regionalGuidanceSlice';
|
import { rgDeleted, rgIsEnabledToggled } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
|
import { selectRGOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
@ -20,8 +21,8 @@ type Props = {
|
|||||||
export const RGHeader = memo(({ id, onToggleVisibility }: Props) => {
|
export const RGHeader = memo(({ id, onToggleVisibility }: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const isEnabled = useAppSelector((s) => selectRGOrThrow(s.regionalGuidance, id).isEnabled);
|
const isEnabled = useAppSelector((s) => selectRGOrThrow(s.canvasV2, id).isEnabled);
|
||||||
const autoNegative = useAppSelector((s) => selectRGOrThrow(s.regionalGuidance, id).autoNegative);
|
const autoNegative = useAppSelector((s) => selectRGOrThrow(s.canvasV2, id).autoNegative);
|
||||||
const onToggleIsEnabled = useCallback(() => {
|
const onToggleIsEnabled = useCallback(() => {
|
||||||
dispatch(rgIsEnabledToggled({ id }));
|
dispatch(rgIsEnabledToggled({ id }));
|
||||||
}, [dispatch, id]);
|
}, [dispatch, id]);
|
||||||
|
@ -13,8 +13,8 @@ import {
|
|||||||
rgIPAdapterMethodChanged,
|
rgIPAdapterMethodChanged,
|
||||||
rgIPAdapterModelChanged,
|
rgIPAdapterModelChanged,
|
||||||
rgIPAdapterWeightChanged,
|
rgIPAdapterWeightChanged,
|
||||||
selectRGOrThrow,
|
} from 'features/controlLayers/store/canvasV2Slice';
|
||||||
} from 'features/controlLayers/store/regionalGuidanceSlice';
|
import { selectRGOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||||
import type { CLIPVisionModelV2, IPMethodV2 } from 'features/controlLayers/store/types';
|
import type { CLIPVisionModelV2, IPMethodV2 } from 'features/controlLayers/store/types';
|
||||||
import type { RGIPAdapterImageDropData } from 'features/dnd/types';
|
import type { RGIPAdapterImageDropData } from 'features/dnd/types';
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
@ -34,7 +34,7 @@ export const RGIPAdapterSettings = memo(({ id, ipAdapterId, ipAdapterNumber }: P
|
|||||||
dispatch(rgIPAdapterDeleted({ id, ipAdapterId }));
|
dispatch(rgIPAdapterDeleted({ id, ipAdapterId }));
|
||||||
}, [dispatch, ipAdapterId, id]);
|
}, [dispatch, ipAdapterId, id]);
|
||||||
const ipAdapter = useAppSelector((s) => {
|
const ipAdapter = useAppSelector((s) => {
|
||||||
const ipa = selectRGOrThrow(s.regionalGuidance, id).ipAdapters.find((ipa) => ipa.id === ipAdapterId);
|
const ipa = selectRGOrThrow(s.canvasV2, id).ipAdapters.find((ipa) => ipa.id === ipAdapterId);
|
||||||
assert(ipa, `Regional GuidanceIP Adapter with id ${ipAdapterId} not found`);
|
assert(ipa, `Regional GuidanceIP Adapter with id ${ipAdapterId} not found`);
|
||||||
return ipa;
|
return ipa;
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Divider, Flex } from '@invoke-ai/ui-library';
|
import { Divider, Flex } from '@invoke-ai/ui-library';
|
||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { RGIPAdapterSettings } from 'features/controlLayers/components/RegionalGuidance/RGIPAdapterSettings';
|
import { RGIPAdapterSettings } from 'features/controlLayers/components/RegionalGuidance/RGIPAdapterSettings';
|
||||||
import { selectRGOrThrow } from 'features/controlLayers/store/regionalGuidanceSlice';
|
import { selectRGOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -9,7 +9,7 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const RGIPAdapters = memo(({ id }: Props) => {
|
export const RGIPAdapters = memo(({ id }: Props) => {
|
||||||
const ipAdapterIds = useAppSelector((s) => selectRGOrThrow(s.regionalGuidance, id).ipAdapters.map(({ id }) => id));
|
const ipAdapterIds = useAppSelector((s) => selectRGOrThrow(s.canvasV2, id).ipAdapters.map(({ id }) => id));
|
||||||
|
|
||||||
if (ipAdapterIds.length === 0) {
|
if (ipAdapterIds.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -3,7 +3,8 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
|||||||
import RgbColorPicker from 'common/components/RgbColorPicker';
|
import RgbColorPicker from 'common/components/RgbColorPicker';
|
||||||
import { stopPropagation } from 'common/util/stopPropagation';
|
import { stopPropagation } from 'common/util/stopPropagation';
|
||||||
import { rgbColorToString } from 'features/canvas/util/colorToString';
|
import { rgbColorToString } from 'features/canvas/util/colorToString';
|
||||||
import { rgFillChanged, selectRGOrThrow } from 'features/controlLayers/store/regionalGuidanceSlice';
|
import { rgFillChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
|
import { selectRGOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import type { RgbColor } from 'react-colorful';
|
import type { RgbColor } from 'react-colorful';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -15,7 +16,7 @@ type Props = {
|
|||||||
export const RGMaskFillColorPicker = memo(({ id }: Props) => {
|
export const RGMaskFillColorPicker = memo(({ id }: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const fill = useAppSelector((s) => selectRGOrThrow(s.regionalGuidance, id).fill);
|
const fill = useAppSelector((s) => selectRGOrThrow(s.canvasV2, id).fill);
|
||||||
const onChange = useCallback(
|
const onChange = useCallback(
|
||||||
(fill: RgbColor) => {
|
(fill: RgbColor) => {
|
||||||
dispatch(rgFillChanged({ id, fill }));
|
dispatch(rgFillChanged({ id, fill }));
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { Box, Textarea } from '@invoke-ai/ui-library';
|
import { Box, Textarea } from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { RGDeletePromptButton } from 'features/controlLayers/components/RegionalGuidance/RGDeletePromptButton';
|
import { RGDeletePromptButton } from 'features/controlLayers/components/RegionalGuidance/RGDeletePromptButton';
|
||||||
import { rgNegativePromptChanged, selectRGOrThrow } from 'features/controlLayers/store/regionalGuidanceSlice';
|
import { rgNegativePromptChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
|
import { selectRGOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||||
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
||||||
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
|
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
|
||||||
import { PromptPopover } from 'features/prompt/PromptPopover';
|
import { PromptPopover } from 'features/prompt/PromptPopover';
|
||||||
@ -14,7 +15,7 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const RGNegativePrompt = memo(({ id }: Props) => {
|
export const RGNegativePrompt = memo(({ id }: Props) => {
|
||||||
const prompt = useAppSelector((s) => selectRGOrThrow(s.regionalGuidance, id).negativePrompt ?? '');
|
const prompt = useAppSelector((s) => selectRGOrThrow(s.canvasV2, id).negativePrompt ?? '');
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { Box, Textarea } from '@invoke-ai/ui-library';
|
import { Box, Textarea } from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { RGDeletePromptButton } from 'features/controlLayers/components/RegionalGuidance/RGDeletePromptButton';
|
import { RGDeletePromptButton } from 'features/controlLayers/components/RegionalGuidance/RGDeletePromptButton';
|
||||||
import { rgPositivePromptChanged, selectRGOrThrow } from 'features/controlLayers/store/regionalGuidanceSlice';
|
import { rgPositivePromptChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
|
import { selectRGOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||||
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
||||||
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
|
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
|
||||||
import { PromptPopover } from 'features/prompt/PromptPopover';
|
import { PromptPopover } from 'features/prompt/PromptPopover';
|
||||||
@ -14,7 +15,7 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const RGPositivePrompt = memo(({ id }: Props) => {
|
export const RGPositivePrompt = memo(({ id }: Props) => {
|
||||||
const prompt = useAppSelector((s) => selectRGOrThrow(s.regionalGuidance, id).positivePrompt ?? '');
|
const prompt = useAppSelector((s) => selectRGOrThrow(s.canvasV2, id).positivePrompt ?? '');
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { AddPromptButtons } from 'features/controlLayers/components/AddPromptButtons';
|
import { AddPromptButtons } from 'features/controlLayers/components/AddPromptButtons';
|
||||||
import { CanvasEntitySettings } from 'features/controlLayers/components/common/CanvasEntitySettings';
|
import { CanvasEntitySettings } from 'features/controlLayers/components/common/CanvasEntitySettings';
|
||||||
import { selectRGOrThrow } from 'features/controlLayers/store/regionalGuidanceSlice';
|
import { selectRGOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
import { RGIPAdapters } from './RGIPAdapters';
|
import { RGIPAdapters } from './RGIPAdapters';
|
||||||
@ -13,9 +13,9 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const RGSettings = memo(({ id }: Props) => {
|
export const RGSettings = memo(({ id }: Props) => {
|
||||||
const hasPositivePrompt = useAppSelector((s) => selectRGOrThrow(s.regionalGuidance, id).positivePrompt !== null);
|
const hasPositivePrompt = useAppSelector((s) => selectRGOrThrow(s.canvasV2, id).positivePrompt !== null);
|
||||||
const hasNegativePrompt = useAppSelector((s) => selectRGOrThrow(s.regionalGuidance, id).negativePrompt !== null);
|
const hasNegativePrompt = useAppSelector((s) => selectRGOrThrow(s.canvasV2, id).negativePrompt !== null);
|
||||||
const hasIPAdapters = useAppSelector((s) => selectRGOrThrow(s.regionalGuidance, id).ipAdapters.length > 0);
|
const hasIPAdapters = useAppSelector((s) => selectRGOrThrow(s.canvasV2, id).ipAdapters.length > 0);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CanvasEntitySettings>
|
<CanvasEntitySettings>
|
||||||
|
@ -12,7 +12,8 @@ import {
|
|||||||
} from '@invoke-ai/ui-library';
|
} from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { stopPropagation } from 'common/util/stopPropagation';
|
import { stopPropagation } from 'common/util/stopPropagation';
|
||||||
import { rgAutoNegativeChanged, selectRGOrThrow } from 'features/controlLayers/store/regionalGuidanceSlice';
|
import { rgAutoNegativeChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
|
import { selectRGOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||||
import type { ChangeEvent } from 'react';
|
import type { ChangeEvent } from 'react';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -25,7 +26,7 @@ type Props = {
|
|||||||
export const RGSettingsPopover = memo(({ id }: Props) => {
|
export const RGSettingsPopover = memo(({ id }: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const autoNegative = useAppSelector((s) => selectRGOrThrow(s.regionalGuidance, id).autoNegative);
|
const autoNegative = useAppSelector((s) => selectRGOrThrow(s.canvasV2, id).autoNegative);
|
||||||
const onChange = useCallback(
|
const onChange = useCallback(
|
||||||
(e: ChangeEvent<HTMLInputElement>) => {
|
(e: ChangeEvent<HTMLInputElement>) => {
|
||||||
dispatch(rgAutoNegativeChanged({ id, autoNegative: e.target.checked ? 'invert' : 'off' }));
|
dispatch(rgAutoNegativeChanged({ id, autoNegative: e.target.checked ? 'invert' : 'off' }));
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
import { $alt, $ctrl, $meta, $shift, Box, Flex, Heading } from '@invoke-ai/ui-library';
|
import { $alt, $ctrl, $meta, $shift, Box, Flex, Heading } from '@invoke-ai/ui-library';
|
||||||
import { useStore } from '@nanostores/react';
|
import { useStore } from '@nanostores/react';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
|
||||||
import { logger } from 'app/logging/logger';
|
import { logger } from 'app/logging/logger';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { rgbaColorToString } from 'features/canvas/util/colorToString';
|
|
||||||
import { HeadsUpDisplay } from 'features/controlLayers/components/HeadsUpDisplay';
|
import { HeadsUpDisplay } from 'features/controlLayers/components/HeadsUpDisplay';
|
||||||
import { TRANSPARENCY_CHECKER_PATTERN } from 'features/controlLayers/konva/constants';
|
import { TRANSPARENCY_CHECKER_PATTERN } from 'features/controlLayers/konva/constants';
|
||||||
import { setStageEventHandlers } from 'features/controlLayers/konva/events';
|
import { setStageEventHandlers } from 'features/controlLayers/konva/events';
|
||||||
import { debouncedRenderers, renderers as normalRenderers } from 'features/controlLayers/konva/renderers/layers';
|
import { debouncedRenderers, renderers as normalRenderers } from 'features/controlLayers/konva/renderers/layers';
|
||||||
import { caBboxChanged, caTranslated } from 'features/controlLayers/store/controlAdaptersSlice';
|
|
||||||
import {
|
import {
|
||||||
$bbox,
|
$bbox,
|
||||||
$currentFill,
|
$currentFill,
|
||||||
@ -23,29 +20,26 @@ import {
|
|||||||
$toolState,
|
$toolState,
|
||||||
bboxChanged,
|
bboxChanged,
|
||||||
brushWidthChanged,
|
brushWidthChanged,
|
||||||
|
caBboxChanged,
|
||||||
|
caTranslated,
|
||||||
eraserWidthChanged,
|
eraserWidthChanged,
|
||||||
selectCanvasV2Slice,
|
|
||||||
toolBufferChanged,
|
|
||||||
toolChanged,
|
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
|
||||||
import {
|
|
||||||
layerBboxChanged,
|
layerBboxChanged,
|
||||||
layerBrushLineAdded,
|
layerBrushLineAdded,
|
||||||
layerEraserLineAdded,
|
layerEraserLineAdded,
|
||||||
layerLinePointAdded,
|
layerLinePointAdded,
|
||||||
layerRectAdded,
|
layerRectAdded,
|
||||||
layerTranslated,
|
layerTranslated,
|
||||||
selectLayersSlice,
|
|
||||||
} from 'features/controlLayers/store/layersSlice';
|
|
||||||
import {
|
|
||||||
rgBboxChanged,
|
rgBboxChanged,
|
||||||
rgBrushLineAdded,
|
rgBrushLineAdded,
|
||||||
rgEraserLineAdded,
|
rgEraserLineAdded,
|
||||||
rgLinePointAdded,
|
rgLinePointAdded,
|
||||||
rgRectAdded,
|
rgRectAdded,
|
||||||
rgTranslated,
|
rgTranslated,
|
||||||
selectRegionalGuidanceSlice,
|
selectCanvasV2Slice,
|
||||||
} from 'features/controlLayers/store/regionalGuidanceSlice';
|
toolBufferChanged,
|
||||||
|
toolChanged,
|
||||||
|
} from 'features/controlLayers/store/canvasV2Slice';
|
||||||
|
import { selectEntityCount } from 'features/controlLayers/store/selectors';
|
||||||
import type {
|
import type {
|
||||||
BboxChangedArg,
|
BboxChangedArg,
|
||||||
BrushLineAddedArg,
|
BrushLineAddedArg,
|
||||||
@ -69,62 +63,42 @@ Konva.showWarnings = false;
|
|||||||
|
|
||||||
const log = logger('controlLayers');
|
const log = logger('controlLayers');
|
||||||
|
|
||||||
const selectBrushFill = createSelector(
|
|
||||||
selectCanvasV2Slice,
|
|
||||||
selectLayersSlice,
|
|
||||||
selectRegionalGuidanceSlice,
|
|
||||||
(canvas, layers, regionalGuidance) => {
|
|
||||||
const rg = regionalGuidance.regions.find((i) => i.id === canvas.selectedEntityIdentifier?.id);
|
|
||||||
|
|
||||||
if (rg) {
|
|
||||||
return rgbaColorToString({ ...rg.fill, a: regionalGuidance.opacity });
|
|
||||||
}
|
|
||||||
|
|
||||||
return rgbaColorToString(canvas.tool.fill);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const useStageRenderer = (stage: Konva.Stage, container: HTMLDivElement | null, asPreview: boolean) => {
|
const useStageRenderer = (stage: Konva.Stage, container: HTMLDivElement | null, asPreview: boolean) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const canvasV2State = useAppSelector(selectCanvasV2Slice);
|
const canvasV2State = useAppSelector(selectCanvasV2Slice);
|
||||||
const layersState = useAppSelector((s) => s.layers);
|
|
||||||
const controlAdaptersState = useAppSelector((s) => s.controlAdaptersV2);
|
|
||||||
const ipAdaptersState = useAppSelector((s) => s.ipAdapters);
|
|
||||||
const regionalGuidanceState = useAppSelector((s) => s.regionalGuidance);
|
|
||||||
const lastCursorPos = useStore($lastCursorPos);
|
const lastCursorPos = useStore($lastCursorPos);
|
||||||
const lastMouseDownPos = useStore($lastMouseDownPos);
|
const lastMouseDownPos = useStore($lastMouseDownPos);
|
||||||
const isMouseDown = useStore($isMouseDown);
|
const isMouseDown = useStore($isMouseDown);
|
||||||
const isDrawing = useStore($isDrawing);
|
const isDrawing = useStore($isDrawing);
|
||||||
const brushColor = useAppSelector(selectBrushFill);
|
|
||||||
const selectedEntity = useMemo(() => {
|
const selectedEntity = useMemo(() => {
|
||||||
const identifier = canvasV2State.selectedEntityIdentifier;
|
const identifier = canvasV2State.selectedEntityIdentifier;
|
||||||
if (!identifier) {
|
if (!identifier) {
|
||||||
return null;
|
return null;
|
||||||
} else if (identifier.type === 'layer') {
|
} else if (identifier.type === 'layer') {
|
||||||
return layersState.layers.find((i) => i.id === identifier.id) ?? null;
|
return canvasV2State.layers.find((i) => i.id === identifier.id) ?? null;
|
||||||
} else if (identifier.type === 'control_adapter') {
|
} else if (identifier.type === 'control_adapter') {
|
||||||
return controlAdaptersState.controlAdapters.find((i) => i.id === identifier.id) ?? null;
|
return canvasV2State.controlAdapters.find((i) => i.id === identifier.id) ?? null;
|
||||||
} else if (identifier.type === 'ip_adapter') {
|
} else if (identifier.type === 'ip_adapter') {
|
||||||
return ipAdaptersState.ipAdapters.find((i) => i.id === identifier.id) ?? null;
|
return canvasV2State.ipAdapters.find((i) => i.id === identifier.id) ?? null;
|
||||||
} else if (identifier.type === 'regional_guidance') {
|
} else if (identifier.type === 'regional_guidance') {
|
||||||
return regionalGuidanceState.regions.find((i) => i.id === identifier.id) ?? null;
|
return canvasV2State.regions.find((i) => i.id === identifier.id) ?? null;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
|
canvasV2State.controlAdapters,
|
||||||
|
canvasV2State.ipAdapters,
|
||||||
|
canvasV2State.layers,
|
||||||
|
canvasV2State.regions,
|
||||||
canvasV2State.selectedEntityIdentifier,
|
canvasV2State.selectedEntityIdentifier,
|
||||||
controlAdaptersState.controlAdapters,
|
|
||||||
ipAdaptersState.ipAdapters,
|
|
||||||
layersState.layers,
|
|
||||||
regionalGuidanceState.regions,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const currentFill = useMemo(() => {
|
const currentFill = useMemo(() => {
|
||||||
if (selectedEntity && selectedEntity.type === 'regional_guidance') {
|
if (selectedEntity && selectedEntity.type === 'regional_guidance') {
|
||||||
return { ...selectedEntity.fill, a: regionalGuidanceState.opacity };
|
return { ...selectedEntity.fill, a: canvasV2State.maskFillOpacity };
|
||||||
}
|
}
|
||||||
return canvasV2State.tool.fill;
|
return canvasV2State.tool.fill;
|
||||||
}, [canvasV2State.tool.fill, regionalGuidanceState.opacity, selectedEntity]);
|
}, [canvasV2State.maskFillOpacity, canvasV2State.tool.fill, selectedEntity]);
|
||||||
|
|
||||||
const renderers = useMemo(() => (asPreview ? debouncedRenderers : normalRenderers), [asPreview]);
|
const renderers = useMemo(() => (asPreview ? debouncedRenderers : normalRenderers), [asPreview]);
|
||||||
const dpr = useDevicePixelRatio({ round: false });
|
const dpr = useDevicePixelRatio({ round: false });
|
||||||
@ -341,7 +315,6 @@ const useStageRenderer = (stage: Konva.Stage, container: HTMLDivElement | null,
|
|||||||
);
|
);
|
||||||
}, [
|
}, [
|
||||||
asPreview,
|
asPreview,
|
||||||
brushColor,
|
|
||||||
canvasV2State.tool,
|
canvasV2State.tool,
|
||||||
currentFill,
|
currentFill,
|
||||||
isDrawing,
|
isDrawing,
|
||||||
@ -376,10 +349,10 @@ const useStageRenderer = (stage: Konva.Stage, container: HTMLDivElement | null,
|
|||||||
log.trace('Rendering layers');
|
log.trace('Rendering layers');
|
||||||
renderers.renderLayers(
|
renderers.renderLayers(
|
||||||
stage,
|
stage,
|
||||||
layersState.layers,
|
canvasV2State.layers,
|
||||||
controlAdaptersState.controlAdapters,
|
canvasV2State.controlAdapters,
|
||||||
regionalGuidanceState.regions,
|
canvasV2State.regions,
|
||||||
regionalGuidanceState.opacity,
|
canvasV2State.maskFillOpacity,
|
||||||
canvasV2State.tool.selected,
|
canvasV2State.tool.selected,
|
||||||
selectedEntity,
|
selectedEntity,
|
||||||
getImageDTO,
|
getImageDTO,
|
||||||
@ -388,13 +361,13 @@ const useStageRenderer = (stage: Konva.Stage, container: HTMLDivElement | null,
|
|||||||
}, [
|
}, [
|
||||||
stage,
|
stage,
|
||||||
renderers,
|
renderers,
|
||||||
layersState.layers,
|
|
||||||
controlAdaptersState.controlAdapters,
|
|
||||||
regionalGuidanceState.regions,
|
|
||||||
regionalGuidanceState.opacity,
|
|
||||||
onPosChanged,
|
onPosChanged,
|
||||||
canvasV2State.tool.selected,
|
canvasV2State.tool.selected,
|
||||||
selectedEntity,
|
selectedEntity,
|
||||||
|
canvasV2State.layers,
|
||||||
|
canvasV2State.controlAdapters,
|
||||||
|
canvasV2State.regions,
|
||||||
|
canvasV2State.maskFillOpacity,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// useLayoutEffect(() => {
|
// useLayoutEffect(() => {
|
||||||
@ -450,7 +423,7 @@ export const StageComponent = memo(({ asPreview = false }: Props) => {
|
|||||||
backgroundRepeat="repeat"
|
backgroundRepeat="repeat"
|
||||||
opacity={0.2}
|
opacity={0.2}
|
||||||
/>
|
/>
|
||||||
{!asPreview && <NoLayersFallback />}
|
{!asPreview && <NoEntitiesFallback />}
|
||||||
<Flex
|
<Flex
|
||||||
position="absolute"
|
position="absolute"
|
||||||
top={0}
|
top={0}
|
||||||
@ -474,10 +447,11 @@ export const StageComponent = memo(({ asPreview = false }: Props) => {
|
|||||||
|
|
||||||
StageComponent.displayName = 'StageComponent';
|
StageComponent.displayName = 'StageComponent';
|
||||||
|
|
||||||
const NoLayersFallback = () => {
|
const NoEntitiesFallback = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const layerCount = useAppSelector((s) => s.layers.layers.length);
|
const entityCount = useAppSelector(selectEntityCount);
|
||||||
if (layerCount) {
|
|
||||||
|
if (entityCount) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
import { ButtonGroup, IconButton } from '@invoke-ai/ui-library';
|
import { ButtonGroup, IconButton } from '@invoke-ai/ui-library';
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { caDeleted } from 'features/controlLayers/store/controlAdaptersSlice';
|
import {
|
||||||
import { selectCanvasV2Slice, toolChanged } from 'features/controlLayers/store/controlLayersSlice';
|
caDeleted,
|
||||||
import { ipaDeleted } from 'features/controlLayers/store/ipAdaptersSlice';
|
ipaDeleted,
|
||||||
import { layerDeleted, layerReset } from 'features/controlLayers/store/layersSlice';
|
layerDeleted,
|
||||||
import { rgDeleted, rgReset } from 'features/controlLayers/store/regionalGuidanceSlice';
|
layerReset,
|
||||||
|
rgDeleted,
|
||||||
|
rgReset,
|
||||||
|
selectCanvasV2Slice,
|
||||||
|
toolChanged,
|
||||||
|
} from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable i18next/no-literal-string */
|
/* eslint-disable i18next/no-literal-string */
|
||||||
import { ButtonGroup, IconButton } from '@invoke-ai/ui-library';
|
import { ButtonGroup, IconButton } from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { redo, undo } from 'features/controlLayers/store/controlLayersSlice';
|
import { redo, undo } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { caAdded } from 'features/controlLayers/store/controlAdaptersSlice';
|
import { caAdded, ipaAdded, rgIPAdapterAdded } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { ipaAdded } from 'features/controlLayers/store/ipAdaptersSlice';
|
|
||||||
import { rgIPAdapterAdded } from 'features/controlLayers/store/regionalGuidanceSlice';
|
|
||||||
import {
|
import {
|
||||||
buildControlNet,
|
buildControlNet,
|
||||||
buildIPAdapter,
|
buildIPAdapter,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/controlLayersSlice';
|
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { isControlAdapterLayer, isRegionalGuidanceLayer } from 'features/controlLayers/store/types';
|
import { isControlAdapterLayer, isRegionalGuidanceLayer } from 'features/controlLayers/store/types';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { assert } from 'tsafe';
|
import { assert } from 'tsafe';
|
||||||
|
@ -3,6 +3,10 @@ import { createSlice } from '@reduxjs/toolkit';
|
|||||||
import type { PersistConfig, RootState } from 'app/store/store';
|
import type { PersistConfig, RootState } from 'app/store/store';
|
||||||
import { deepClone } from 'common/util/deepClone';
|
import { deepClone } from 'common/util/deepClone';
|
||||||
import { roundDownToMultiple } from 'common/util/roundDownToMultiple';
|
import { roundDownToMultiple } from 'common/util/roundDownToMultiple';
|
||||||
|
import { controlAdaptersReducers } from 'features/controlLayers/store/controlAdaptersReducers';
|
||||||
|
import { ipAdaptersReducers } from 'features/controlLayers/store/ipAdaptersReducers';
|
||||||
|
import { layersReducers } from 'features/controlLayers/store/layersReducers';
|
||||||
|
import { regionsReducers } from 'features/controlLayers/store/regionsReducers';
|
||||||
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
||||||
import { initialAspectRatioState } from 'features/parameters/components/ImageSize/constants';
|
import { initialAspectRatioState } from 'features/parameters/components/ImageSize/constants';
|
||||||
import type { AspectRatioState } from 'features/parameters/components/ImageSize/types';
|
import type { AspectRatioState } from 'features/parameters/components/ImageSize/types';
|
||||||
@ -47,12 +51,21 @@ const initialState: CanvasV2State = {
|
|||||||
width: 512,
|
width: 512,
|
||||||
height: 512,
|
height: 512,
|
||||||
},
|
},
|
||||||
|
controlAdapters: [],
|
||||||
|
ipAdapters: [],
|
||||||
|
regions: [],
|
||||||
|
layers: [],
|
||||||
|
maskFillOpacity: 0.3,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const canvasV2Slice = createSlice({
|
export const canvasV2Slice = createSlice({
|
||||||
name: 'canvasV2',
|
name: 'canvasV2',
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
|
...layersReducers,
|
||||||
|
...ipAdaptersReducers,
|
||||||
|
...controlAdaptersReducers,
|
||||||
|
...regionsReducers,
|
||||||
positivePromptChanged: (state, action: PayloadAction<string>) => {
|
positivePromptChanged: (state, action: PayloadAction<string>) => {
|
||||||
state.prompts.positivePrompt = action.payload;
|
state.prompts.positivePrompt = action.payload;
|
||||||
},
|
},
|
||||||
@ -110,9 +123,18 @@ export const canvasV2Slice = createSlice({
|
|||||||
toolBufferChanged: (state, action: PayloadAction<Tool | null>) => {
|
toolBufferChanged: (state, action: PayloadAction<Tool | null>) => {
|
||||||
state.tool.selectedBuffer = action.payload;
|
state.tool.selectedBuffer = action.payload;
|
||||||
},
|
},
|
||||||
|
maskFillOpacityChanged: (state, action: PayloadAction<number>) => {
|
||||||
|
state.maskFillOpacity = action.payload;
|
||||||
|
},
|
||||||
entitySelected: (state, action: PayloadAction<CanvasEntityIdentifier>) => {
|
entitySelected: (state, action: PayloadAction<CanvasEntityIdentifier>) => {
|
||||||
state.selectedEntityIdentifier = action.payload;
|
state.selectedEntityIdentifier = action.payload;
|
||||||
},
|
},
|
||||||
|
allEntitiesDeleted: (state) => {
|
||||||
|
state.regions = [];
|
||||||
|
state.layers = [];
|
||||||
|
state.ipAdapters = [];
|
||||||
|
state.controlAdapters = [];
|
||||||
|
},
|
||||||
},
|
},
|
||||||
extraReducers(builder) {
|
extraReducers(builder) {
|
||||||
builder.addCase(modelChanged, (state, action) => {
|
builder.addCase(modelChanged, (state, action) => {
|
||||||
@ -148,7 +170,88 @@ export const {
|
|||||||
invertScrollChanged,
|
invertScrollChanged,
|
||||||
toolChanged,
|
toolChanged,
|
||||||
toolBufferChanged,
|
toolBufferChanged,
|
||||||
|
maskFillOpacityChanged,
|
||||||
entitySelected,
|
entitySelected,
|
||||||
|
allEntitiesDeleted,
|
||||||
|
// layers
|
||||||
|
layerAdded,
|
||||||
|
layerDeleted,
|
||||||
|
layerReset,
|
||||||
|
layerMovedForwardOne,
|
||||||
|
layerMovedToFront,
|
||||||
|
layerMovedBackwardOne,
|
||||||
|
layerMovedToBack,
|
||||||
|
layerIsEnabledToggled,
|
||||||
|
layerOpacityChanged,
|
||||||
|
layerTranslated,
|
||||||
|
layerBboxChanged,
|
||||||
|
layerBrushLineAdded,
|
||||||
|
layerEraserLineAdded,
|
||||||
|
layerLinePointAdded,
|
||||||
|
layerRectAdded,
|
||||||
|
layerImageAdded,
|
||||||
|
// IP Adapters
|
||||||
|
ipaAdded,
|
||||||
|
ipaRecalled,
|
||||||
|
ipaIsEnabledToggled,
|
||||||
|
ipaDeleted,
|
||||||
|
ipaImageChanged,
|
||||||
|
ipaMethodChanged,
|
||||||
|
ipaModelChanged,
|
||||||
|
ipaCLIPVisionModelChanged,
|
||||||
|
ipaWeightChanged,
|
||||||
|
ipaBeginEndStepPctChanged,
|
||||||
|
// Control Adapters
|
||||||
|
caAdded,
|
||||||
|
caBboxChanged,
|
||||||
|
caDeleted,
|
||||||
|
caIsEnabledToggled,
|
||||||
|
caMovedBackwardOne,
|
||||||
|
caMovedForwardOne,
|
||||||
|
caMovedToBack,
|
||||||
|
caMovedToFront,
|
||||||
|
caOpacityChanged,
|
||||||
|
caTranslated,
|
||||||
|
caRecalled,
|
||||||
|
caImageChanged,
|
||||||
|
caProcessedImageChanged,
|
||||||
|
caModelChanged,
|
||||||
|
caControlModeChanged,
|
||||||
|
caProcessorConfigChanged,
|
||||||
|
caFilterChanged,
|
||||||
|
caProcessorPendingBatchIdChanged,
|
||||||
|
caWeightChanged,
|
||||||
|
caBeginEndStepPctChanged,
|
||||||
|
// Regions
|
||||||
|
rgAdded,
|
||||||
|
rgRecalled,
|
||||||
|
rgReset,
|
||||||
|
rgIsEnabledToggled,
|
||||||
|
rgTranslated,
|
||||||
|
rgBboxChanged,
|
||||||
|
rgDeleted,
|
||||||
|
rgGlobalOpacityChanged,
|
||||||
|
rgMovedForwardOne,
|
||||||
|
rgMovedToFront,
|
||||||
|
rgMovedBackwardOne,
|
||||||
|
rgMovedToBack,
|
||||||
|
rgPositivePromptChanged,
|
||||||
|
rgNegativePromptChanged,
|
||||||
|
rgFillChanged,
|
||||||
|
rgMaskImageUploaded,
|
||||||
|
rgAutoNegativeChanged,
|
||||||
|
rgIPAdapterAdded,
|
||||||
|
rgIPAdapterDeleted,
|
||||||
|
rgIPAdapterImageChanged,
|
||||||
|
rgIPAdapterWeightChanged,
|
||||||
|
rgIPAdapterBeginEndStepPctChanged,
|
||||||
|
rgIPAdapterMethodChanged,
|
||||||
|
rgIPAdapterModelChanged,
|
||||||
|
rgIPAdapterCLIPVisionModelChanged,
|
||||||
|
rgBrushLineAdded,
|
||||||
|
rgEraserLineAdded,
|
||||||
|
rgLinePointAdded,
|
||||||
|
rgRectAdded,
|
||||||
} = canvasV2Slice.actions;
|
} = canvasV2Slice.actions;
|
||||||
|
|
||||||
export const selectCanvasV2Slice = (state: RootState) => state.canvasV2;
|
export const selectCanvasV2Slice = (state: RootState) => state.canvasV2;
|
@ -0,0 +1,238 @@
|
|||||||
|
import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit';
|
||||||
|
import { moveOneToEnd, moveOneToStart, moveToEnd, moveToStart } from 'common/util/arrayUtils';
|
||||||
|
import { zModelIdentifierField } from 'features/nodes/types/common';
|
||||||
|
import type { IRect } from 'konva/lib/types';
|
||||||
|
import { isEqual } from 'lodash-es';
|
||||||
|
import type { ControlNetModelConfig, ImageDTO, T2IAdapterModelConfig } from 'services/api/types';
|
||||||
|
import { assert } from 'tsafe';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
CanvasV2State,
|
||||||
|
ControlAdapterConfig,
|
||||||
|
ControlAdapterData,
|
||||||
|
ControlModeV2,
|
||||||
|
Filter,
|
||||||
|
ProcessorConfig,
|
||||||
|
} from './types';
|
||||||
|
import { buildControlAdapterProcessorV2, imageDTOToImageWithDims } from './types';
|
||||||
|
|
||||||
|
export const selectCA = (state: CanvasV2State, id: string) => state.controlAdapters.find((ca) => ca.id === id);
|
||||||
|
export const selectCAOrThrow = (state: CanvasV2State, id: string) => {
|
||||||
|
const ca = selectCA(state, id);
|
||||||
|
assert(ca, `Control Adapter with id ${id} not found`);
|
||||||
|
return ca;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const controlAdaptersReducers = {
|
||||||
|
caAdded: {
|
||||||
|
reducer: (state, action: PayloadAction<{ id: string; config: ControlAdapterConfig }>) => {
|
||||||
|
const { id, config } = action.payload;
|
||||||
|
state.controlAdapters.push({
|
||||||
|
id,
|
||||||
|
type: 'control_adapter',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
bbox: null,
|
||||||
|
bboxNeedsUpdate: false,
|
||||||
|
isEnabled: true,
|
||||||
|
opacity: 1,
|
||||||
|
filter: 'LightnessToAlphaFilter',
|
||||||
|
processorPendingBatchId: null,
|
||||||
|
...config,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
prepare: (config: ControlAdapterConfig) => ({
|
||||||
|
payload: { id: uuidv4(), config },
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
caRecalled: (state, action: PayloadAction<{ data: ControlAdapterData }>) => {
|
||||||
|
state.controlAdapters.push(action.payload.data);
|
||||||
|
},
|
||||||
|
caIsEnabledToggled: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const ca = selectCA(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.isEnabled = !ca.isEnabled;
|
||||||
|
},
|
||||||
|
caTranslated: (state, action: PayloadAction<{ id: string; x: number; y: number }>) => {
|
||||||
|
const { id, x, y } = action.payload;
|
||||||
|
const ca = selectCA(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.x = x;
|
||||||
|
ca.y = y;
|
||||||
|
},
|
||||||
|
caBboxChanged: (state, action: PayloadAction<{ id: string; bbox: IRect | null }>) => {
|
||||||
|
const { id, bbox } = action.payload;
|
||||||
|
const ca = selectCA(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.bbox = bbox;
|
||||||
|
ca.bboxNeedsUpdate = false;
|
||||||
|
},
|
||||||
|
caDeleted: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
state.controlAdapters = state.controlAdapters.filter((ca) => ca.id !== id);
|
||||||
|
},
|
||||||
|
caOpacityChanged: (state, action: PayloadAction<{ id: string; opacity: number }>) => {
|
||||||
|
const { id, opacity } = action.payload;
|
||||||
|
const ca = selectCA(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.opacity = opacity;
|
||||||
|
},
|
||||||
|
caMovedForwardOne: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const ca = selectCA(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
moveOneToEnd(state.controlAdapters, ca);
|
||||||
|
},
|
||||||
|
caMovedToFront: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const ca = selectCA(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
moveToEnd(state.controlAdapters, ca);
|
||||||
|
},
|
||||||
|
caMovedBackwardOne: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const ca = selectCA(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
moveOneToStart(state.controlAdapters, ca);
|
||||||
|
},
|
||||||
|
caMovedToBack: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const ca = selectCA(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
moveToStart(state.controlAdapters, ca);
|
||||||
|
},
|
||||||
|
caImageChanged: (state, action: PayloadAction<{ id: string; imageDTO: ImageDTO | null }>) => {
|
||||||
|
const { id, imageDTO } = action.payload;
|
||||||
|
const ca = selectCA(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.bbox = null;
|
||||||
|
ca.bboxNeedsUpdate = true;
|
||||||
|
ca.isEnabled = true;
|
||||||
|
if (imageDTO) {
|
||||||
|
const newImage = imageDTOToImageWithDims(imageDTO);
|
||||||
|
if (isEqual(newImage, ca.image)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.image = newImage;
|
||||||
|
ca.processedImage = null;
|
||||||
|
} else {
|
||||||
|
ca.image = null;
|
||||||
|
ca.processedImage = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
caProcessedImageChanged: (state, action: PayloadAction<{ id: string; imageDTO: ImageDTO | null }>) => {
|
||||||
|
const { id, imageDTO } = action.payload;
|
||||||
|
const ca = selectCA(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.bbox = null;
|
||||||
|
ca.bboxNeedsUpdate = true;
|
||||||
|
ca.isEnabled = true;
|
||||||
|
ca.processedImage = imageDTO ? imageDTOToImageWithDims(imageDTO) : null;
|
||||||
|
},
|
||||||
|
caModelChanged: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{
|
||||||
|
id: string;
|
||||||
|
modelConfig: ControlNetModelConfig | T2IAdapterModelConfig | null;
|
||||||
|
}>
|
||||||
|
) => {
|
||||||
|
const { id, modelConfig } = action.payload;
|
||||||
|
const ca = selectCA(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!modelConfig) {
|
||||||
|
ca.model = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.model = zModelIdentifierField.parse(modelConfig);
|
||||||
|
|
||||||
|
// We may need to convert the CA to match the model
|
||||||
|
if (!ca.controlMode && ca.model.type === 'controlnet') {
|
||||||
|
ca.controlMode = 'balanced';
|
||||||
|
} else if (ca.controlMode && ca.model.type === 't2i_adapter') {
|
||||||
|
ca.controlMode = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const candidateProcessorConfig = buildControlAdapterProcessorV2(modelConfig);
|
||||||
|
if (candidateProcessorConfig?.type !== ca.processorConfig?.type) {
|
||||||
|
// The processor has changed. For example, the previous model was a Canny model and the new model is a Depth
|
||||||
|
// model. We need to use the new processor.
|
||||||
|
ca.processedImage = null;
|
||||||
|
ca.processorConfig = candidateProcessorConfig;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
caControlModeChanged: (state, action: PayloadAction<{ id: string; controlMode: ControlModeV2 }>) => {
|
||||||
|
const { id, controlMode } = action.payload;
|
||||||
|
const ca = selectCA(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.controlMode = controlMode;
|
||||||
|
},
|
||||||
|
caProcessorConfigChanged: (state, action: PayloadAction<{ id: string; processorConfig: ProcessorConfig | null }>) => {
|
||||||
|
const { id, processorConfig } = action.payload;
|
||||||
|
const ca = selectCA(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.processorConfig = processorConfig;
|
||||||
|
if (!processorConfig) {
|
||||||
|
ca.processedImage = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
caFilterChanged: (state, action: PayloadAction<{ id: string; filter: Filter }>) => {
|
||||||
|
const { id, filter } = action.payload;
|
||||||
|
const ca = selectCA(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.filter = filter;
|
||||||
|
},
|
||||||
|
caProcessorPendingBatchIdChanged: (state, action: PayloadAction<{ id: string; batchId: string | null }>) => {
|
||||||
|
const { id, batchId } = action.payload;
|
||||||
|
const ca = selectCA(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.processorPendingBatchId = batchId;
|
||||||
|
},
|
||||||
|
caWeightChanged: (state, action: PayloadAction<{ id: string; weight: number }>) => {
|
||||||
|
const { id, weight } = action.payload;
|
||||||
|
const ca = selectCA(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.weight = weight;
|
||||||
|
},
|
||||||
|
caBeginEndStepPctChanged: (state, action: PayloadAction<{ id: string; beginEndStepPct: [number, number] }>) => {
|
||||||
|
const { id, beginEndStepPct } = action.payload;
|
||||||
|
const ca = selectCA(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.beginEndStepPct = beginEndStepPct;
|
||||||
|
},
|
||||||
|
} satisfies SliceCaseReducers<CanvasV2State>;
|
@ -1,291 +0,0 @@
|
|||||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
|
||||||
import { createSlice } from '@reduxjs/toolkit';
|
|
||||||
import type { PersistConfig, RootState } from 'app/store/store';
|
|
||||||
import { moveOneToEnd, moveOneToStart, moveToEnd, moveToStart } from 'common/util/arrayUtils';
|
|
||||||
import { zModelIdentifierField } from 'features/nodes/types/common';
|
|
||||||
import type { IRect } from 'konva/lib/types';
|
|
||||||
import { isEqual } from 'lodash-es';
|
|
||||||
import type { ControlNetModelConfig, ImageDTO, T2IAdapterModelConfig } from 'services/api/types';
|
|
||||||
import { assert } from 'tsafe';
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
|
||||||
|
|
||||||
import type { ControlAdapterConfig, ControlAdapterData, ControlModeV2, Filter, ProcessorConfig } from './types';
|
|
||||||
import { buildControlAdapterProcessorV2, imageDTOToImageWithDims } from './types';
|
|
||||||
|
|
||||||
type ControlAdaptersV2State = {
|
|
||||||
_version: 1;
|
|
||||||
controlAdapters: ControlAdapterData[];
|
|
||||||
};
|
|
||||||
|
|
||||||
const initialState: ControlAdaptersV2State = {
|
|
||||||
_version: 1,
|
|
||||||
controlAdapters: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
export const selectCA = (state: ControlAdaptersV2State, id: string) => state.controlAdapters.find((ca) => ca.id === id);
|
|
||||||
export const selectCAOrThrow = (state: ControlAdaptersV2State, id: string) => {
|
|
||||||
const ca = selectCA(state, id);
|
|
||||||
assert(ca, `Control Adapter with id ${id} not found`);
|
|
||||||
return ca;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const controlAdaptersV2Slice = createSlice({
|
|
||||||
name: 'controlAdaptersV2',
|
|
||||||
initialState,
|
|
||||||
reducers: {
|
|
||||||
caAdded: {
|
|
||||||
reducer: (state, action: PayloadAction<{ id: string; config: ControlAdapterConfig }>) => {
|
|
||||||
const { id, config } = action.payload;
|
|
||||||
state.controlAdapters.push({
|
|
||||||
id,
|
|
||||||
type: 'control_adapter',
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
bbox: null,
|
|
||||||
bboxNeedsUpdate: false,
|
|
||||||
isEnabled: true,
|
|
||||||
opacity: 1,
|
|
||||||
filter: 'LightnessToAlphaFilter',
|
|
||||||
processorPendingBatchId: null,
|
|
||||||
...config,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
prepare: (config: ControlAdapterConfig) => ({
|
|
||||||
payload: { id: uuidv4(), config },
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
caRecalled: (state, action: PayloadAction<{ data: ControlAdapterData }>) => {
|
|
||||||
state.controlAdapters.push(action.payload.data);
|
|
||||||
},
|
|
||||||
caIsEnabledToggled: (state, action: PayloadAction<{ id: string }>) => {
|
|
||||||
const { id } = action.payload;
|
|
||||||
const ca = selectCA(state, id);
|
|
||||||
if (!ca) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ca.isEnabled = !ca.isEnabled;
|
|
||||||
},
|
|
||||||
caTranslated: (state, action: PayloadAction<{ id: string; x: number; y: number }>) => {
|
|
||||||
const { id, x, y } = action.payload;
|
|
||||||
const ca = selectCA(state, id);
|
|
||||||
if (!ca) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ca.x = x;
|
|
||||||
ca.y = y;
|
|
||||||
},
|
|
||||||
caBboxChanged: (state, action: PayloadAction<{ id: string; bbox: IRect | null }>) => {
|
|
||||||
const { id, bbox } = action.payload;
|
|
||||||
const ca = selectCA(state, id);
|
|
||||||
if (!ca) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ca.bbox = bbox;
|
|
||||||
ca.bboxNeedsUpdate = false;
|
|
||||||
},
|
|
||||||
caDeleted: (state, action: PayloadAction<{ id: string }>) => {
|
|
||||||
const { id } = action.payload;
|
|
||||||
state.controlAdapters = state.controlAdapters.filter((ca) => ca.id !== id);
|
|
||||||
},
|
|
||||||
caOpacityChanged: (state, action: PayloadAction<{ id: string; opacity: number }>) => {
|
|
||||||
const { id, opacity } = action.payload;
|
|
||||||
const ca = selectCA(state, id);
|
|
||||||
if (!ca) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ca.opacity = opacity;
|
|
||||||
},
|
|
||||||
caMovedForwardOne: (state, action: PayloadAction<{ id: string }>) => {
|
|
||||||
const { id } = action.payload;
|
|
||||||
const ca = selectCA(state, id);
|
|
||||||
if (!ca) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
moveOneToEnd(state.controlAdapters, ca);
|
|
||||||
},
|
|
||||||
caMovedToFront: (state, action: PayloadAction<{ id: string }>) => {
|
|
||||||
const { id } = action.payload;
|
|
||||||
const ca = selectCA(state, id);
|
|
||||||
if (!ca) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
moveToEnd(state.controlAdapters, ca);
|
|
||||||
},
|
|
||||||
caMovedBackwardOne: (state, action: PayloadAction<{ id: string }>) => {
|
|
||||||
const { id } = action.payload;
|
|
||||||
const ca = selectCA(state, id);
|
|
||||||
if (!ca) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
moveOneToStart(state.controlAdapters, ca);
|
|
||||||
},
|
|
||||||
caMovedToBack: (state, action: PayloadAction<{ id: string }>) => {
|
|
||||||
const { id } = action.payload;
|
|
||||||
const ca = selectCA(state, id);
|
|
||||||
if (!ca) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
moveToStart(state.controlAdapters, ca);
|
|
||||||
},
|
|
||||||
caImageChanged: (state, action: PayloadAction<{ id: string; imageDTO: ImageDTO | null }>) => {
|
|
||||||
const { id, imageDTO } = action.payload;
|
|
||||||
const ca = selectCA(state, id);
|
|
||||||
if (!ca) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ca.bbox = null;
|
|
||||||
ca.bboxNeedsUpdate = true;
|
|
||||||
ca.isEnabled = true;
|
|
||||||
if (imageDTO) {
|
|
||||||
const newImage = imageDTOToImageWithDims(imageDTO);
|
|
||||||
if (isEqual(newImage, ca.image)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ca.image = newImage;
|
|
||||||
ca.processedImage = null;
|
|
||||||
} else {
|
|
||||||
ca.image = null;
|
|
||||||
ca.processedImage = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
caProcessedImageChanged: (state, action: PayloadAction<{ id: string; imageDTO: ImageDTO | null }>) => {
|
|
||||||
const { id, imageDTO } = action.payload;
|
|
||||||
const ca = selectCA(state, id);
|
|
||||||
if (!ca) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ca.bbox = null;
|
|
||||||
ca.bboxNeedsUpdate = true;
|
|
||||||
ca.isEnabled = true;
|
|
||||||
ca.processedImage = imageDTO ? imageDTOToImageWithDims(imageDTO) : null;
|
|
||||||
},
|
|
||||||
caModelChanged: (
|
|
||||||
state,
|
|
||||||
action: PayloadAction<{
|
|
||||||
id: string;
|
|
||||||
modelConfig: ControlNetModelConfig | T2IAdapterModelConfig | null;
|
|
||||||
}>
|
|
||||||
) => {
|
|
||||||
const { id, modelConfig } = action.payload;
|
|
||||||
const ca = selectCA(state, id);
|
|
||||||
if (!ca) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!modelConfig) {
|
|
||||||
ca.model = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ca.model = zModelIdentifierField.parse(modelConfig);
|
|
||||||
|
|
||||||
// We may need to convert the CA to match the model
|
|
||||||
if (!ca.controlMode && ca.model.type === 'controlnet') {
|
|
||||||
ca.controlMode = 'balanced';
|
|
||||||
} else if (ca.controlMode && ca.model.type === 't2i_adapter') {
|
|
||||||
ca.controlMode = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const candidateProcessorConfig = buildControlAdapterProcessorV2(modelConfig);
|
|
||||||
if (candidateProcessorConfig?.type !== ca.processorConfig?.type) {
|
|
||||||
// The processor has changed. For example, the previous model was a Canny model and the new model is a Depth
|
|
||||||
// model. We need to use the new processor.
|
|
||||||
ca.processedImage = null;
|
|
||||||
ca.processorConfig = candidateProcessorConfig;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
caControlModeChanged: (state, action: PayloadAction<{ id: string; controlMode: ControlModeV2 }>) => {
|
|
||||||
const { id, controlMode } = action.payload;
|
|
||||||
const ca = selectCA(state, id);
|
|
||||||
if (!ca) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ca.controlMode = controlMode;
|
|
||||||
},
|
|
||||||
caProcessorConfigChanged: (
|
|
||||||
state,
|
|
||||||
action: PayloadAction<{ id: string; processorConfig: ProcessorConfig | null }>
|
|
||||||
) => {
|
|
||||||
const { id, processorConfig } = action.payload;
|
|
||||||
const ca = selectCA(state, id);
|
|
||||||
if (!ca) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ca.processorConfig = processorConfig;
|
|
||||||
if (!processorConfig) {
|
|
||||||
ca.processedImage = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
caFilterChanged: (state, action: PayloadAction<{ id: string; filter: Filter }>) => {
|
|
||||||
const { id, filter } = action.payload;
|
|
||||||
const ca = selectCA(state, id);
|
|
||||||
if (!ca) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ca.filter = filter;
|
|
||||||
},
|
|
||||||
caProcessorPendingBatchIdChanged: (state, action: PayloadAction<{ id: string; batchId: string | null }>) => {
|
|
||||||
const { id, batchId } = action.payload;
|
|
||||||
const ca = selectCA(state, id);
|
|
||||||
if (!ca) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ca.processorPendingBatchId = batchId;
|
|
||||||
},
|
|
||||||
caWeightChanged: (state, action: PayloadAction<{ id: string; weight: number }>) => {
|
|
||||||
const { id, weight } = action.payload;
|
|
||||||
const ca = selectCA(state, id);
|
|
||||||
if (!ca) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ca.weight = weight;
|
|
||||||
},
|
|
||||||
caBeginEndStepPctChanged: (state, action: PayloadAction<{ id: string; beginEndStepPct: [number, number] }>) => {
|
|
||||||
const { id, beginEndStepPct } = action.payload;
|
|
||||||
const ca = selectCA(state, id);
|
|
||||||
if (!ca) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ca.beginEndStepPct = beginEndStepPct;
|
|
||||||
},
|
|
||||||
caAllDeleted: (state) => {
|
|
||||||
state.controlAdapters = [];
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const {
|
|
||||||
caAdded,
|
|
||||||
caBboxChanged,
|
|
||||||
caDeleted,
|
|
||||||
caIsEnabledToggled,
|
|
||||||
caMovedBackwardOne,
|
|
||||||
caMovedForwardOne,
|
|
||||||
caMovedToBack,
|
|
||||||
caMovedToFront,
|
|
||||||
caOpacityChanged,
|
|
||||||
caTranslated,
|
|
||||||
caRecalled,
|
|
||||||
caImageChanged,
|
|
||||||
caProcessedImageChanged,
|
|
||||||
caModelChanged,
|
|
||||||
caControlModeChanged,
|
|
||||||
caProcessorConfigChanged,
|
|
||||||
caFilterChanged,
|
|
||||||
caProcessorPendingBatchIdChanged,
|
|
||||||
caWeightChanged,
|
|
||||||
caBeginEndStepPctChanged,
|
|
||||||
caAllDeleted,
|
|
||||||
} = controlAdaptersV2Slice.actions;
|
|
||||||
|
|
||||||
export const selectControlAdaptersV2Slice = (state: RootState) => state.controlAdaptersV2;
|
|
||||||
|
|
||||||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
|
||||||
const migrate = (state: any): any => {
|
|
||||||
return state;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const controlAdaptersV2PersistConfig: PersistConfig<ControlAdaptersV2State> = {
|
|
||||||
name: controlAdaptersV2Slice.name,
|
|
||||||
initialState,
|
|
||||||
migrate,
|
|
||||||
persistDenylist: [],
|
|
||||||
};
|
|
@ -0,0 +1,102 @@
|
|||||||
|
import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit';
|
||||||
|
import { zModelIdentifierField } from 'features/nodes/types/common';
|
||||||
|
import type { ImageDTO, IPAdapterModelConfig } from 'services/api/types';
|
||||||
|
import { assert } from 'tsafe';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
import type { CanvasV2State, CLIPVisionModelV2, IPAdapterConfig, IPAdapterData, IPMethodV2 } from './types';
|
||||||
|
import { imageDTOToImageWithDims } from './types';
|
||||||
|
|
||||||
|
export const selectIPA = (state: CanvasV2State, id: string) => state.ipAdapters.find((ipa) => ipa.id === id);
|
||||||
|
export const selectIPAOrThrow = (state: CanvasV2State, id: string) => {
|
||||||
|
const ipa = selectIPA(state, id);
|
||||||
|
assert(ipa, `IP Adapter with id ${id} not found`);
|
||||||
|
return ipa;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ipAdaptersReducers = {
|
||||||
|
ipaAdded: {
|
||||||
|
reducer: (state, action: PayloadAction<{ id: string; config: IPAdapterConfig }>) => {
|
||||||
|
const { id, config } = action.payload;
|
||||||
|
const layer: IPAdapterData = {
|
||||||
|
id,
|
||||||
|
type: 'ip_adapter',
|
||||||
|
isEnabled: true,
|
||||||
|
...config,
|
||||||
|
};
|
||||||
|
state.ipAdapters.push(layer);
|
||||||
|
},
|
||||||
|
prepare: (config: IPAdapterConfig) => ({ payload: { id: uuidv4(), config } }),
|
||||||
|
},
|
||||||
|
ipaRecalled: (state, action: PayloadAction<{ data: IPAdapterData }>) => {
|
||||||
|
state.ipAdapters.push(action.payload.data);
|
||||||
|
},
|
||||||
|
ipaIsEnabledToggled: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const ipa = selectIPA(state, id);
|
||||||
|
if (ipa) {
|
||||||
|
ipa.isEnabled = !ipa.isEnabled;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ipaDeleted: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
state.ipAdapters = state.ipAdapters.filter((ipa) => ipa.id !== action.payload.id);
|
||||||
|
},
|
||||||
|
ipaImageChanged: (state, action: PayloadAction<{ id: string; imageDTO: ImageDTO | null }>) => {
|
||||||
|
const { id, imageDTO } = action.payload;
|
||||||
|
const ipa = selectIPA(state, id);
|
||||||
|
if (!ipa) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ipa.image = imageDTO ? imageDTOToImageWithDims(imageDTO) : null;
|
||||||
|
},
|
||||||
|
ipaMethodChanged: (state, action: PayloadAction<{ id: string; method: IPMethodV2 }>) => {
|
||||||
|
const { id, method } = action.payload;
|
||||||
|
const ipa = selectIPA(state, id);
|
||||||
|
if (!ipa) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ipa.method = method;
|
||||||
|
},
|
||||||
|
ipaModelChanged: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{
|
||||||
|
id: string;
|
||||||
|
modelConfig: IPAdapterModelConfig | null;
|
||||||
|
}>
|
||||||
|
) => {
|
||||||
|
const { id, modelConfig } = action.payload;
|
||||||
|
const ipa = selectIPA(state, id);
|
||||||
|
if (!ipa) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (modelConfig) {
|
||||||
|
ipa.model = zModelIdentifierField.parse(modelConfig);
|
||||||
|
} else {
|
||||||
|
ipa.model = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ipaCLIPVisionModelChanged: (state, action: PayloadAction<{ id: string; clipVisionModel: CLIPVisionModelV2 }>) => {
|
||||||
|
const { id, clipVisionModel } = action.payload;
|
||||||
|
const ipa = selectIPA(state, id);
|
||||||
|
if (!ipa) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ipa.clipVisionModel = clipVisionModel;
|
||||||
|
},
|
||||||
|
ipaWeightChanged: (state, action: PayloadAction<{ id: string; weight: number }>) => {
|
||||||
|
const { id, weight } = action.payload;
|
||||||
|
const ipa = selectIPA(state, id);
|
||||||
|
if (!ipa) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ipa.weight = weight;
|
||||||
|
},
|
||||||
|
ipaBeginEndStepPctChanged: (state, action: PayloadAction<{ id: string; beginEndStepPct: [number, number] }>) => {
|
||||||
|
const { id, beginEndStepPct } = action.payload;
|
||||||
|
const ipa = selectIPA(state, id);
|
||||||
|
if (!ipa) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ipa.beginEndStepPct = beginEndStepPct;
|
||||||
|
},
|
||||||
|
} satisfies SliceCaseReducers<CanvasV2State>;
|
@ -1,149 +0,0 @@
|
|||||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
|
||||||
import { createSlice } from '@reduxjs/toolkit';
|
|
||||||
import type { PersistConfig, RootState } from 'app/store/store';
|
|
||||||
import { zModelIdentifierField } from 'features/nodes/types/common';
|
|
||||||
import type { ImageDTO, IPAdapterModelConfig } from 'services/api/types';
|
|
||||||
import { assert } from 'tsafe';
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
|
||||||
|
|
||||||
import type { CLIPVisionModelV2, IPAdapterConfig, IPAdapterData, IPMethodV2 } from './types';
|
|
||||||
import { imageDTOToImageWithDims } from './types';
|
|
||||||
|
|
||||||
type IPAdaptersState = {
|
|
||||||
_version: 1;
|
|
||||||
ipAdapters: IPAdapterData[];
|
|
||||||
};
|
|
||||||
|
|
||||||
const initialState: IPAdaptersState = {
|
|
||||||
_version: 1,
|
|
||||||
ipAdapters: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
export const selectIPA = (state: IPAdaptersState, id: string) => state.ipAdapters.find((ipa) => ipa.id === id);
|
|
||||||
export const selectIPAOrThrow = (state: IPAdaptersState, id: string) => {
|
|
||||||
const ipa = selectIPA(state, id);
|
|
||||||
assert(ipa, `IP Adapter with id ${id} not found`);
|
|
||||||
return ipa;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ipAdaptersSlice = createSlice({
|
|
||||||
name: 'ipAdapters',
|
|
||||||
initialState,
|
|
||||||
reducers: {
|
|
||||||
ipaAdded: {
|
|
||||||
reducer: (state, action: PayloadAction<{ id: string; config: IPAdapterConfig }>) => {
|
|
||||||
const { id, config } = action.payload;
|
|
||||||
const layer: IPAdapterData = {
|
|
||||||
id,
|
|
||||||
type: 'ip_adapter',
|
|
||||||
isEnabled: true,
|
|
||||||
...config,
|
|
||||||
};
|
|
||||||
state.ipAdapters.push(layer);
|
|
||||||
},
|
|
||||||
prepare: (config: IPAdapterConfig) => ({ payload: { id: uuidv4(), config } }),
|
|
||||||
},
|
|
||||||
ipaRecalled: (state, action: PayloadAction<{ data: IPAdapterData }>) => {
|
|
||||||
state.ipAdapters.push(action.payload.data);
|
|
||||||
},
|
|
||||||
ipaIsEnabledToggled: (state, action: PayloadAction<{ id: string }>) => {
|
|
||||||
const { id } = action.payload;
|
|
||||||
const ipa = selectIPA(state, id);
|
|
||||||
if (ipa) {
|
|
||||||
ipa.isEnabled = !ipa.isEnabled;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ipaDeleted: (state, action: PayloadAction<{ id: string }>) => {
|
|
||||||
state.ipAdapters = state.ipAdapters.filter((ipa) => ipa.id !== action.payload.id);
|
|
||||||
},
|
|
||||||
ipaImageChanged: (state, action: PayloadAction<{ id: string; imageDTO: ImageDTO | null }>) => {
|
|
||||||
const { id, imageDTO } = action.payload;
|
|
||||||
const ipa = selectIPA(state, id);
|
|
||||||
if (!ipa) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ipa.image = imageDTO ? imageDTOToImageWithDims(imageDTO) : null;
|
|
||||||
},
|
|
||||||
ipaMethodChanged: (state, action: PayloadAction<{ id: string; method: IPMethodV2 }>) => {
|
|
||||||
const { id, method } = action.payload;
|
|
||||||
const ipa = selectIPA(state, id);
|
|
||||||
if (!ipa) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ipa.method = method;
|
|
||||||
},
|
|
||||||
ipaModelChanged: (
|
|
||||||
state,
|
|
||||||
action: PayloadAction<{
|
|
||||||
id: string;
|
|
||||||
modelConfig: IPAdapterModelConfig | null;
|
|
||||||
}>
|
|
||||||
) => {
|
|
||||||
const { id, modelConfig } = action.payload;
|
|
||||||
const ipa = selectIPA(state, id);
|
|
||||||
if (!ipa) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (modelConfig) {
|
|
||||||
ipa.model = zModelIdentifierField.parse(modelConfig);
|
|
||||||
} else {
|
|
||||||
ipa.model = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ipaCLIPVisionModelChanged: (state, action: PayloadAction<{ id: string; clipVisionModel: CLIPVisionModelV2 }>) => {
|
|
||||||
const { id, clipVisionModel } = action.payload;
|
|
||||||
const ipa = selectIPA(state, id);
|
|
||||||
if (!ipa) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ipa.clipVisionModel = clipVisionModel;
|
|
||||||
},
|
|
||||||
ipaWeightChanged: (state, action: PayloadAction<{ id: string; weight: number }>) => {
|
|
||||||
const { id, weight } = action.payload;
|
|
||||||
const ipa = selectIPA(state, id);
|
|
||||||
if (!ipa) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ipa.weight = weight;
|
|
||||||
},
|
|
||||||
ipaBeginEndStepPctChanged: (state, action: PayloadAction<{ id: string; beginEndStepPct: [number, number] }>) => {
|
|
||||||
const { id, beginEndStepPct } = action.payload;
|
|
||||||
const ipa = selectIPA(state, id);
|
|
||||||
if (!ipa) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ipa.beginEndStepPct = beginEndStepPct;
|
|
||||||
},
|
|
||||||
ipaAllDeleted: (state) => {
|
|
||||||
state.ipAdapters = [];
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const {
|
|
||||||
ipaAdded,
|
|
||||||
ipaRecalled,
|
|
||||||
ipaIsEnabledToggled,
|
|
||||||
ipaDeleted,
|
|
||||||
ipaImageChanged,
|
|
||||||
ipaMethodChanged,
|
|
||||||
ipaModelChanged,
|
|
||||||
ipaCLIPVisionModelChanged,
|
|
||||||
ipaWeightChanged,
|
|
||||||
ipaBeginEndStepPctChanged,
|
|
||||||
ipaAllDeleted,
|
|
||||||
} = ipAdaptersSlice.actions;
|
|
||||||
|
|
||||||
export const selectIPAdaptersSlice = (state: RootState) => state.ipAdapters;
|
|
||||||
|
|
||||||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
|
||||||
const migrate = (state: any): any => {
|
|
||||||
return state;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ipAdaptersPersistConfig: PersistConfig<IPAdaptersState> = {
|
|
||||||
name: ipAdaptersSlice.name,
|
|
||||||
initialState,
|
|
||||||
migrate,
|
|
||||||
persistDenylist: [],
|
|
||||||
};
|
|
@ -0,0 +1,227 @@
|
|||||||
|
import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit';
|
||||||
|
import { moveOneToEnd, moveOneToStart, moveToEnd, moveToStart } from 'common/util/arrayUtils';
|
||||||
|
import { getBrushLineId, getEraserLineId, getImageObjectId, getRectShapeId } from 'features/controlLayers/konva/naming';
|
||||||
|
import type { IRect } from 'konva/lib/types';
|
||||||
|
import { assert } from 'tsafe';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
BrushLineAddedArg,
|
||||||
|
CanvasV2State,
|
||||||
|
EraserLineAddedArg,
|
||||||
|
ImageObjectAddedArg,
|
||||||
|
LayerData,
|
||||||
|
PointAddedToLineArg,
|
||||||
|
RectShapeAddedArg,
|
||||||
|
} from './types';
|
||||||
|
import { isLine } from './types';
|
||||||
|
|
||||||
|
export const selectLayer = (state: CanvasV2State, id: string) => state.layers.find((layer) => layer.id === id);
|
||||||
|
export const selectLayerOrThrow = (state: CanvasV2State, id: string) => {
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
assert(layer, `Layer with id ${id} not found`);
|
||||||
|
return layer;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const layersReducers = {
|
||||||
|
layerAdded: {
|
||||||
|
reducer: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
state.layers.push({
|
||||||
|
id,
|
||||||
|
type: 'layer',
|
||||||
|
isEnabled: true,
|
||||||
|
bbox: null,
|
||||||
|
bboxNeedsUpdate: false,
|
||||||
|
objects: [],
|
||||||
|
opacity: 1,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
prepare: () => ({ payload: { id: uuidv4() } }),
|
||||||
|
},
|
||||||
|
layerRecalled: (state, action: PayloadAction<{ data: LayerData }>) => {
|
||||||
|
state.layers.push(action.payload.data);
|
||||||
|
},
|
||||||
|
layerIsEnabledToggled: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
layer.isEnabled = !layer.isEnabled;
|
||||||
|
},
|
||||||
|
layerTranslated: (state, action: PayloadAction<{ id: string; x: number; y: number }>) => {
|
||||||
|
const { id, x, y } = action.payload;
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
layer.x = x;
|
||||||
|
layer.y = y;
|
||||||
|
},
|
||||||
|
layerBboxChanged: (state, action: PayloadAction<{ id: string; bbox: IRect | null }>) => {
|
||||||
|
const { id, bbox } = action.payload;
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
layer.bbox = bbox;
|
||||||
|
layer.bboxNeedsUpdate = false;
|
||||||
|
if (bbox === null) {
|
||||||
|
layer.objects = [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
layerReset: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
layer.isEnabled = true;
|
||||||
|
layer.objects = [];
|
||||||
|
layer.bbox = null;
|
||||||
|
layer.bboxNeedsUpdate = false;
|
||||||
|
},
|
||||||
|
layerDeleted: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
state.layers = state.layers.filter((l) => l.id !== id);
|
||||||
|
},
|
||||||
|
layerOpacityChanged: (state, action: PayloadAction<{ id: string; opacity: number }>) => {
|
||||||
|
const { id, opacity } = action.payload;
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
layer.opacity = opacity;
|
||||||
|
},
|
||||||
|
layerMovedForwardOne: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
moveOneToEnd(state.layers, layer);
|
||||||
|
},
|
||||||
|
layerMovedToFront: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
moveToEnd(state.layers, layer);
|
||||||
|
},
|
||||||
|
layerMovedBackwardOne: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
moveOneToStart(state.layers, layer);
|
||||||
|
},
|
||||||
|
layerMovedToBack: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
moveToStart(state.layers, layer);
|
||||||
|
},
|
||||||
|
layerBrushLineAdded: {
|
||||||
|
reducer: (state, action: PayloadAction<BrushLineAddedArg & { lineId: string }>) => {
|
||||||
|
const { id, points, lineId, color, width } = action.payload;
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
layer.objects.push({
|
||||||
|
id: getBrushLineId(id, lineId),
|
||||||
|
type: 'brush_line',
|
||||||
|
points,
|
||||||
|
strokeWidth: width,
|
||||||
|
color,
|
||||||
|
});
|
||||||
|
layer.bboxNeedsUpdate = true;
|
||||||
|
},
|
||||||
|
prepare: (payload: BrushLineAddedArg) => ({
|
||||||
|
payload: { ...payload, lineId: uuidv4() },
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
layerEraserLineAdded: {
|
||||||
|
reducer: (state, action: PayloadAction<EraserLineAddedArg & { lineId: string }>) => {
|
||||||
|
const { id, points, lineId, width } = action.payload;
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
layer.objects.push({
|
||||||
|
id: getEraserLineId(id, lineId),
|
||||||
|
type: 'eraser_line',
|
||||||
|
points,
|
||||||
|
strokeWidth: width,
|
||||||
|
});
|
||||||
|
layer.bboxNeedsUpdate = true;
|
||||||
|
},
|
||||||
|
prepare: (payload: EraserLineAddedArg) => ({
|
||||||
|
payload: { ...payload, lineId: uuidv4() },
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
layerLinePointAdded: (state, action: PayloadAction<PointAddedToLineArg>) => {
|
||||||
|
const { id, point } = action.payload;
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const lastObject = layer.objects[layer.objects.length - 1];
|
||||||
|
if (!lastObject || !isLine(lastObject)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lastObject.points.push(...point);
|
||||||
|
layer.bboxNeedsUpdate = true;
|
||||||
|
},
|
||||||
|
layerRectAdded: {
|
||||||
|
reducer: (state, action: PayloadAction<RectShapeAddedArg & { rectId: string }>) => {
|
||||||
|
const { id, rect, rectId, color } = action.payload;
|
||||||
|
if (rect.height === 0 || rect.width === 0) {
|
||||||
|
// Ignore zero-area rectangles
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
layer.objects.push({
|
||||||
|
type: 'rect_shape',
|
||||||
|
id: getRectShapeId(id, rectId),
|
||||||
|
...rect,
|
||||||
|
color,
|
||||||
|
});
|
||||||
|
layer.bboxNeedsUpdate = true;
|
||||||
|
},
|
||||||
|
prepare: (payload: RectShapeAddedArg) => ({ payload: { ...payload, rectId: uuidv4() } }),
|
||||||
|
},
|
||||||
|
layerImageAdded: {
|
||||||
|
reducer: (state, action: PayloadAction<ImageObjectAddedArg & { imageId: string }>) => {
|
||||||
|
const { id, imageId, imageDTO } = action.payload;
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { width, height, image_name: name } = imageDTO;
|
||||||
|
layer.objects.push({
|
||||||
|
type: 'image',
|
||||||
|
id: getImageObjectId(id, imageId),
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
image: { width, height, name },
|
||||||
|
});
|
||||||
|
layer.bboxNeedsUpdate = true;
|
||||||
|
},
|
||||||
|
prepare: (payload: ImageObjectAddedArg) => ({ payload: { ...payload, imageId: uuidv4() } }),
|
||||||
|
},
|
||||||
|
} satisfies SliceCaseReducers<CanvasV2State>;
|
@ -1,275 +0,0 @@
|
|||||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
|
||||||
import { createSlice } from '@reduxjs/toolkit';
|
|
||||||
import type { PersistConfig, RootState } from 'app/store/store';
|
|
||||||
import { moveOneToEnd, moveOneToStart, moveToEnd, moveToStart } from 'common/util/arrayUtils';
|
|
||||||
import { getBrushLineId, getEraserLineId, getImageObjectId, getRectShapeId } from 'features/controlLayers/konva/naming';
|
|
||||||
import type { IRect } from 'konva/lib/types';
|
|
||||||
import { assert } from 'tsafe';
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
|
||||||
|
|
||||||
import type {
|
|
||||||
BrushLineAddedArg,
|
|
||||||
EraserLineAddedArg,
|
|
||||||
ImageObjectAddedArg,
|
|
||||||
LayerData,
|
|
||||||
PointAddedToLineArg,
|
|
||||||
RectShapeAddedArg,
|
|
||||||
} from './types';
|
|
||||||
import { isLine } from './types';
|
|
||||||
|
|
||||||
type LayersState = {
|
|
||||||
_version: 1;
|
|
||||||
layers: LayerData[];
|
|
||||||
};
|
|
||||||
|
|
||||||
const initialState: LayersState = { _version: 1, layers: [] };
|
|
||||||
export const selectLayer = (state: LayersState, id: string) => state.layers.find((layer) => layer.id === id);
|
|
||||||
export const selectLayerOrThrow = (state: LayersState, id: string) => {
|
|
||||||
const layer = selectLayer(state, id);
|
|
||||||
assert(layer, `Layer with id ${id} not found`);
|
|
||||||
return layer;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const layersSlice = createSlice({
|
|
||||||
name: 'layers',
|
|
||||||
initialState,
|
|
||||||
reducers: {
|
|
||||||
layerAdded: {
|
|
||||||
reducer: (state, action: PayloadAction<{ id: string }>) => {
|
|
||||||
const { id } = action.payload;
|
|
||||||
state.layers.push({
|
|
||||||
id,
|
|
||||||
type: 'layer',
|
|
||||||
isEnabled: true,
|
|
||||||
bbox: null,
|
|
||||||
bboxNeedsUpdate: false,
|
|
||||||
objects: [],
|
|
||||||
opacity: 1,
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
prepare: () => ({ payload: { id: uuidv4() } }),
|
|
||||||
},
|
|
||||||
layerRecalled: (state, action: PayloadAction<{ data: LayerData }>) => {
|
|
||||||
state.layers.push(action.payload.data);
|
|
||||||
},
|
|
||||||
layerIsEnabledToggled: (state, action: PayloadAction<{ id: string }>) => {
|
|
||||||
const { id } = action.payload;
|
|
||||||
const layer = selectLayer(state, id);
|
|
||||||
if (!layer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
layer.isEnabled = !layer.isEnabled;
|
|
||||||
},
|
|
||||||
layerTranslated: (state, action: PayloadAction<{ id: string; x: number; y: number }>) => {
|
|
||||||
const { id, x, y } = action.payload;
|
|
||||||
const layer = selectLayer(state, id);
|
|
||||||
if (!layer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
layer.x = x;
|
|
||||||
layer.y = y;
|
|
||||||
},
|
|
||||||
layerBboxChanged: (state, action: PayloadAction<{ id: string; bbox: IRect | null }>) => {
|
|
||||||
const { id, bbox } = action.payload;
|
|
||||||
const layer = selectLayer(state, id);
|
|
||||||
if (!layer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
layer.bbox = bbox;
|
|
||||||
layer.bboxNeedsUpdate = false;
|
|
||||||
if (bbox === null) {
|
|
||||||
layer.objects = [];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
layerReset: (state, action: PayloadAction<{ id: string }>) => {
|
|
||||||
const { id } = action.payload;
|
|
||||||
const layer = selectLayer(state, id);
|
|
||||||
if (!layer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
layer.isEnabled = true;
|
|
||||||
layer.objects = [];
|
|
||||||
layer.bbox = null;
|
|
||||||
layer.bboxNeedsUpdate = false;
|
|
||||||
},
|
|
||||||
layerDeleted: (state, action: PayloadAction<{ id: string }>) => {
|
|
||||||
const { id } = action.payload;
|
|
||||||
state.layers = state.layers.filter((l) => l.id !== id);
|
|
||||||
},
|
|
||||||
layerOpacityChanged: (state, action: PayloadAction<{ id: string; opacity: number }>) => {
|
|
||||||
const { id, opacity } = action.payload;
|
|
||||||
const layer = selectLayer(state, id);
|
|
||||||
if (!layer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
layer.opacity = opacity;
|
|
||||||
},
|
|
||||||
layerMovedForwardOne: (state, action: PayloadAction<{ id: string }>) => {
|
|
||||||
const { id } = action.payload;
|
|
||||||
const layer = selectLayer(state, id);
|
|
||||||
if (!layer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
moveOneToEnd(state.layers, layer);
|
|
||||||
},
|
|
||||||
layerMovedToFront: (state, action: PayloadAction<{ id: string }>) => {
|
|
||||||
const { id } = action.payload;
|
|
||||||
const layer = selectLayer(state, id);
|
|
||||||
if (!layer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
moveToEnd(state.layers, layer);
|
|
||||||
},
|
|
||||||
layerMovedBackwardOne: (state, action: PayloadAction<{ id: string }>) => {
|
|
||||||
const { id } = action.payload;
|
|
||||||
const layer = selectLayer(state, id);
|
|
||||||
if (!layer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
moveOneToStart(state.layers, layer);
|
|
||||||
},
|
|
||||||
layerMovedToBack: (state, action: PayloadAction<{ id: string }>) => {
|
|
||||||
const { id } = action.payload;
|
|
||||||
const layer = selectLayer(state, id);
|
|
||||||
if (!layer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
moveToStart(state.layers, layer);
|
|
||||||
},
|
|
||||||
layerBrushLineAdded: {
|
|
||||||
reducer: (state, action: PayloadAction<BrushLineAddedArg & { lineId: string }>) => {
|
|
||||||
const { id, points, lineId, color, width } = action.payload;
|
|
||||||
const layer = selectLayer(state, id);
|
|
||||||
if (!layer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
layer.objects.push({
|
|
||||||
id: getBrushLineId(id, lineId),
|
|
||||||
type: 'brush_line',
|
|
||||||
points,
|
|
||||||
strokeWidth: width,
|
|
||||||
color,
|
|
||||||
});
|
|
||||||
layer.bboxNeedsUpdate = true;
|
|
||||||
},
|
|
||||||
prepare: (payload: BrushLineAddedArg) => ({
|
|
||||||
payload: { ...payload, lineId: uuidv4() },
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
layerEraserLineAdded: {
|
|
||||||
reducer: (state, action: PayloadAction<EraserLineAddedArg & { lineId: string }>) => {
|
|
||||||
const { id, points, lineId, width } = action.payload;
|
|
||||||
const layer = selectLayer(state, id);
|
|
||||||
if (!layer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
layer.objects.push({
|
|
||||||
id: getEraserLineId(id, lineId),
|
|
||||||
type: 'eraser_line',
|
|
||||||
points,
|
|
||||||
strokeWidth: width,
|
|
||||||
});
|
|
||||||
layer.bboxNeedsUpdate = true;
|
|
||||||
},
|
|
||||||
prepare: (payload: EraserLineAddedArg) => ({
|
|
||||||
payload: { ...payload, lineId: uuidv4() },
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
layerLinePointAdded: (state, action: PayloadAction<PointAddedToLineArg>) => {
|
|
||||||
const { id, point } = action.payload;
|
|
||||||
const layer = selectLayer(state, id);
|
|
||||||
if (!layer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const lastObject = layer.objects[layer.objects.length - 1];
|
|
||||||
if (!lastObject || !isLine(lastObject)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
lastObject.points.push(...point);
|
|
||||||
layer.bboxNeedsUpdate = true;
|
|
||||||
},
|
|
||||||
layerRectAdded: {
|
|
||||||
reducer: (state, action: PayloadAction<RectShapeAddedArg & { rectId: string }>) => {
|
|
||||||
const { id, rect, rectId, color } = action.payload;
|
|
||||||
if (rect.height === 0 || rect.width === 0) {
|
|
||||||
// Ignore zero-area rectangles
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const layer = selectLayer(state, id);
|
|
||||||
if (!layer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
layer.objects.push({
|
|
||||||
type: 'rect_shape',
|
|
||||||
id: getRectShapeId(id, rectId),
|
|
||||||
...rect,
|
|
||||||
color,
|
|
||||||
});
|
|
||||||
layer.bboxNeedsUpdate = true;
|
|
||||||
},
|
|
||||||
prepare: (payload: RectShapeAddedArg) => ({ payload: { ...payload, rectId: uuidv4() } }),
|
|
||||||
},
|
|
||||||
layerImageAdded: {
|
|
||||||
reducer: (state, action: PayloadAction<ImageObjectAddedArg & { imageId: string }>) => {
|
|
||||||
const { id, imageId, imageDTO } = action.payload;
|
|
||||||
const layer = selectLayer(state, id);
|
|
||||||
if (!layer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const { width, height, image_name: name } = imageDTO;
|
|
||||||
layer.objects.push({
|
|
||||||
type: 'image',
|
|
||||||
id: getImageObjectId(id, imageId),
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
image: { width, height, name },
|
|
||||||
});
|
|
||||||
layer.bboxNeedsUpdate = true;
|
|
||||||
},
|
|
||||||
prepare: (payload: ImageObjectAddedArg) => ({ payload: { ...payload, imageId: uuidv4() } }),
|
|
||||||
},
|
|
||||||
layerAllDeleted: (state) => {
|
|
||||||
state.layers = [];
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const {
|
|
||||||
layerAdded,
|
|
||||||
layerDeleted,
|
|
||||||
layerReset,
|
|
||||||
layerMovedForwardOne,
|
|
||||||
layerMovedToFront,
|
|
||||||
layerMovedBackwardOne,
|
|
||||||
layerMovedToBack,
|
|
||||||
layerIsEnabledToggled,
|
|
||||||
layerOpacityChanged,
|
|
||||||
layerTranslated,
|
|
||||||
layerBboxChanged,
|
|
||||||
layerBrushLineAdded,
|
|
||||||
layerEraserLineAdded,
|
|
||||||
layerLinePointAdded,
|
|
||||||
layerRectAdded,
|
|
||||||
layerImageAdded,
|
|
||||||
layerAllDeleted,
|
|
||||||
} = layersSlice.actions;
|
|
||||||
|
|
||||||
export const selectLayersSlice = (state: RootState) => state.layers;
|
|
||||||
|
|
||||||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
|
||||||
const migrate = (state: any): any => {
|
|
||||||
return state;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const layersPersistConfig: PersistConfig<LayersState> = {
|
|
||||||
name: layersSlice.name,
|
|
||||||
initialState,
|
|
||||||
migrate,
|
|
||||||
persistDenylist: [],
|
|
||||||
};
|
|
@ -1,452 +0,0 @@
|
|||||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
|
||||||
import { createSlice } from '@reduxjs/toolkit';
|
|
||||||
import type { PersistConfig, RootState } from 'app/store/store';
|
|
||||||
import { moveOneToEnd, moveOneToStart, moveToEnd, moveToStart } from 'common/util/arrayUtils';
|
|
||||||
import { getBrushLineId, getEraserLineId, getRectShapeId } from 'features/controlLayers/konva/naming';
|
|
||||||
import type { CLIPVisionModelV2, IPMethodV2 } from 'features/controlLayers/store/types';
|
|
||||||
import { imageDTOToImageWithDims } from 'features/controlLayers/store/types';
|
|
||||||
import { zModelIdentifierField } from 'features/nodes/types/common';
|
|
||||||
import type { ParameterAutoNegative } from 'features/parameters/types/parameterSchemas';
|
|
||||||
import type { IRect } from 'konva/lib/types';
|
|
||||||
import { isEqual } from 'lodash-es';
|
|
||||||
import type { ImageDTO, IPAdapterModelConfig } from 'services/api/types';
|
|
||||||
import { assert } from 'tsafe';
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
|
||||||
|
|
||||||
import type {
|
|
||||||
BrushLineAddedArg,
|
|
||||||
EraserLineAddedArg,
|
|
||||||
IPAdapterData,
|
|
||||||
PointAddedToLineArg,
|
|
||||||
RectShapeAddedArg,
|
|
||||||
RegionalGuidanceData,
|
|
||||||
RgbColor,
|
|
||||||
} from './types';
|
|
||||||
import { isLine } from './types';
|
|
||||||
|
|
||||||
type RegionalGuidanceState = {
|
|
||||||
_version: 1;
|
|
||||||
regions: RegionalGuidanceData[];
|
|
||||||
opacity: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
const initialState: RegionalGuidanceState = {
|
|
||||||
_version: 1,
|
|
||||||
regions: [],
|
|
||||||
opacity: 0.3,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const selectRG = (state: RegionalGuidanceState, id: string) => state.regions.find((rg) => rg.id === id);
|
|
||||||
export const selectRGOrThrow = (state: RegionalGuidanceState, id: string) => {
|
|
||||||
const rg = selectRG(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)
|
|
||||||
{ r: 250, g: 225, b: 80 }, // rgb(250, 225, 80)
|
|
||||||
{ r: 220, g: 144, b: 101 }, // rgb(220, 144, 101)
|
|
||||||
{ r: 224, g: 117, b: 117 }, // rgb(224, 117, 117)
|
|
||||||
{ r: 213, g: 139, b: 202 }, // rgb(213, 139, 202)
|
|
||||||
{ r: 161, g: 120, b: 214 }, // rgb(161, 120, 214)
|
|
||||||
];
|
|
||||||
|
|
||||||
const getRGMaskFill = (state: RegionalGuidanceState): RgbColor => {
|
|
||||||
const lastFill = state.regions.slice(-1)[0]?.fill;
|
|
||||||
let i = DEFAULT_MASK_COLORS.findIndex((c) => isEqual(c, lastFill));
|
|
||||||
if (i === -1) {
|
|
||||||
i = 0;
|
|
||||||
}
|
|
||||||
i = (i + 1) % DEFAULT_MASK_COLORS.length;
|
|
||||||
const fill = DEFAULT_MASK_COLORS[i];
|
|
||||||
assert(fill, 'This should never happen');
|
|
||||||
return fill;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const regionalGuidanceSlice = createSlice({
|
|
||||||
name: 'regionalGuidance',
|
|
||||||
initialState,
|
|
||||||
reducers: {
|
|
||||||
rgAdded: {
|
|
||||||
reducer: (state, action: PayloadAction<{ id: string }>) => {
|
|
||||||
const { id } = action.payload;
|
|
||||||
const rg: RegionalGuidanceData = {
|
|
||||||
id,
|
|
||||||
type: 'regional_guidance',
|
|
||||||
isEnabled: true,
|
|
||||||
bbox: null,
|
|
||||||
bboxNeedsUpdate: false,
|
|
||||||
objects: [],
|
|
||||||
fill: getRGMaskFill(state),
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
autoNegative: 'invert',
|
|
||||||
positivePrompt: '',
|
|
||||||
negativePrompt: null,
|
|
||||||
ipAdapters: [],
|
|
||||||
imageCache: null,
|
|
||||||
};
|
|
||||||
state.regions.push(rg);
|
|
||||||
},
|
|
||||||
prepare: () => ({ payload: { id: uuidv4() } }),
|
|
||||||
},
|
|
||||||
rgReset: (state, action: PayloadAction<{ id: string }>) => {
|
|
||||||
const { id } = action.payload;
|
|
||||||
const rg = selectRG(state, id);
|
|
||||||
if (!rg) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
rg.objects = [];
|
|
||||||
rg.bbox = null;
|
|
||||||
rg.bboxNeedsUpdate = false;
|
|
||||||
rg.imageCache = null;
|
|
||||||
},
|
|
||||||
rgRecalled: (state, action: PayloadAction<{ data: RegionalGuidanceData }>) => {
|
|
||||||
const { data } = action.payload;
|
|
||||||
state.regions.push(data);
|
|
||||||
},
|
|
||||||
rgIsEnabledToggled: (state, action: PayloadAction<{ id: string }>) => {
|
|
||||||
const { id } = action.payload;
|
|
||||||
const rg = selectRG(state, id);
|
|
||||||
if (rg) {
|
|
||||||
rg.isEnabled = !rg.isEnabled;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
rgTranslated: (state, action: PayloadAction<{ id: string; x: number; y: number }>) => {
|
|
||||||
const { id, x, y } = action.payload;
|
|
||||||
const rg = selectRG(state, id);
|
|
||||||
if (rg) {
|
|
||||||
rg.x = x;
|
|
||||||
rg.y = y;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
rgBboxChanged: (state, action: PayloadAction<{ id: string; bbox: IRect | null }>) => {
|
|
||||||
const { id, bbox } = action.payload;
|
|
||||||
const rg = selectRG(state, id);
|
|
||||||
if (rg) {
|
|
||||||
rg.bbox = bbox;
|
|
||||||
rg.bboxNeedsUpdate = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
rgDeleted: (state, action: PayloadAction<{ id: string }>) => {
|
|
||||||
const { id } = action.payload;
|
|
||||||
state.regions = state.regions.filter((ca) => ca.id !== id);
|
|
||||||
},
|
|
||||||
rgGlobalOpacityChanged: (state, action: PayloadAction<{ opacity: number }>) => {
|
|
||||||
const { opacity } = action.payload;
|
|
||||||
state.opacity = opacity;
|
|
||||||
},
|
|
||||||
rgMovedForwardOne: (state, action: PayloadAction<{ id: string }>) => {
|
|
||||||
const { id } = action.payload;
|
|
||||||
const rg = selectRG(state, id);
|
|
||||||
if (!rg) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
moveOneToEnd(state.regions, rg);
|
|
||||||
},
|
|
||||||
rgMovedToFront: (state, action: PayloadAction<{ id: string }>) => {
|
|
||||||
const { id } = action.payload;
|
|
||||||
const rg = selectRG(state, id);
|
|
||||||
if (!rg) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
moveToEnd(state.regions, rg);
|
|
||||||
},
|
|
||||||
rgMovedBackwardOne: (state, action: PayloadAction<{ id: string }>) => {
|
|
||||||
const { id } = action.payload;
|
|
||||||
const rg = selectRG(state, id);
|
|
||||||
if (!rg) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
moveOneToStart(state.regions, rg);
|
|
||||||
},
|
|
||||||
rgMovedToBack: (state, action: PayloadAction<{ id: string }>) => {
|
|
||||||
const { id } = action.payload;
|
|
||||||
const rg = selectRG(state, id);
|
|
||||||
if (!rg) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
moveToStart(state.regions, rg);
|
|
||||||
},
|
|
||||||
rgPositivePromptChanged: (state, action: PayloadAction<{ id: string; prompt: string | null }>) => {
|
|
||||||
const { id, prompt } = action.payload;
|
|
||||||
const rg = selectRG(state, id);
|
|
||||||
if (!rg) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
rg.positivePrompt = prompt;
|
|
||||||
},
|
|
||||||
rgNegativePromptChanged: (state, action: PayloadAction<{ id: string; prompt: string | null }>) => {
|
|
||||||
const { id, prompt } = action.payload;
|
|
||||||
const rg = selectRG(state, id);
|
|
||||||
if (!rg) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
rg.negativePrompt = prompt;
|
|
||||||
},
|
|
||||||
rgFillChanged: (state, action: PayloadAction<{ id: string; fill: RgbColor }>) => {
|
|
||||||
const { id, fill } = action.payload;
|
|
||||||
const rg = selectRG(state, id);
|
|
||||||
if (!rg) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
rg.fill = fill;
|
|
||||||
},
|
|
||||||
rgMaskImageUploaded: (state, action: PayloadAction<{ id: string; imageDTO: ImageDTO }>) => {
|
|
||||||
const { id, imageDTO } = action.payload;
|
|
||||||
const rg = selectRG(state, id);
|
|
||||||
if (!rg) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
rg.imageCache = imageDTOToImageWithDims(imageDTO);
|
|
||||||
},
|
|
||||||
rgAutoNegativeChanged: (state, action: PayloadAction<{ id: string; autoNegative: ParameterAutoNegative }>) => {
|
|
||||||
const { id, autoNegative } = action.payload;
|
|
||||||
const rg = selectRG(state, id);
|
|
||||||
if (!rg) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
rg.autoNegative = autoNegative;
|
|
||||||
},
|
|
||||||
rgIPAdapterAdded: (state, action: PayloadAction<{ id: string; ipAdapter: IPAdapterData }>) => {
|
|
||||||
const { id, ipAdapter } = action.payload;
|
|
||||||
const rg = selectRG(state, id);
|
|
||||||
if (!rg) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
rg.ipAdapters.push(ipAdapter);
|
|
||||||
},
|
|
||||||
rgIPAdapterDeleted: (state, action: PayloadAction<{ id: string; ipAdapterId: string }>) => {
|
|
||||||
const { id, ipAdapterId } = action.payload;
|
|
||||||
const rg = selectRG(state, id);
|
|
||||||
if (!rg) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
rg.ipAdapters = rg.ipAdapters.filter((ipAdapter) => ipAdapter.id !== ipAdapterId);
|
|
||||||
},
|
|
||||||
rgIPAdapterImageChanged: (
|
|
||||||
state,
|
|
||||||
action: PayloadAction<{ id: string; ipAdapterId: string; imageDTO: ImageDTO | null }>
|
|
||||||
) => {
|
|
||||||
const { id, ipAdapterId, imageDTO } = action.payload;
|
|
||||||
const rg = selectRG(state, id);
|
|
||||||
if (!rg) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const ipa = rg.ipAdapters.find((ipa) => ipa.id === ipAdapterId);
|
|
||||||
if (!ipa) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ipa.image = imageDTO ? imageDTOToImageWithDims(imageDTO) : null;
|
|
||||||
},
|
|
||||||
rgIPAdapterWeightChanged: (state, action: PayloadAction<{ id: string; ipAdapterId: string; weight: number }>) => {
|
|
||||||
const { id, ipAdapterId, weight } = action.payload;
|
|
||||||
const rg = selectRG(state, id);
|
|
||||||
if (!rg) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const ipa = rg.ipAdapters.find((ipa) => ipa.id === ipAdapterId);
|
|
||||||
if (!ipa) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ipa.weight = weight;
|
|
||||||
},
|
|
||||||
rgIPAdapterBeginEndStepPctChanged: (
|
|
||||||
state,
|
|
||||||
action: PayloadAction<{ id: string; ipAdapterId: string; beginEndStepPct: [number, number] }>
|
|
||||||
) => {
|
|
||||||
const { id, ipAdapterId, beginEndStepPct } = action.payload;
|
|
||||||
const rg = selectRG(state, id);
|
|
||||||
if (!rg) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const ipa = rg.ipAdapters.find((ipa) => ipa.id === ipAdapterId);
|
|
||||||
if (!ipa) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ipa.beginEndStepPct = beginEndStepPct;
|
|
||||||
},
|
|
||||||
rgIPAdapterMethodChanged: (
|
|
||||||
state,
|
|
||||||
action: PayloadAction<{ id: string; ipAdapterId: string; method: IPMethodV2 }>
|
|
||||||
) => {
|
|
||||||
const { id, ipAdapterId, method } = action.payload;
|
|
||||||
const rg = selectRG(state, id);
|
|
||||||
if (!rg) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const ipa = rg.ipAdapters.find((ipa) => ipa.id === ipAdapterId);
|
|
||||||
if (!ipa) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ipa.method = method;
|
|
||||||
},
|
|
||||||
rgIPAdapterModelChanged: (
|
|
||||||
state,
|
|
||||||
action: PayloadAction<{
|
|
||||||
id: string;
|
|
||||||
ipAdapterId: string;
|
|
||||||
modelConfig: IPAdapterModelConfig | null;
|
|
||||||
}>
|
|
||||||
) => {
|
|
||||||
const { id, ipAdapterId, modelConfig } = action.payload;
|
|
||||||
const rg = selectRG(state, id);
|
|
||||||
if (!rg) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const ipa = rg.ipAdapters.find((ipa) => ipa.id === ipAdapterId);
|
|
||||||
if (!ipa) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (modelConfig) {
|
|
||||||
ipa.model = zModelIdentifierField.parse(modelConfig);
|
|
||||||
} else {
|
|
||||||
ipa.model = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
rgIPAdapterCLIPVisionModelChanged: (
|
|
||||||
state,
|
|
||||||
action: PayloadAction<{ id: string; ipAdapterId: string; clipVisionModel: CLIPVisionModelV2 }>
|
|
||||||
) => {
|
|
||||||
const { id, ipAdapterId, clipVisionModel } = action.payload;
|
|
||||||
const rg = selectRG(state, id);
|
|
||||||
if (!rg) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const ipa = rg.ipAdapters.find((ipa) => ipa.id === ipAdapterId);
|
|
||||||
if (!ipa) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ipa.clipVisionModel = clipVisionModel;
|
|
||||||
},
|
|
||||||
rgBrushLineAdded: {
|
|
||||||
reducer: (state, action: PayloadAction<BrushLineAddedArg & { lineId: string }>) => {
|
|
||||||
const { id, points, lineId, color, width } = action.payload;
|
|
||||||
const rg = selectRG(state, id);
|
|
||||||
if (!rg) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
rg.objects.push({
|
|
||||||
id: getBrushLineId(id, lineId),
|
|
||||||
type: 'brush_line',
|
|
||||||
points,
|
|
||||||
strokeWidth: width,
|
|
||||||
color,
|
|
||||||
});
|
|
||||||
rg.bboxNeedsUpdate = true;
|
|
||||||
rg.imageCache = null;
|
|
||||||
},
|
|
||||||
prepare: (payload: BrushLineAddedArg) => ({
|
|
||||||
payload: { ...payload, lineId: uuidv4() },
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
rgEraserLineAdded: {
|
|
||||||
reducer: (state, action: PayloadAction<EraserLineAddedArg & { lineId: string }>) => {
|
|
||||||
const { id, points, lineId, width } = action.payload;
|
|
||||||
const rg = selectRG(state, id);
|
|
||||||
if (!rg) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
rg.objects.push({
|
|
||||||
id: getEraserLineId(id, lineId),
|
|
||||||
type: 'eraser_line',
|
|
||||||
points,
|
|
||||||
strokeWidth: width,
|
|
||||||
});
|
|
||||||
rg.bboxNeedsUpdate = true;
|
|
||||||
rg.imageCache = null;
|
|
||||||
},
|
|
||||||
prepare: (payload: EraserLineAddedArg) => ({
|
|
||||||
payload: { ...payload, lineId: uuidv4() },
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
rgLinePointAdded: (state, action: PayloadAction<PointAddedToLineArg>) => {
|
|
||||||
const { id, point } = action.payload;
|
|
||||||
const rg = selectRG(state, id);
|
|
||||||
if (!rg) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const lastObject = rg.objects[rg.objects.length - 1];
|
|
||||||
if (!lastObject || !isLine(lastObject)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
lastObject.points.push(...point);
|
|
||||||
rg.bboxNeedsUpdate = true;
|
|
||||||
rg.imageCache = null;
|
|
||||||
},
|
|
||||||
rgRectAdded: {
|
|
||||||
reducer: (state, action: PayloadAction<RectShapeAddedArg & { rectId: string }>) => {
|
|
||||||
const { id, rect, rectId, color } = action.payload;
|
|
||||||
if (rect.height === 0 || rect.width === 0) {
|
|
||||||
// Ignore zero-area rectangles
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const rg = selectRG(state, id);
|
|
||||||
if (!rg) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
rg.objects.push({
|
|
||||||
type: 'rect_shape',
|
|
||||||
id: getRectShapeId(id, rectId),
|
|
||||||
...rect,
|
|
||||||
color,
|
|
||||||
});
|
|
||||||
rg.bboxNeedsUpdate = true;
|
|
||||||
rg.imageCache = null;
|
|
||||||
},
|
|
||||||
prepare: (payload: RectShapeAddedArg) => ({ payload: { ...payload, rectId: uuidv4() } }),
|
|
||||||
},
|
|
||||||
rgAllDeleted: (state) => {
|
|
||||||
state.regions = [];
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const {
|
|
||||||
rgAdded,
|
|
||||||
rgRecalled,
|
|
||||||
rgReset,
|
|
||||||
rgIsEnabledToggled,
|
|
||||||
rgTranslated,
|
|
||||||
rgBboxChanged,
|
|
||||||
rgDeleted,
|
|
||||||
rgGlobalOpacityChanged,
|
|
||||||
rgMovedForwardOne,
|
|
||||||
rgMovedToFront,
|
|
||||||
rgMovedBackwardOne,
|
|
||||||
rgMovedToBack,
|
|
||||||
rgPositivePromptChanged,
|
|
||||||
rgNegativePromptChanged,
|
|
||||||
rgFillChanged,
|
|
||||||
rgMaskImageUploaded,
|
|
||||||
rgAutoNegativeChanged,
|
|
||||||
rgIPAdapterAdded,
|
|
||||||
rgIPAdapterDeleted,
|
|
||||||
rgIPAdapterImageChanged,
|
|
||||||
rgIPAdapterWeightChanged,
|
|
||||||
rgIPAdapterBeginEndStepPctChanged,
|
|
||||||
rgIPAdapterMethodChanged,
|
|
||||||
rgIPAdapterModelChanged,
|
|
||||||
rgIPAdapterCLIPVisionModelChanged,
|
|
||||||
rgBrushLineAdded,
|
|
||||||
rgEraserLineAdded,
|
|
||||||
rgLinePointAdded,
|
|
||||||
rgRectAdded,
|
|
||||||
rgAllDeleted,
|
|
||||||
} = regionalGuidanceSlice.actions;
|
|
||||||
|
|
||||||
export const selectRegionalGuidanceSlice = (state: RootState) => state.regionalGuidance;
|
|
||||||
|
|
||||||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
|
||||||
const migrate = (state: any): any => {
|
|
||||||
return state;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const regionalGuidancePersistConfig: PersistConfig<RegionalGuidanceState> = {
|
|
||||||
name: regionalGuidanceSlice.name,
|
|
||||||
initialState,
|
|
||||||
migrate,
|
|
||||||
persistDenylist: [],
|
|
||||||
};
|
|
@ -0,0 +1,381 @@
|
|||||||
|
import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit';
|
||||||
|
import { moveOneToEnd, moveOneToStart, moveToEnd, moveToStart } from 'common/util/arrayUtils';
|
||||||
|
import { getBrushLineId, getEraserLineId, getRectShapeId } from 'features/controlLayers/konva/naming';
|
||||||
|
import type { CanvasV2State, CLIPVisionModelV2, IPMethodV2 } from 'features/controlLayers/store/types';
|
||||||
|
import { imageDTOToImageWithDims } from 'features/controlLayers/store/types';
|
||||||
|
import { zModelIdentifierField } from 'features/nodes/types/common';
|
||||||
|
import type { ParameterAutoNegative } from 'features/parameters/types/parameterSchemas';
|
||||||
|
import type { IRect } from 'konva/lib/types';
|
||||||
|
import { isEqual } from 'lodash-es';
|
||||||
|
import type { ImageDTO, IPAdapterModelConfig } from 'services/api/types';
|
||||||
|
import { assert } from 'tsafe';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
BrushLineAddedArg,
|
||||||
|
EraserLineAddedArg,
|
||||||
|
IPAdapterData,
|
||||||
|
PointAddedToLineArg,
|
||||||
|
RectShapeAddedArg,
|
||||||
|
RegionalGuidanceData,
|
||||||
|
RgbColor,
|
||||||
|
} from './types';
|
||||||
|
import { isLine } from './types';
|
||||||
|
|
||||||
|
export const selectRG = (state: CanvasV2State, id: string) => state.regions.find((rg) => rg.id === id);
|
||||||
|
export const selectRGOrThrow = (state: CanvasV2State, id: string) => {
|
||||||
|
const rg = selectRG(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)
|
||||||
|
{ r: 250, g: 225, b: 80 }, // rgb(250, 225, 80)
|
||||||
|
{ r: 220, g: 144, b: 101 }, // rgb(220, 144, 101)
|
||||||
|
{ r: 224, g: 117, b: 117 }, // rgb(224, 117, 117)
|
||||||
|
{ r: 213, g: 139, b: 202 }, // rgb(213, 139, 202)
|
||||||
|
{ r: 161, g: 120, b: 214 }, // rgb(161, 120, 214)
|
||||||
|
];
|
||||||
|
|
||||||
|
const getRGMaskFill = (state: CanvasV2State): RgbColor => {
|
||||||
|
const lastFill = state.regions.slice(-1)[0]?.fill;
|
||||||
|
let i = DEFAULT_MASK_COLORS.findIndex((c) => isEqual(c, lastFill));
|
||||||
|
if (i === -1) {
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
i = (i + 1) % DEFAULT_MASK_COLORS.length;
|
||||||
|
const fill = DEFAULT_MASK_COLORS[i];
|
||||||
|
assert(fill, 'This should never happen');
|
||||||
|
return fill;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const regionsReducers = {
|
||||||
|
rgAdded: {
|
||||||
|
reducer: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const rg: RegionalGuidanceData = {
|
||||||
|
id,
|
||||||
|
type: 'regional_guidance',
|
||||||
|
isEnabled: true,
|
||||||
|
bbox: null,
|
||||||
|
bboxNeedsUpdate: false,
|
||||||
|
objects: [],
|
||||||
|
fill: getRGMaskFill(state),
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
autoNegative: 'invert',
|
||||||
|
positivePrompt: '',
|
||||||
|
negativePrompt: null,
|
||||||
|
ipAdapters: [],
|
||||||
|
imageCache: null,
|
||||||
|
};
|
||||||
|
state.regions.push(rg);
|
||||||
|
},
|
||||||
|
prepare: () => ({ payload: { id: uuidv4() } }),
|
||||||
|
},
|
||||||
|
rgReset: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const rg = selectRG(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rg.objects = [];
|
||||||
|
rg.bbox = null;
|
||||||
|
rg.bboxNeedsUpdate = false;
|
||||||
|
rg.imageCache = null;
|
||||||
|
},
|
||||||
|
rgRecalled: (state, action: PayloadAction<{ data: RegionalGuidanceData }>) => {
|
||||||
|
const { data } = action.payload;
|
||||||
|
state.regions.push(data);
|
||||||
|
},
|
||||||
|
rgIsEnabledToggled: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const rg = selectRG(state, id);
|
||||||
|
if (rg) {
|
||||||
|
rg.isEnabled = !rg.isEnabled;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rgTranslated: (state, action: PayloadAction<{ id: string; x: number; y: number }>) => {
|
||||||
|
const { id, x, y } = action.payload;
|
||||||
|
const rg = selectRG(state, id);
|
||||||
|
if (rg) {
|
||||||
|
rg.x = x;
|
||||||
|
rg.y = y;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rgBboxChanged: (state, action: PayloadAction<{ id: string; bbox: IRect | null }>) => {
|
||||||
|
const { id, bbox } = action.payload;
|
||||||
|
const rg = selectRG(state, id);
|
||||||
|
if (rg) {
|
||||||
|
rg.bbox = bbox;
|
||||||
|
rg.bboxNeedsUpdate = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rgDeleted: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
state.regions = state.regions.filter((ca) => ca.id !== id);
|
||||||
|
},
|
||||||
|
rgGlobalOpacityChanged: (state, action: PayloadAction<{ opacity: number }>) => {
|
||||||
|
const { opacity } = action.payload;
|
||||||
|
state.maskFillOpacity = opacity;
|
||||||
|
},
|
||||||
|
rgMovedForwardOne: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const rg = selectRG(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
moveOneToEnd(state.regions, rg);
|
||||||
|
},
|
||||||
|
rgMovedToFront: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const rg = selectRG(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
moveToEnd(state.regions, rg);
|
||||||
|
},
|
||||||
|
rgMovedBackwardOne: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const rg = selectRG(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
moveOneToStart(state.regions, rg);
|
||||||
|
},
|
||||||
|
rgMovedToBack: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const rg = selectRG(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
moveToStart(state.regions, rg);
|
||||||
|
},
|
||||||
|
rgPositivePromptChanged: (state, action: PayloadAction<{ id: string; prompt: string | null }>) => {
|
||||||
|
const { id, prompt } = action.payload;
|
||||||
|
const rg = selectRG(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rg.positivePrompt = prompt;
|
||||||
|
},
|
||||||
|
rgNegativePromptChanged: (state, action: PayloadAction<{ id: string; prompt: string | null }>) => {
|
||||||
|
const { id, prompt } = action.payload;
|
||||||
|
const rg = selectRG(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rg.negativePrompt = prompt;
|
||||||
|
},
|
||||||
|
rgFillChanged: (state, action: PayloadAction<{ id: string; fill: RgbColor }>) => {
|
||||||
|
const { id, fill } = action.payload;
|
||||||
|
const rg = selectRG(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rg.fill = fill;
|
||||||
|
},
|
||||||
|
rgMaskImageUploaded: (state, action: PayloadAction<{ id: string; imageDTO: ImageDTO }>) => {
|
||||||
|
const { id, imageDTO } = action.payload;
|
||||||
|
const rg = selectRG(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rg.imageCache = imageDTOToImageWithDims(imageDTO);
|
||||||
|
},
|
||||||
|
rgAutoNegativeChanged: (state, action: PayloadAction<{ id: string; autoNegative: ParameterAutoNegative }>) => {
|
||||||
|
const { id, autoNegative } = action.payload;
|
||||||
|
const rg = selectRG(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rg.autoNegative = autoNegative;
|
||||||
|
},
|
||||||
|
rgIPAdapterAdded: (state, action: PayloadAction<{ id: string; ipAdapter: IPAdapterData }>) => {
|
||||||
|
const { id, ipAdapter } = action.payload;
|
||||||
|
const rg = selectRG(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rg.ipAdapters.push(ipAdapter);
|
||||||
|
},
|
||||||
|
rgIPAdapterDeleted: (state, action: PayloadAction<{ id: string; ipAdapterId: string }>) => {
|
||||||
|
const { id, ipAdapterId } = action.payload;
|
||||||
|
const rg = selectRG(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rg.ipAdapters = rg.ipAdapters.filter((ipAdapter) => ipAdapter.id !== ipAdapterId);
|
||||||
|
},
|
||||||
|
rgIPAdapterImageChanged: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{ id: string; ipAdapterId: string; imageDTO: ImageDTO | null }>
|
||||||
|
) => {
|
||||||
|
const { id, ipAdapterId, imageDTO } = action.payload;
|
||||||
|
const rg = selectRG(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ipa = rg.ipAdapters.find((ipa) => ipa.id === ipAdapterId);
|
||||||
|
if (!ipa) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ipa.image = imageDTO ? imageDTOToImageWithDims(imageDTO) : null;
|
||||||
|
},
|
||||||
|
rgIPAdapterWeightChanged: (state, action: PayloadAction<{ id: string; ipAdapterId: string; weight: number }>) => {
|
||||||
|
const { id, ipAdapterId, weight } = action.payload;
|
||||||
|
const rg = selectRG(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ipa = rg.ipAdapters.find((ipa) => ipa.id === ipAdapterId);
|
||||||
|
if (!ipa) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ipa.weight = weight;
|
||||||
|
},
|
||||||
|
rgIPAdapterBeginEndStepPctChanged: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{ id: string; ipAdapterId: string; beginEndStepPct: [number, number] }>
|
||||||
|
) => {
|
||||||
|
const { id, ipAdapterId, beginEndStepPct } = action.payload;
|
||||||
|
const rg = selectRG(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ipa = rg.ipAdapters.find((ipa) => ipa.id === ipAdapterId);
|
||||||
|
if (!ipa) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ipa.beginEndStepPct = beginEndStepPct;
|
||||||
|
},
|
||||||
|
rgIPAdapterMethodChanged: (state, action: PayloadAction<{ id: string; ipAdapterId: string; method: IPMethodV2 }>) => {
|
||||||
|
const { id, ipAdapterId, method } = action.payload;
|
||||||
|
const rg = selectRG(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ipa = rg.ipAdapters.find((ipa) => ipa.id === ipAdapterId);
|
||||||
|
if (!ipa) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ipa.method = method;
|
||||||
|
},
|
||||||
|
rgIPAdapterModelChanged: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{
|
||||||
|
id: string;
|
||||||
|
ipAdapterId: string;
|
||||||
|
modelConfig: IPAdapterModelConfig | null;
|
||||||
|
}>
|
||||||
|
) => {
|
||||||
|
const { id, ipAdapterId, modelConfig } = action.payload;
|
||||||
|
const rg = selectRG(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ipa = rg.ipAdapters.find((ipa) => ipa.id === ipAdapterId);
|
||||||
|
if (!ipa) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (modelConfig) {
|
||||||
|
ipa.model = zModelIdentifierField.parse(modelConfig);
|
||||||
|
} else {
|
||||||
|
ipa.model = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rgIPAdapterCLIPVisionModelChanged: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{ id: string; ipAdapterId: string; clipVisionModel: CLIPVisionModelV2 }>
|
||||||
|
) => {
|
||||||
|
const { id, ipAdapterId, clipVisionModel } = action.payload;
|
||||||
|
const rg = selectRG(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ipa = rg.ipAdapters.find((ipa) => ipa.id === ipAdapterId);
|
||||||
|
if (!ipa) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ipa.clipVisionModel = clipVisionModel;
|
||||||
|
},
|
||||||
|
rgBrushLineAdded: {
|
||||||
|
reducer: (state, action: PayloadAction<BrushLineAddedArg & { lineId: string }>) => {
|
||||||
|
const { id, points, lineId, color, width } = action.payload;
|
||||||
|
const rg = selectRG(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rg.objects.push({
|
||||||
|
id: getBrushLineId(id, lineId),
|
||||||
|
type: 'brush_line',
|
||||||
|
points,
|
||||||
|
strokeWidth: width,
|
||||||
|
color,
|
||||||
|
});
|
||||||
|
rg.bboxNeedsUpdate = true;
|
||||||
|
rg.imageCache = null;
|
||||||
|
},
|
||||||
|
prepare: (payload: BrushLineAddedArg) => ({
|
||||||
|
payload: { ...payload, lineId: uuidv4() },
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
rgEraserLineAdded: {
|
||||||
|
reducer: (state, action: PayloadAction<EraserLineAddedArg & { lineId: string }>) => {
|
||||||
|
const { id, points, lineId, width } = action.payload;
|
||||||
|
const rg = selectRG(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rg.objects.push({
|
||||||
|
id: getEraserLineId(id, lineId),
|
||||||
|
type: 'eraser_line',
|
||||||
|
points,
|
||||||
|
strokeWidth: width,
|
||||||
|
});
|
||||||
|
rg.bboxNeedsUpdate = true;
|
||||||
|
rg.imageCache = null;
|
||||||
|
},
|
||||||
|
prepare: (payload: EraserLineAddedArg) => ({
|
||||||
|
payload: { ...payload, lineId: uuidv4() },
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
rgLinePointAdded: (state, action: PayloadAction<PointAddedToLineArg>) => {
|
||||||
|
const { id, point } = action.payload;
|
||||||
|
const rg = selectRG(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const lastObject = rg.objects[rg.objects.length - 1];
|
||||||
|
if (!lastObject || !isLine(lastObject)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lastObject.points.push(...point);
|
||||||
|
rg.bboxNeedsUpdate = true;
|
||||||
|
rg.imageCache = null;
|
||||||
|
},
|
||||||
|
rgRectAdded: {
|
||||||
|
reducer: (state, action: PayloadAction<RectShapeAddedArg & { rectId: string }>) => {
|
||||||
|
const { id, rect, rectId, color } = action.payload;
|
||||||
|
if (rect.height === 0 || rect.width === 0) {
|
||||||
|
// Ignore zero-area rectangles
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const rg = selectRG(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rg.objects.push({
|
||||||
|
type: 'rect_shape',
|
||||||
|
id: getRectShapeId(id, rectId),
|
||||||
|
...rect,
|
||||||
|
color,
|
||||||
|
});
|
||||||
|
rg.bboxNeedsUpdate = true;
|
||||||
|
rg.imageCache = null;
|
||||||
|
},
|
||||||
|
prepare: (payload: RectShapeAddedArg) => ({ payload: { ...payload, rectId: uuidv4() } }),
|
||||||
|
},
|
||||||
|
} satisfies SliceCaseReducers<CanvasV2State>;
|
@ -0,0 +1,57 @@
|
|||||||
|
import type { ActionReducerMapBuilder, PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit';
|
||||||
|
import { createSlice } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
|
type MySlice = {
|
||||||
|
flavour: 'vanilla' | 'chocolate' | 'strawberry';
|
||||||
|
sprinkles: boolean;
|
||||||
|
customers: { id: string; name: string }[];
|
||||||
|
};
|
||||||
|
const initialStateMySlice: MySlice = { flavour: 'vanilla', sprinkles: false, customers: [] };
|
||||||
|
|
||||||
|
const reducersInAnotherFile: SliceCaseReducers<MySlice> = {
|
||||||
|
sprinklesToggled: (state) => {
|
||||||
|
state.sprinkles = !state.sprinkles;
|
||||||
|
},
|
||||||
|
customerAdded: {
|
||||||
|
reducer: (state, action: PayloadAction<{ id: string; name: string }>) => {
|
||||||
|
state.customers.push(action.payload);
|
||||||
|
},
|
||||||
|
prepare: (name: string) => ({ payload: { name, id: crypto.randomUUID() } }),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const extraReducersInAnotherFile = (builder: ActionReducerMapBuilder<MySlice>) => {
|
||||||
|
builder.addCase(otherSlice.actions.fooChanged, (state, action) => {
|
||||||
|
if (action.payload === 'bar') {
|
||||||
|
state.flavour = 'vanilla';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mySlice = createSlice({
|
||||||
|
name: 'mySlice',
|
||||||
|
initialState: initialStateMySlice,
|
||||||
|
reducers: {
|
||||||
|
...reducersInAnotherFile,
|
||||||
|
flavourChanged: (state, action: PayloadAction<MySlice['flavour']>) => {
|
||||||
|
state.flavour = action.payload;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
extraReducers: extraReducersInAnotherFile,
|
||||||
|
});
|
||||||
|
|
||||||
|
type OtherSlice = {
|
||||||
|
something: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialStateOtherSlice: OtherSlice = { something: 'foo' };
|
||||||
|
|
||||||
|
export const otherSlice = createSlice({
|
||||||
|
name: 'otherSlice',
|
||||||
|
initialState: initialStateOtherSlice,
|
||||||
|
reducers: {
|
||||||
|
fooChanged: (state, action: PayloadAction<string>) => {
|
||||||
|
state.something = action.payload;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
@ -783,6 +783,11 @@ export type CanvasV2State = {
|
|||||||
aspectRatio: AspectRatioState;
|
aspectRatio: AspectRatioState;
|
||||||
};
|
};
|
||||||
bbox: IRect;
|
bbox: IRect;
|
||||||
|
layers: LayerData[];
|
||||||
|
controlAdapters: ControlAdapterData[];
|
||||||
|
ipAdapters: IPAdapterData[];
|
||||||
|
regions: RegionalGuidanceData[];
|
||||||
|
maskFillOpacity: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type StageAttrs = { x: number; y: number; width: number; height: number; scale: number };
|
export type StageAttrs = { x: number; y: number; width: number; height: number; scale: number };
|
||||||
|
@ -3,7 +3,7 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
|||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
|
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
|
||||||
import { selectControlAdaptersSlice } from 'features/controlAdapters/store/controlAdaptersSlice';
|
import { selectControlAdaptersSlice } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/controlLayersSlice';
|
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { imageDeletionConfirmed } from 'features/deleteImageModal/store/actions';
|
import { imageDeletionConfirmed } from 'features/deleteImageModal/store/actions';
|
||||||
import { getImageUsage, selectImageUsage } from 'features/deleteImageModal/store/selectors';
|
import { getImageUsage, selectImageUsage } from 'features/deleteImageModal/store/selectors';
|
||||||
import {
|
import {
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||||
import type { ControlAdaptersState } from 'features/controlAdapters/store/types';
|
import type { ControlAdaptersState } from 'features/controlAdapters/store/types';
|
||||||
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
|
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
|
||||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/controlLayersSlice';
|
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import type { CanvasV2State } from 'features/controlLayers/store/types';
|
import type { CanvasV2State } from 'features/controlLayers/store/types';
|
||||||
import {
|
import {
|
||||||
isControlAdapterLayer,
|
isControlAdapterLayer,
|
||||||
|
@ -15,7 +15,7 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
|||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
|
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
|
||||||
import { selectControlAdaptersSlice } from 'features/controlAdapters/store/controlAdaptersSlice';
|
import { selectControlAdaptersSlice } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/controlLayersSlice';
|
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import ImageUsageMessage from 'features/deleteImageModal/components/ImageUsageMessage';
|
import ImageUsageMessage from 'features/deleteImageModal/components/ImageUsageMessage';
|
||||||
import { getImageUsage } from 'features/deleteImageModal/store/selectors';
|
import { getImageUsage } from 'features/deleteImageModal/store/selectors';
|
||||||
import type { ImageUsage } from 'features/deleteImageModal/store/types';
|
import type { ImageUsage } from 'features/deleteImageModal/store/types';
|
||||||
|
@ -6,7 +6,7 @@ import { useCopyImageToClipboard } from 'common/hooks/useCopyImageToClipboard';
|
|||||||
import { useDownloadImage } from 'common/hooks/useDownloadImage';
|
import { useDownloadImage } from 'common/hooks/useDownloadImage';
|
||||||
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
||||||
import { imagesToChangeSelected, isModalOpenChanged } from 'features/changeBoardModal/store/slice';
|
import { imagesToChangeSelected, isModalOpenChanged } from 'features/changeBoardModal/store/slice';
|
||||||
import { iiLayerAdded } from 'features/controlLayers/store/controlLayersSlice';
|
import { iiLayerAdded } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice';
|
import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice';
|
||||||
import { useImageActions } from 'features/gallery/hooks/useImageActions';
|
import { useImageActions } from 'features/gallery/hooks/useImageActions';
|
||||||
import { sentImageToCanvas, sentImageToImg2Img } from 'features/gallery/store/actions';
|
import { sentImageToCanvas, sentImageToImg2Img } from 'features/gallery/store/actions';
|
||||||
|
@ -4,7 +4,7 @@ import { createSelector } from '@reduxjs/toolkit';
|
|||||||
import { skipToken } from '@reduxjs/toolkit/query';
|
import { skipToken } from '@reduxjs/toolkit/query';
|
||||||
import { adHocPostProcessingRequested } from 'app/store/middleware/listenerMiddleware/listeners/addAdHocPostProcessingRequestedListener';
|
import { adHocPostProcessingRequested } from 'app/store/middleware/listenerMiddleware/listeners/addAdHocPostProcessingRequestedListener';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { iiLayerAdded } from 'features/controlLayers/store/controlLayersSlice';
|
import { iiLayerAdded } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { DeleteImageButton } from 'features/deleteImageModal/components/DeleteImageButton';
|
import { DeleteImageButton } from 'features/deleteImageModal/components/DeleteImageButton';
|
||||||
import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice';
|
import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice';
|
||||||
import SingleSelectionMenuItems from 'features/gallery/components/ImageContextMenu/SingleSelectionMenuItems';
|
import SingleSelectionMenuItems from 'features/gallery/components/ImageContextMenu/SingleSelectionMenuItems';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { getStore } from 'app/store/nanostores/store';
|
import { getStore } from 'app/store/nanostores/store';
|
||||||
import { deepClone } from 'common/util/deepClone';
|
import { deepClone } from 'common/util/deepClone';
|
||||||
import { objectKeys } from 'common/util/objectKeys';
|
import { objectKeys } from 'common/util/objectKeys';
|
||||||
import { shouldConcatPromptsChanged } from 'features/controlLayers/store/controlLayersSlice';
|
import { shouldConcatPromptsChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import type { LayerData } from 'features/controlLayers/store/types';
|
import type { LayerData } from 'features/controlLayers/store/types';
|
||||||
import type { LoRA } from 'features/lora/store/loraSlice';
|
import type { LoRA } from 'features/lora/store/loraSlice';
|
||||||
import type {
|
import type {
|
||||||
|
@ -19,7 +19,7 @@ import {
|
|||||||
positivePromptChanged,
|
positivePromptChanged,
|
||||||
regionalGuidanceRecalled,
|
regionalGuidanceRecalled,
|
||||||
widthChanged,
|
widthChanged,
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
} from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import type { LayerData } from 'features/controlLayers/store/types';
|
import type { LayerData } from 'features/controlLayers/store/types';
|
||||||
import { setHrfEnabled, setHrfMethod, setHrfStrength } from 'features/hrf/store/hrfSlice';
|
import { setHrfEnabled, setHrfMethod, setHrfStrength } from 'features/hrf/store/hrfSlice';
|
||||||
import type { LoRA } from 'features/lora/store/loraSlice';
|
import type { LoRA } from 'features/lora/store/loraSlice';
|
||||||
|
@ -5,7 +5,7 @@ import openBase64ImageInTab from 'common/util/openBase64ImageInTab';
|
|||||||
import { blobToDataURL } from 'features/canvas/util/blobToDataURL';
|
import { blobToDataURL } from 'features/canvas/util/blobToDataURL';
|
||||||
import { RG_LAYER_NAME } from 'features/controlLayers/konva/naming';
|
import { RG_LAYER_NAME } from 'features/controlLayers/konva/naming';
|
||||||
import { renderers } from 'features/controlLayers/konva/renderers/layers';
|
import { renderers } from 'features/controlLayers/konva/renderers/layers';
|
||||||
import { regionalGuidanceMaskImageUploaded } from 'features/controlLayers/store/controlLayersSlice';
|
import { regionalGuidanceMaskImageUploaded } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import type { InitialImageLayer, LayerData, RegionalGuidanceLayer } from 'features/controlLayers/store/types';
|
import type { InitialImageLayer, LayerData, RegionalGuidanceLayer } from 'features/controlLayers/store/types';
|
||||||
import {
|
import {
|
||||||
isControlAdapterLayer,
|
isControlAdapterLayer,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Box, Textarea } from '@invoke-ai/ui-library';
|
import { Box, Textarea } from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { negativePromptChanged } from 'features/controlLayers/store/controlLayersSlice';
|
import { negativePromptChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { PromptLabel } from 'features/parameters/components/Prompts/PromptLabel';
|
import { PromptLabel } from 'features/parameters/components/Prompts/PromptLabel';
|
||||||
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
||||||
import { ViewModePrompt } from 'features/parameters/components/Prompts/ViewModePrompt';
|
import { ViewModePrompt } from 'features/parameters/components/Prompts/ViewModePrompt';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Box, Textarea } from '@invoke-ai/ui-library';
|
import { Box, Textarea } from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { positivePromptChanged } from 'features/controlLayers/store/controlLayersSlice';
|
import { positivePromptChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { ShowDynamicPromptsPreviewButton } from 'features/dynamicPrompts/components/ShowDynamicPromptsPreviewButton';
|
import { ShowDynamicPromptsPreviewButton } from 'features/dynamicPrompts/components/ShowDynamicPromptsPreviewButton';
|
||||||
import { PromptLabel } from 'features/parameters/components/Prompts/PromptLabel';
|
import { PromptLabel } from 'features/parameters/components/Prompts/PromptLabel';
|
||||||
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Flex } from '@invoke-ai/ui-library';
|
import { Flex } from '@invoke-ai/ui-library';
|
||||||
import { useStore } from '@nanostores/react';
|
import { useStore } from '@nanostores/react';
|
||||||
import { StageComponent } from 'features/controlLayers/components/StageComponent';
|
import { StageComponent } from 'features/controlLayers/components/StageComponent';
|
||||||
import { $isPreviewVisible } from 'features/controlLayers/store/controlLayersSlice';
|
import { $isPreviewVisible } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { AspectRatioIconPreview } from 'features/parameters/components/ImageSize/AspectRatioIconPreview';
|
import { AspectRatioIconPreview } from 'features/parameters/components/ImageSize/AspectRatioIconPreview';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { skipToken } from '@reduxjs/toolkit/query';
|
import { skipToken } from '@reduxjs/toolkit/query';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
||||||
import { iiLayerAdded } from 'features/controlLayers/store/controlLayersSlice';
|
import { iiLayerAdded } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { parseAndRecallAllMetadata } from 'features/metadata/util/handlers';
|
import { parseAndRecallAllMetadata } from 'features/metadata/util/handlers';
|
||||||
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
import { toast } from 'features/toast/toast';
|
import { toast } from 'features/toast/toast';
|
||||||
|
@ -2,7 +2,7 @@ import { Divider, Flex, ListItem, Text, Tooltip, UnorderedList } from '@invoke-a
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { useIsReadyToEnqueue } from 'common/hooks/useIsReadyToEnqueue';
|
import { useIsReadyToEnqueue } from 'common/hooks/useIsReadyToEnqueue';
|
||||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/controlLayersSlice';
|
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
|
import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
|
||||||
import { getShouldProcessPrompt } from 'features/dynamicPrompts/util/getShouldProcessPrompt';
|
import { getShouldProcessPrompt } from 'features/dynamicPrompts/util/getShouldProcessPrompt';
|
||||||
import type { PropsWithChildren } from 'react';
|
import type { PropsWithChildren } from 'react';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Box, Textarea } from '@invoke-ai/ui-library';
|
import { Box, Textarea } from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { negativePrompt2Changed } from 'features/controlLayers/store/controlLayersSlice';
|
import { negativePrompt2Changed } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { PromptLabel } from 'features/parameters/components/Prompts/PromptLabel';
|
import { PromptLabel } from 'features/parameters/components/Prompts/PromptLabel';
|
||||||
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
||||||
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
|
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Box, Textarea } from '@invoke-ai/ui-library';
|
import { Box, Textarea } from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { positivePrompt2Changed } from 'features/controlLayers/store/controlLayersSlice';
|
import { positivePrompt2Changed } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { PromptLabel } from 'features/parameters/components/Prompts/PromptLabel';
|
import { PromptLabel } from 'features/parameters/components/Prompts/PromptLabel';
|
||||||
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
||||||
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
|
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { IconButton, Tooltip } from '@invoke-ai/ui-library';
|
import { IconButton, Tooltip } from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { shouldConcatPromptsChanged } from 'features/controlLayers/store/controlLayersSlice';
|
import { shouldConcatPromptsChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { PiLinkSimpleBold, PiLinkSimpleBreakBold } from 'react-icons/pi';
|
import { PiLinkSimpleBold, PiLinkSimpleBreakBold } from 'react-icons/pi';
|
||||||
|
@ -2,7 +2,7 @@ import type { FormLabelProps } from '@invoke-ai/ui-library';
|
|||||||
import { Expander, Flex, FormControlGroup, StandaloneAccordion } from '@invoke-ai/ui-library';
|
import { Expander, Flex, FormControlGroup, StandaloneAccordion } from '@invoke-ai/ui-library';
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/controlLayersSlice';
|
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { HrfSettings } from 'features/hrf/components/HrfSettings';
|
import { HrfSettings } from 'features/hrf/components/HrfSettings';
|
||||||
import { selectHrfSlice } from 'features/hrf/store/hrfSlice';
|
import { selectHrfSlice } from 'features/hrf/store/hrfSlice';
|
||||||
import ParamScaleBeforeProcessing from 'features/parameters/components/Canvas/InfillAndScaling/ParamScaleBeforeProcessing';
|
import ParamScaleBeforeProcessing from 'features/parameters/components/Canvas/InfillAndScaling/ParamScaleBeforeProcessing';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { aspectRatioChanged, heightChanged, widthChanged } from 'features/controlLayers/store/controlLayersSlice';
|
import { aspectRatioChanged, heightChanged, widthChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { ParamHeight } from 'features/parameters/components/Core/ParamHeight';
|
import { ParamHeight } from 'features/parameters/components/Core/ParamHeight';
|
||||||
import { ParamWidth } from 'features/parameters/components/Core/ParamWidth';
|
import { ParamWidth } from 'features/parameters/components/Core/ParamWidth';
|
||||||
import { AspectRatioCanvasPreview } from 'features/parameters/components/ImageSize/AspectRatioCanvasPreview';
|
import { AspectRatioCanvasPreview } from 'features/parameters/components/ImageSize/AspectRatioCanvasPreview';
|
||||||
|
@ -4,7 +4,7 @@ import { useStore } from '@nanostores/react';
|
|||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
|
import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
|
||||||
import { ControlLayersPanelContent } from 'features/controlLayers/components/ControlLayersPanelContent';
|
import { ControlLayersPanelContent } from 'features/controlLayers/components/ControlLayersPanelContent';
|
||||||
import { $isPreviewVisible } from 'features/controlLayers/store/controlLayersSlice';
|
import { $isPreviewVisible } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { isImageViewerOpenChanged } from 'features/gallery/store/gallerySlice';
|
import { isImageViewerOpenChanged } from 'features/gallery/store/gallerySlice';
|
||||||
import { Prompts } from 'features/parameters/components/Prompts/Prompts';
|
import { Prompts } from 'features/parameters/components/Prompts/Prompts';
|
||||||
import QueueControls from 'features/queue/components/QueueControls';
|
import QueueControls from 'features/queue/components/QueueControls';
|
||||||
|
Loading…
Reference in New Issue
Block a user