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
8a6690a57c
commit
19c66e5c76
@ -1,7 +1,7 @@
|
||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||
import { resetCanvas } from 'features/canvas/store/canvasSlice';
|
||||
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 { nodeEditorReset } from 'features/nodes/store/nodesSlice';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
controlAdapterProcessorConfigChanged,
|
||||
controlAdapterProcessorPendingBatchIdChanged,
|
||||
controlAdapterRecalled,
|
||||
} from 'features/controlLayers/store/controlLayersSlice';
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { isControlAdapterLayer } from 'features/controlLayers/store/types';
|
||||
import { CA_PROCESSOR_DATA } from 'features/controlLayers/util/controlAdapters';
|
||||
import { toast } from 'features/toast/toast';
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
selectControlAdapterAll,
|
||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
|
||||
import { layerDeleted } from 'features/controlLayers/store/controlLayersSlice';
|
||||
import { layerDeleted } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import {
|
||||
isControlAdapterLayer,
|
||||
isInitialImageLayer,
|
||||
|
@ -13,7 +13,7 @@ import {
|
||||
layerImageAdded,
|
||||
ipAdapterImageChanged,
|
||||
regionalGuidanceIPAdapterImageChanged,
|
||||
} from 'features/controlLayers/store/controlLayersSlice';
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import type { TypesafeDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
||||
import { isValidDrop } from 'features/dnd/util/isValidDrop';
|
||||
import {
|
||||
|
@ -1,19 +1,8 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
||||
import {
|
||||
controlAdapterImageChanged,
|
||||
controlAdapterIsEnabledChanged,
|
||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import {
|
||||
controlAdapterImageChanged,
|
||||
iiLayerImageChanged,
|
||||
ipAdapterImageChanged,
|
||||
regionalGuidanceIPAdapterImageChanged,
|
||||
} from 'features/controlLayers/store/controlLayersSlice';
|
||||
import { caImageChanged, ipaImageChanged, rgIPAdapterImageChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectListBoardsQueryArgs } from 'features/gallery/store/gallerySelectors';
|
||||
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
|
||||
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||
import { upscaleInitialImageChanged } from 'features/parameters/store/upscaleSlice';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import { t } from 'i18next';
|
||||
@ -81,15 +70,6 @@ export const addImageUploadedFulfilledListener = (startAppListening: AppStartLis
|
||||
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') {
|
||||
dispatch(upscaleInitialImageChanged(imageDTO));
|
||||
toast({
|
||||
@ -99,57 +79,27 @@ export const addImageUploadedFulfilledListener = (startAppListening: AppStartLis
|
||||
return;
|
||||
}
|
||||
|
||||
if (postUploadAction?.type === 'SET_CONTROL_ADAPTER_IMAGE') {
|
||||
if (postUploadAction?.type === 'SET_CA_IMAGE') {
|
||||
const { id } = postUploadAction;
|
||||
dispatch(
|
||||
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 }));
|
||||
dispatch(caImageChanged({ id, imageDTO }));
|
||||
toast({
|
||||
...DEFAULT_UPLOADED_TOAST,
|
||||
description: t('toast.setControlImage'),
|
||||
});
|
||||
}
|
||||
|
||||
if (postUploadAction?.type === 'SET_IPA_LAYER_IMAGE') {
|
||||
const { layerId } = postUploadAction;
|
||||
dispatch(ipAdapterImageChanged({ layerId, imageDTO }));
|
||||
if (postUploadAction?.type === 'SET_IPA_IMAGE') {
|
||||
const { id } = postUploadAction;
|
||||
dispatch(ipaImageChanged({ id, imageDTO }));
|
||||
toast({
|
||||
...DEFAULT_UPLOADED_TOAST,
|
||||
description: t('toast.setControlImage'),
|
||||
});
|
||||
}
|
||||
|
||||
if (postUploadAction?.type === 'SET_RG_LAYER_IP_ADAPTER_IMAGE') {
|
||||
const { layerId, ipAdapterId } = postUploadAction;
|
||||
dispatch(regionalGuidanceIPAdapterImageChanged({ layerId, ipAdapterId, imageDTO }));
|
||||
toast({
|
||||
...DEFAULT_UPLOADED_TOAST,
|
||||
description: t('toast.setControlImage'),
|
||||
});
|
||||
}
|
||||
|
||||
if (postUploadAction?.type === 'SET_II_LAYER_IMAGE') {
|
||||
const { layerId } = postUploadAction;
|
||||
dispatch(iiLayerImageChanged({ layerId, imageDTO }));
|
||||
if (postUploadAction?.type === 'SET_RG_IP_ADAPTER_IMAGE') {
|
||||
const { id, ipAdapterId } = postUploadAction;
|
||||
dispatch(rgIPAdapterImageChanged({ id, ipAdapterId, imageDTO }));
|
||||
toast({
|
||||
...DEFAULT_UPLOADED_TOAST,
|
||||
description: t('toast.setControlImage'),
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
controlAdapterModelCleared,
|
||||
selectControlAdapterAll,
|
||||
} 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 { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
||||
import { modelChanged, vaeSelected } from 'features/parameters/store/generationSlice';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { isAnyOf } from '@reduxjs/toolkit';
|
||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||
import { positivePromptChanged } from 'features/controlLayers/store/controlLayersSlice';
|
||||
import { positivePromptChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import {
|
||||
combinatorialToggled,
|
||||
isErrorChanged,
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 {
|
||||
setCfgRescaleMultiplier,
|
||||
|
@ -5,17 +5,7 @@ import { idbKeyValDriver } from 'app/store/enhancers/reduxRemember/driver';
|
||||
import { errorHandler } from 'app/store/enhancers/reduxRemember/errors';
|
||||
import type { JSONObject } from 'common/types';
|
||||
import { changeBoardModalSlice } from 'features/changeBoardModal/store/slice';
|
||||
import {
|
||||
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 { canvasV2PersistConfig, canvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { deleteImageModalSlice } from 'features/deleteImageModal/store/slice';
|
||||
import { dynamicPromptsPersistConfig, dynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
|
||||
import { galleryPersistConfig, gallerySlice } from 'features/gallery/store/gallerySlice';
|
||||
@ -70,10 +60,6 @@ const allReducers = {
|
||||
[workflowSettingsSlice.name]: workflowSettingsSlice.reducer,
|
||||
[upscaleSlice.name]: upscaleSlice.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);
|
||||
@ -118,10 +104,6 @@ const persistConfigs: { [key in keyof typeof allReducers]?: PersistConfig } = {
|
||||
[workflowSettingsPersistConfig.name]: workflowSettingsPersistConfig,
|
||||
[upscalePersistConfig.name]: upscalePersistConfig,
|
||||
[stylePresetPersistConfig.name]: stylePresetPersistConfig,
|
||||
[layersPersistConfig.name]: layersPersistConfig,
|
||||
[controlAdaptersV2PersistConfig.name]: controlAdaptersV2PersistConfig,
|
||||
[ipAdaptersPersistConfig.name]: ipAdaptersPersistConfig,
|
||||
[regionalGuidancePersistConfig.name]: regionalGuidancePersistConfig,
|
||||
};
|
||||
|
||||
const unserialize: UnserializeFunction = (data, key) => {
|
||||
|
@ -2,7 +2,7 @@ import { useStore } from '@nanostores/react';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
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 { selectLayersSlice } from 'features/controlLayers/store/layersSlice';
|
||||
import { selectRegionalGuidanceSlice } from 'features/controlLayers/store/regionalGuidanceSlice';
|
||||
|
@ -12,7 +12,7 @@ import {
|
||||
controlAdapterImageChanged,
|
||||
selectControlAdaptersSlice,
|
||||
} 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 { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
||||
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { Button, Menu, MenuButton, MenuItem, MenuList } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { useAddCALayer, useAddIPALayer } from 'features/controlLayers/hooks/addLayerHooks';
|
||||
import { layerAdded } from 'features/controlLayers/store/layersSlice';
|
||||
import { rgAdded } from 'features/controlLayers/store/regionalGuidanceSlice';
|
||||
import { layerAdded, rgAdded } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiPlusBold } from 'react-icons/pi';
|
||||
|
@ -5,8 +5,8 @@ import { useAddIPAdapterToRGLayer } from 'features/controlLayers/hooks/addLayerH
|
||||
import {
|
||||
rgNegativePromptChanged,
|
||||
rgPositivePromptChanged,
|
||||
selectRegionalGuidanceSlice,
|
||||
} from 'features/controlLayers/store/regionalGuidanceSlice';
|
||||
selectCanvasV2Slice,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiPlusBold } from 'react-icons/pi';
|
||||
@ -21,8 +21,8 @@ export const AddPromptButtons = ({ id }: AddPromptButtonProps) => {
|
||||
const [addIPAdapter, isAddIPAdapterDisabled] = useAddIPAdapterToRGLayer(id);
|
||||
const selectValidActions = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(selectRegionalGuidanceSlice, (regionalGuidanceState) => {
|
||||
const rg = regionalGuidanceState.regions.find((rg) => rg.id === id);
|
||||
createMemoizedSelector(selectCanvasV2Slice, (caState) => {
|
||||
const rg = caState.regions.find((rg) => rg.id === id);
|
||||
return {
|
||||
canAddPositivePrompt: rg?.positivePrompt === null,
|
||||
canAddNegativePrompt: rg?.negativePrompt === null,
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
PopoverTrigger,
|
||||
} from '@invoke-ai/ui-library';
|
||||
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 { useTranslation } from 'react-i18next';
|
||||
|
||||
|
@ -3,7 +3,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
|
||||
import { CAHeader } from 'features/controlLayers/components/ControlAdapter/CAEntityHeader';
|
||||
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';
|
||||
|
||||
type Props = {
|
||||
|
@ -8,9 +8,9 @@ import {
|
||||
caMovedForwardOne,
|
||||
caMovedToBack,
|
||||
caMovedToFront,
|
||||
selectCAOrThrow,
|
||||
selectControlAdaptersV2Slice,
|
||||
} from 'features/controlLayers/store/controlAdaptersSlice';
|
||||
selectCanvasV2Slice,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCAOrThrow } from 'features/controlLayers/store/controlAdaptersReducers';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
@ -25,20 +25,17 @@ type Props = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
const selectValidActions = createAppSelector(
|
||||
[selectControlAdaptersV2Slice, (caState, id: string) => id],
|
||||
(caState, id) => {
|
||||
const ca = selectCAOrThrow(caState, id);
|
||||
const caIndex = caState.controlAdapters.indexOf(ca);
|
||||
const caCount = caState.controlAdapters.length;
|
||||
const selectValidActions = createAppSelector([selectCanvasV2Slice, (canvasV2, id: string) => id], (canvasV2, id) => {
|
||||
const ca = selectCAOrThrow(canvasV2, id);
|
||||
const caIndex = canvasV2.controlAdapters.indexOf(ca);
|
||||
const caCount = canvasV2.controlAdapters.length;
|
||||
return {
|
||||
canMoveForward: caIndex < caCount - 1,
|
||||
canMoveBackward: caIndex > 0,
|
||||
canMoveToFront: caIndex < caCount - 1,
|
||||
canMoveToBack: caIndex > 0,
|
||||
};
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
export const CAActionsMenu = memo(({ id }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
|
@ -6,7 +6,8 @@ import { CanvasEntityHeader } from 'features/controlLayers/components/common/Can
|
||||
import { CanvasEntityTitle } from 'features/controlLayers/components/common/CanvasEntityTitle';
|
||||
import { CAActionsMenu } from 'features/controlLayers/components/ControlAdapter/CAActionsMenu';
|
||||
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 { useTranslation } from 'react-i18next';
|
||||
|
||||
@ -18,7 +19,7 @@ type Props = {
|
||||
export const CAHeader = memo(({ id, onToggleVisibility }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const isEnabled = useAppSelector((s) => selectCAOrThrow(s.controlAdaptersV2, id).isEnabled);
|
||||
const isEnabled = useAppSelector((s) => selectCAOrThrow(s.canvasV2, id).isEnabled);
|
||||
const onToggleIsEnabled = useCallback(() => {
|
||||
dispatch(caIsEnabledToggled({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
@ -3,7 +3,7 @@ import { skipToken } from '@reduxjs/toolkit/query';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIDndImage from 'common/components/IAIDndImage';
|
||||
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 { ImageDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
||||
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
||||
|
@ -15,7 +15,7 @@ import {
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { stopPropagation } from 'common/util/stopPropagation';
|
||||
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 { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
@ -16,8 +16,8 @@ import {
|
||||
caProcessedImageChanged,
|
||||
caProcessorConfigChanged,
|
||||
caWeightChanged,
|
||||
selectCAOrThrow,
|
||||
} from 'features/controlLayers/store/controlAdaptersSlice';
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCAOrThrow } from 'features/controlLayers/store/controlAdaptersReducers';
|
||||
import type { ControlModeV2, ProcessorConfig } from 'features/controlLayers/store/types';
|
||||
import type { CAImageDropData } from 'features/dnd/types';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
@ -40,7 +40,7 @@ export const CASettings = memo(({ id }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const [isExpanded, toggleIsExpanded] = useToggle(false);
|
||||
|
||||
const controlAdapter = useAppSelector((s) => selectCAOrThrow(s.controlAdaptersV2, id));
|
||||
const controlAdapter = useAppSelector((s) => selectCAOrThrow(s.canvasV2, id));
|
||||
|
||||
const onChangeBeginEndStepPct = useCallback(
|
||||
(beginEndStepPct: [number, number]) => {
|
||||
|
@ -11,39 +11,22 @@ import { IPA } from 'features/controlLayers/components/IPAdapter/IPA';
|
||||
import { Layer } from 'features/controlLayers/components/Layer/Layer';
|
||||
import { RG } from 'features/controlLayers/components/RegionalGuidance/RG';
|
||||
import { mapId } from 'features/controlLayers/konva/util';
|
||||
import { selectControlAdaptersV2Slice } from 'features/controlLayers/store/controlAdaptersSlice';
|
||||
import { selectIPAdaptersSlice } from 'features/controlLayers/store/ipAdaptersSlice';
|
||||
import { selectLayersSlice } from 'features/controlLayers/store/layersSlice';
|
||||
import { selectRegionalGuidanceSlice } from 'features/controlLayers/store/regionalGuidanceSlice';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const selectRGIds = createMemoizedSelector(selectRegionalGuidanceSlice, (rgState) => {
|
||||
return rgState.regions.map(mapId).reverse();
|
||||
});
|
||||
|
||||
const selectCAIds = createMemoizedSelector(selectControlAdaptersV2Slice, (caState) => {
|
||||
return caState.controlAdapters.map(mapId).reverse();
|
||||
});
|
||||
|
||||
const selectIPAIds = createMemoizedSelector(selectIPAdaptersSlice, (ipaState) => {
|
||||
return ipaState.ipAdapters.map(mapId).reverse();
|
||||
});
|
||||
|
||||
const selectLayerIds = createMemoizedSelector(selectLayersSlice, (layersState) => {
|
||||
return layersState.layers.map(mapId).reverse();
|
||||
const selectEntityIds = createMemoizedSelector(selectCanvasV2Slice, (canvasV2State) => {
|
||||
const rgIds = canvasV2State.regions.map(mapId).reverse();
|
||||
const caIds = canvasV2State.controlAdapters.map(mapId).reverse();
|
||||
const ipaIds = canvasV2State.ipAdapters.map(mapId).reverse();
|
||||
const layerIds = canvasV2State.layers.map(mapId).reverse();
|
||||
const entityCount = rgIds.length + caIds.length + ipaIds.length + layerIds.length;
|
||||
return { rgIds, caIds, ipaIds, layerIds, entityCount };
|
||||
});
|
||||
|
||||
export const ControlLayersPanelContent = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const rgIds = useAppSelector(selectRGIds);
|
||||
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]
|
||||
);
|
||||
const { rgIds, caIds, ipaIds, layerIds, entityCount } = useAppSelector(selectEntityIds);
|
||||
|
||||
return (
|
||||
<Flex flexDir="column" gap={2} w="full" h="full">
|
||||
|
@ -1,10 +1,6 @@
|
||||
import { Button } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { caAllDeleted } from 'features/controlLayers/store/controlAdaptersSlice';
|
||||
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 { allEntitiesDeleted } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiTrashSimpleBold } from 'react-icons/pi';
|
||||
@ -12,12 +8,16 @@ import { PiTrashSimpleBold } from 'react-icons/pi';
|
||||
export const DeleteAllLayersButton = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
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(() => {
|
||||
dispatch(caAllDeleted());
|
||||
dispatch(rgAllDeleted());
|
||||
dispatch(ipaAllDeleted());
|
||||
dispatch(layerAllDeleted());
|
||||
dispatch(allEntitiesDeleted());
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
PopoverTrigger,
|
||||
} from '@invoke-ai/ui-library';
|
||||
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 { useTranslation } from 'react-i18next';
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { Flex, Popover, PopoverBody, PopoverContent, PopoverTrigger } from '@inv
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIColorPicker from 'common/components/IAIColorPicker';
|
||||
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 { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Box, Flex, Text } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
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 { memo } from 'react';
|
||||
|
||||
|
@ -3,7 +3,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
|
||||
import { IPAHeader } from 'features/controlLayers/components/IPAdapter/IPAHeader';
|
||||
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';
|
||||
|
||||
type Props = {
|
||||
|
@ -4,7 +4,8 @@ import { CanvasEntityDeleteButton } from 'features/controlLayers/components/comm
|
||||
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
|
||||
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
||||
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 { useTranslation } from 'react-i18next';
|
||||
|
||||
@ -16,7 +17,7 @@ type Props = {
|
||||
export const IPAHeader = memo(({ id, onToggleVisibility }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const isEnabled = useAppSelector((s) => selectIPAOrThrow(s.ipAdapters, id).isEnabled);
|
||||
const isEnabled = useAppSelector((s) => selectIPAOrThrow(s.canvasV2, id).isEnabled);
|
||||
const onToggleIsEnabled = useCallback(() => {
|
||||
dispatch(ipaIsEnabledToggled({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
@ -3,7 +3,7 @@ import { skipToken } from '@reduxjs/toolkit/query';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIDndImage from 'common/components/IAIDndImage';
|
||||
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 { ImageDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
||||
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
||||
|
@ -11,8 +11,8 @@ import {
|
||||
ipaMethodChanged,
|
||||
ipaModelChanged,
|
||||
ipaWeightChanged,
|
||||
selectIPAOrThrow,
|
||||
} from 'features/controlLayers/store/ipAdaptersSlice';
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectIPAOrThrow } from 'features/controlLayers/store/ipAdaptersReducers';
|
||||
import type { CLIPVisionModelV2, IPMethodV2 } from 'features/controlLayers/store/types';
|
||||
import type { IPAImageDropData } from 'features/dnd/types';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
@ -27,7 +27,7 @@ type Props = {
|
||||
|
||||
export const IPASettings = memo(({ id }: Props) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const ipAdapter = useAppSelector((s) => selectIPAOrThrow(s.ipAdapters, id));
|
||||
const ipAdapter = useAppSelector((s) => selectIPAOrThrow(s.canvasV2, id));
|
||||
|
||||
const onChangeBeginEndStepPct = useCallback(
|
||||
(beginEndStepPct: [number, number]) => {
|
||||
|
@ -3,7 +3,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
|
||||
import { LayerHeader } from 'features/controlLayers/components/Layer/LayerHeader';
|
||||
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';
|
||||
|
||||
type Props = {
|
||||
|
@ -8,9 +8,9 @@ import {
|
||||
layerMovedForwardOne,
|
||||
layerMovedToBack,
|
||||
layerMovedToFront,
|
||||
selectLayerOrThrow,
|
||||
selectLayersSlice,
|
||||
} from 'features/controlLayers/store/layersSlice';
|
||||
selectCanvasV2Slice,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectLayerOrThrow } from 'features/controlLayers/store/layersReducers';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
@ -25,20 +25,17 @@ type Props = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
const selectValidActions = createAppSelector(
|
||||
[selectLayersSlice, (layersState, id: string) => id],
|
||||
(layersState, id) => {
|
||||
const layer = selectLayerOrThrow(layersState, id);
|
||||
const layerIndex = layersState.layers.indexOf(layer);
|
||||
const layerCount = layersState.layers.length;
|
||||
const selectValidActions = createAppSelector([selectCanvasV2Slice, (canvasV2, id: string) => id], (canvasV2, id) => {
|
||||
const layer = selectLayerOrThrow(canvasV2, id);
|
||||
const layerIndex = canvasV2.layers.indexOf(layer);
|
||||
const layerCount = canvasV2.layers.length;
|
||||
return {
|
||||
canMoveForward: layerIndex < layerCount - 1,
|
||||
canMoveBackward: layerIndex > 0,
|
||||
canMoveToFront: layerIndex < layerCount - 1,
|
||||
canMoveToBack: layerIndex > 0,
|
||||
};
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
export const LayerActionsMenu = memo(({ id }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
|
@ -5,7 +5,8 @@ import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/com
|
||||
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
||||
import { CanvasEntityTitle } from 'features/controlLayers/components/common/CanvasEntityTitle';
|
||||
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 { useTranslation } from 'react-i18next';
|
||||
|
||||
@ -19,7 +20,7 @@ type Props = {
|
||||
export const LayerHeader = memo(({ id, onToggleVisibility }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const isEnabled = useAppSelector((s) => selectLayerOrThrow(s.layers, id).isEnabled);
|
||||
const isEnabled = useAppSelector((s) => selectLayerOrThrow(s.canvasV2, id).isEnabled);
|
||||
const onToggleIsEnabled = useCallback(() => {
|
||||
dispatch(layerIsEnabledToggled({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
@ -13,7 +13,8 @@ import {
|
||||
} from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
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 { useTranslation } from 'react-i18next';
|
||||
import { PiDropHalfFill } from 'react-icons/pi';
|
||||
@ -28,7 +29,7 @@ const formatPct = (v: number | string) => `${v} %`;
|
||||
export const LayerOpacity = memo(({ id }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
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(
|
||||
(v: number) => {
|
||||
dispatch(layerOpacityChanged({ id, opacity: v / 100 }));
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { CompositeNumberInput, CompositeSlider, Flex, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||
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 { useTranslation } from 'react-i18next';
|
||||
|
||||
@ -10,7 +10,7 @@ const formatPct = (v: number | string) => `${v} %`;
|
||||
export const RGGlobalOpacity = memo(() => {
|
||||
const dispatch = useAppDispatch();
|
||||
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(
|
||||
(v: number) => {
|
||||
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 { RGHeader } from 'features/controlLayers/components/RegionalGuidance/RGHeader';
|
||||
import { RGSettings } from 'features/controlLayers/components/RegionalGuidance/RGSettings';
|
||||
import { entitySelected } from 'features/controlLayers/store/controlLayersSlice';
|
||||
import { selectRGOrThrow } from 'features/controlLayers/store/regionalGuidanceSlice';
|
||||
import { entitySelected } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectRGOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||
import { memo, useCallback } from 'react';
|
||||
|
||||
type Props = {
|
||||
@ -14,7 +14,7 @@ type Props = {
|
||||
|
||||
export const RG = memo(({ id }: Props) => {
|
||||
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 { isOpen, onToggle } = useDisclosure({ defaultIsOpen: true });
|
||||
const onSelect = useCallback(() => {
|
||||
|
@ -12,9 +12,9 @@ import {
|
||||
rgNegativePromptChanged,
|
||||
rgPositivePromptChanged,
|
||||
rgReset,
|
||||
selectRegionalGuidanceSlice,
|
||||
selectRGOrThrow,
|
||||
} from 'features/controlLayers/store/regionalGuidanceSlice';
|
||||
selectCanvasV2Slice,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectRGOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
@ -32,11 +32,11 @@ type Props = {
|
||||
};
|
||||
|
||||
const selectActionsValidity = createMemoizedAppSelector(
|
||||
[selectRegionalGuidanceSlice, (rgState, id: string) => id],
|
||||
(rgState, id) => {
|
||||
const rg = selectRGOrThrow(rgState, id);
|
||||
const rgIndex = rgState.regions.indexOf(rg);
|
||||
const rgCount = rgState.regions.length;
|
||||
[selectCanvasV2Slice, (canvasV2, id: string) => id],
|
||||
(canvasV2, id) => {
|
||||
const rg = selectRGOrThrow(canvasV2, id);
|
||||
const rgIndex = canvasV2.regions.indexOf(rg);
|
||||
const rgCount = canvasV2.regions.length;
|
||||
return {
|
||||
isMoveForwardOneDisabled: rgIndex < rgCount - 1,
|
||||
isMoveBackardOneDisabled: rgIndex > 0,
|
||||
|
@ -5,7 +5,8 @@ import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/com
|
||||
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
||||
import { CanvasEntityTitle } from 'features/controlLayers/components/common/CanvasEntityTitle';
|
||||
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 { useTranslation } from 'react-i18next';
|
||||
|
||||
@ -20,8 +21,8 @@ type Props = {
|
||||
export const RGHeader = memo(({ id, onToggleVisibility }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const isEnabled = useAppSelector((s) => selectRGOrThrow(s.regionalGuidance, id).isEnabled);
|
||||
const autoNegative = useAppSelector((s) => selectRGOrThrow(s.regionalGuidance, id).autoNegative);
|
||||
const isEnabled = useAppSelector((s) => selectRGOrThrow(s.canvasV2, id).isEnabled);
|
||||
const autoNegative = useAppSelector((s) => selectRGOrThrow(s.canvasV2, id).autoNegative);
|
||||
const onToggleIsEnabled = useCallback(() => {
|
||||
dispatch(rgIsEnabledToggled({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
@ -13,8 +13,8 @@ import {
|
||||
rgIPAdapterMethodChanged,
|
||||
rgIPAdapterModelChanged,
|
||||
rgIPAdapterWeightChanged,
|
||||
selectRGOrThrow,
|
||||
} from 'features/controlLayers/store/regionalGuidanceSlice';
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectRGOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||
import type { CLIPVisionModelV2, IPMethodV2 } from 'features/controlLayers/store/types';
|
||||
import type { RGIPAdapterImageDropData } from 'features/dnd/types';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
@ -34,7 +34,7 @@ export const RGIPAdapterSettings = memo(({ id, ipAdapterId, ipAdapterNumber }: P
|
||||
dispatch(rgIPAdapterDeleted({ id, ipAdapterId }));
|
||||
}, [dispatch, ipAdapterId, id]);
|
||||
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`);
|
||||
return ipa;
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Divider, Flex } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
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';
|
||||
|
||||
type Props = {
|
||||
@ -9,7 +9,7 @@ type 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) {
|
||||
return null;
|
||||
|
@ -3,7 +3,8 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import RgbColorPicker from 'common/components/RgbColorPicker';
|
||||
import { stopPropagation } from 'common/util/stopPropagation';
|
||||
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 type { RgbColor } from 'react-colorful';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -15,7 +16,7 @@ type Props = {
|
||||
export const RGMaskFillColorPicker = memo(({ id }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const fill = useAppSelector((s) => selectRGOrThrow(s.regionalGuidance, id).fill);
|
||||
const fill = useAppSelector((s) => selectRGOrThrow(s.canvasV2, id).fill);
|
||||
const onChange = useCallback(
|
||||
(fill: RgbColor) => {
|
||||
dispatch(rgFillChanged({ id, fill }));
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { Box, Textarea } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
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 { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
|
||||
import { PromptPopover } from 'features/prompt/PromptPopover';
|
||||
@ -14,7 +15,7 @@ type 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 textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const { t } = useTranslation();
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { Box, Textarea } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
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 { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
|
||||
import { PromptPopover } from 'features/prompt/PromptPopover';
|
||||
@ -14,7 +15,7 @@ type 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 textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const { t } = useTranslation();
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { AddPromptButtons } from 'features/controlLayers/components/AddPromptButtons';
|
||||
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 { RGIPAdapters } from './RGIPAdapters';
|
||||
@ -13,9 +13,9 @@ type Props = {
|
||||
};
|
||||
|
||||
export const RGSettings = memo(({ id }: Props) => {
|
||||
const hasPositivePrompt = useAppSelector((s) => selectRGOrThrow(s.regionalGuidance, id).positivePrompt !== null);
|
||||
const hasNegativePrompt = useAppSelector((s) => selectRGOrThrow(s.regionalGuidance, id).negativePrompt !== null);
|
||||
const hasIPAdapters = useAppSelector((s) => selectRGOrThrow(s.regionalGuidance, id).ipAdapters.length > 0);
|
||||
const hasPositivePrompt = useAppSelector((s) => selectRGOrThrow(s.canvasV2, id).positivePrompt !== null);
|
||||
const hasNegativePrompt = useAppSelector((s) => selectRGOrThrow(s.canvasV2, id).negativePrompt !== null);
|
||||
const hasIPAdapters = useAppSelector((s) => selectRGOrThrow(s.canvasV2, id).ipAdapters.length > 0);
|
||||
|
||||
return (
|
||||
<CanvasEntitySettings>
|
||||
|
@ -12,7 +12,8 @@ import {
|
||||
} from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
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 { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -25,7 +26,7 @@ type Props = {
|
||||
export const RGSettingsPopover = memo(({ id }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const autoNegative = useAppSelector((s) => selectRGOrThrow(s.regionalGuidance, id).autoNegative);
|
||||
const autoNegative = useAppSelector((s) => selectRGOrThrow(s.canvasV2, id).autoNegative);
|
||||
const onChange = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => {
|
||||
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 { useStore } from '@nanostores/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { logger } from 'app/logging/logger';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { rgbaColorToString } from 'features/canvas/util/colorToString';
|
||||
import { HeadsUpDisplay } from 'features/controlLayers/components/HeadsUpDisplay';
|
||||
import { TRANSPARENCY_CHECKER_PATTERN } from 'features/controlLayers/konva/constants';
|
||||
import { setStageEventHandlers } from 'features/controlLayers/konva/events';
|
||||
import { debouncedRenderers, renderers as normalRenderers } from 'features/controlLayers/konva/renderers/layers';
|
||||
import { caBboxChanged, caTranslated } from 'features/controlLayers/store/controlAdaptersSlice';
|
||||
import {
|
||||
$bbox,
|
||||
$currentFill,
|
||||
@ -23,29 +20,26 @@ import {
|
||||
$toolState,
|
||||
bboxChanged,
|
||||
brushWidthChanged,
|
||||
caBboxChanged,
|
||||
caTranslated,
|
||||
eraserWidthChanged,
|
||||
selectCanvasV2Slice,
|
||||
toolBufferChanged,
|
||||
toolChanged,
|
||||
} from 'features/controlLayers/store/controlLayersSlice';
|
||||
import {
|
||||
layerBboxChanged,
|
||||
layerBrushLineAdded,
|
||||
layerEraserLineAdded,
|
||||
layerLinePointAdded,
|
||||
layerRectAdded,
|
||||
layerTranslated,
|
||||
selectLayersSlice,
|
||||
} from 'features/controlLayers/store/layersSlice';
|
||||
import {
|
||||
rgBboxChanged,
|
||||
rgBrushLineAdded,
|
||||
rgEraserLineAdded,
|
||||
rgLinePointAdded,
|
||||
rgRectAdded,
|
||||
rgTranslated,
|
||||
selectRegionalGuidanceSlice,
|
||||
} from 'features/controlLayers/store/regionalGuidanceSlice';
|
||||
selectCanvasV2Slice,
|
||||
toolBufferChanged,
|
||||
toolChanged,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectEntityCount } from 'features/controlLayers/store/selectors';
|
||||
import type {
|
||||
BboxChangedArg,
|
||||
BrushLineAddedArg,
|
||||
@ -69,62 +63,42 @@ Konva.showWarnings = false;
|
||||
|
||||
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 dispatch = useAppDispatch();
|
||||
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 lastMouseDownPos = useStore($lastMouseDownPos);
|
||||
const isMouseDown = useStore($isMouseDown);
|
||||
const isDrawing = useStore($isDrawing);
|
||||
const brushColor = useAppSelector(selectBrushFill);
|
||||
const selectedEntity = useMemo(() => {
|
||||
const identifier = canvasV2State.selectedEntityIdentifier;
|
||||
if (!identifier) {
|
||||
return null;
|
||||
} 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') {
|
||||
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') {
|
||||
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') {
|
||||
return regionalGuidanceState.regions.find((i) => i.id === identifier.id) ?? null;
|
||||
return canvasV2State.regions.find((i) => i.id === identifier.id) ?? null;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}, [
|
||||
canvasV2State.controlAdapters,
|
||||
canvasV2State.ipAdapters,
|
||||
canvasV2State.layers,
|
||||
canvasV2State.regions,
|
||||
canvasV2State.selectedEntityIdentifier,
|
||||
controlAdaptersState.controlAdapters,
|
||||
ipAdaptersState.ipAdapters,
|
||||
layersState.layers,
|
||||
regionalGuidanceState.regions,
|
||||
]);
|
||||
|
||||
const currentFill = useMemo(() => {
|
||||
if (selectedEntity && selectedEntity.type === 'regional_guidance') {
|
||||
return { ...selectedEntity.fill, a: regionalGuidanceState.opacity };
|
||||
return { ...selectedEntity.fill, a: canvasV2State.maskFillOpacity };
|
||||
}
|
||||
return canvasV2State.tool.fill;
|
||||
}, [canvasV2State.tool.fill, regionalGuidanceState.opacity, selectedEntity]);
|
||||
}, [canvasV2State.maskFillOpacity, canvasV2State.tool.fill, selectedEntity]);
|
||||
|
||||
const renderers = useMemo(() => (asPreview ? debouncedRenderers : normalRenderers), [asPreview]);
|
||||
const dpr = useDevicePixelRatio({ round: false });
|
||||
@ -341,7 +315,6 @@ const useStageRenderer = (stage: Konva.Stage, container: HTMLDivElement | null,
|
||||
);
|
||||
}, [
|
||||
asPreview,
|
||||
brushColor,
|
||||
canvasV2State.tool,
|
||||
currentFill,
|
||||
isDrawing,
|
||||
@ -376,10 +349,10 @@ const useStageRenderer = (stage: Konva.Stage, container: HTMLDivElement | null,
|
||||
log.trace('Rendering layers');
|
||||
renderers.renderLayers(
|
||||
stage,
|
||||
layersState.layers,
|
||||
controlAdaptersState.controlAdapters,
|
||||
regionalGuidanceState.regions,
|
||||
regionalGuidanceState.opacity,
|
||||
canvasV2State.layers,
|
||||
canvasV2State.controlAdapters,
|
||||
canvasV2State.regions,
|
||||
canvasV2State.maskFillOpacity,
|
||||
canvasV2State.tool.selected,
|
||||
selectedEntity,
|
||||
getImageDTO,
|
||||
@ -388,13 +361,13 @@ const useStageRenderer = (stage: Konva.Stage, container: HTMLDivElement | null,
|
||||
}, [
|
||||
stage,
|
||||
renderers,
|
||||
layersState.layers,
|
||||
controlAdaptersState.controlAdapters,
|
||||
regionalGuidanceState.regions,
|
||||
regionalGuidanceState.opacity,
|
||||
onPosChanged,
|
||||
canvasV2State.tool.selected,
|
||||
selectedEntity,
|
||||
canvasV2State.layers,
|
||||
canvasV2State.controlAdapters,
|
||||
canvasV2State.regions,
|
||||
canvasV2State.maskFillOpacity,
|
||||
]);
|
||||
|
||||
// useLayoutEffect(() => {
|
||||
@ -450,7 +423,7 @@ export const StageComponent = memo(({ asPreview = false }: Props) => {
|
||||
backgroundRepeat="repeat"
|
||||
opacity={0.2}
|
||||
/>
|
||||
{!asPreview && <NoLayersFallback />}
|
||||
{!asPreview && <NoEntitiesFallback />}
|
||||
<Flex
|
||||
position="absolute"
|
||||
top={0}
|
||||
@ -474,10 +447,11 @@ export const StageComponent = memo(({ asPreview = false }: Props) => {
|
||||
|
||||
StageComponent.displayName = 'StageComponent';
|
||||
|
||||
const NoLayersFallback = () => {
|
||||
const NoEntitiesFallback = () => {
|
||||
const { t } = useTranslation();
|
||||
const layerCount = useAppSelector((s) => s.layers.layers.length);
|
||||
if (layerCount) {
|
||||
const entityCount = useAppSelector(selectEntityCount);
|
||||
|
||||
if (entityCount) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,16 @@
|
||||
import { ButtonGroup, IconButton } from '@invoke-ai/ui-library';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { caDeleted } from 'features/controlLayers/store/controlAdaptersSlice';
|
||||
import { selectCanvasV2Slice, toolChanged } from 'features/controlLayers/store/controlLayersSlice';
|
||||
import { ipaDeleted } from 'features/controlLayers/store/ipAdaptersSlice';
|
||||
import { layerDeleted, layerReset } from 'features/controlLayers/store/layersSlice';
|
||||
import { rgDeleted, rgReset } from 'features/controlLayers/store/regionalGuidanceSlice';
|
||||
import {
|
||||
caDeleted,
|
||||
ipaDeleted,
|
||||
layerDeleted,
|
||||
layerReset,
|
||||
rgDeleted,
|
||||
rgReset,
|
||||
selectCanvasV2Slice,
|
||||
toolChanged,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* eslint-disable i18next/no-literal-string */
|
||||
import { ButtonGroup, IconButton } from '@invoke-ai/ui-library';
|
||||
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 { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { caAdded } from 'features/controlLayers/store/controlAdaptersSlice';
|
||||
import { ipaAdded } from 'features/controlLayers/store/ipAdaptersSlice';
|
||||
import { rgIPAdapterAdded } from 'features/controlLayers/store/regionalGuidanceSlice';
|
||||
import { caAdded, ipaAdded, rgIPAdapterAdded } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import {
|
||||
buildControlNet,
|
||||
buildIPAdapter,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
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 { useMemo } from 'react';
|
||||
import { assert } from 'tsafe';
|
||||
|
@ -3,6 +3,10 @@ import { createSlice } from '@reduxjs/toolkit';
|
||||
import type { PersistConfig, RootState } from 'app/store/store';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
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 { initialAspectRatioState } from 'features/parameters/components/ImageSize/constants';
|
||||
import type { AspectRatioState } from 'features/parameters/components/ImageSize/types';
|
||||
@ -47,12 +51,21 @@ const initialState: CanvasV2State = {
|
||||
width: 512,
|
||||
height: 512,
|
||||
},
|
||||
controlAdapters: [],
|
||||
ipAdapters: [],
|
||||
regions: [],
|
||||
layers: [],
|
||||
maskFillOpacity: 0.3,
|
||||
};
|
||||
|
||||
export const canvasV2Slice = createSlice({
|
||||
name: 'canvasV2',
|
||||
initialState,
|
||||
reducers: {
|
||||
...layersReducers,
|
||||
...ipAdaptersReducers,
|
||||
...controlAdaptersReducers,
|
||||
...regionsReducers,
|
||||
positivePromptChanged: (state, action: PayloadAction<string>) => {
|
||||
state.prompts.positivePrompt = action.payload;
|
||||
},
|
||||
@ -110,9 +123,18 @@ export const canvasV2Slice = createSlice({
|
||||
toolBufferChanged: (state, action: PayloadAction<Tool | null>) => {
|
||||
state.tool.selectedBuffer = action.payload;
|
||||
},
|
||||
maskFillOpacityChanged: (state, action: PayloadAction<number>) => {
|
||||
state.maskFillOpacity = action.payload;
|
||||
},
|
||||
entitySelected: (state, action: PayloadAction<CanvasEntityIdentifier>) => {
|
||||
state.selectedEntityIdentifier = action.payload;
|
||||
},
|
||||
allEntitiesDeleted: (state) => {
|
||||
state.regions = [];
|
||||
state.layers = [];
|
||||
state.ipAdapters = [];
|
||||
state.controlAdapters = [];
|
||||
},
|
||||
},
|
||||
extraReducers(builder) {
|
||||
builder.addCase(modelChanged, (state, action) => {
|
||||
@ -148,7 +170,88 @@ export const {
|
||||
invertScrollChanged,
|
||||
toolChanged,
|
||||
toolBufferChanged,
|
||||
maskFillOpacityChanged,
|
||||
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;
|
||||
|
||||
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;
|
||||
};
|
||||
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 };
|
||||
|
@ -3,7 +3,7 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
|
||||
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 { getImageUsage, selectImageUsage } from 'features/deleteImageModal/store/selectors';
|
||||
import {
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import type { ControlAdaptersState } 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 {
|
||||
isControlAdapterLayer,
|
||||
|
@ -15,7 +15,7 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
|
||||
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 { getImageUsage } from 'features/deleteImageModal/store/selectors';
|
||||
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 { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
||||
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 { useImageActions } from 'features/gallery/hooks/useImageActions';
|
||||
import { sentImageToCanvas, sentImageToImg2Img } from 'features/gallery/store/actions';
|
||||
|
@ -4,7 +4,7 @@ import { createSelector } from '@reduxjs/toolkit';
|
||||
import { skipToken } from '@reduxjs/toolkit/query';
|
||||
import { adHocPostProcessingRequested } from 'app/store/middleware/listenerMiddleware/listeners/addAdHocPostProcessingRequestedListener';
|
||||
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 { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice';
|
||||
import SingleSelectionMenuItems from 'features/gallery/components/ImageContextMenu/SingleSelectionMenuItems';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { getStore } from 'app/store/nanostores/store';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
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 { LoRA } from 'features/lora/store/loraSlice';
|
||||
import type {
|
||||
|
@ -19,7 +19,7 @@ import {
|
||||
positivePromptChanged,
|
||||
regionalGuidanceRecalled,
|
||||
widthChanged,
|
||||
} from 'features/controlLayers/store/controlLayersSlice';
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import type { LayerData } from 'features/controlLayers/store/types';
|
||||
import { setHrfEnabled, setHrfMethod, setHrfStrength } from 'features/hrf/store/hrfSlice';
|
||||
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 { RG_LAYER_NAME } from 'features/controlLayers/konva/naming';
|
||||
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 {
|
||||
isControlAdapterLayer,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Box, Textarea } from '@invoke-ai/ui-library';
|
||||
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 { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
||||
import { ViewModePrompt } from 'features/parameters/components/Prompts/ViewModePrompt';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Box, Textarea } from '@invoke-ai/ui-library';
|
||||
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 { PromptLabel } from 'features/parameters/components/Prompts/PromptLabel';
|
||||
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Flex } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
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 { memo } from 'react';
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { skipToken } from '@reduxjs/toolkit/query';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
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 { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||
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 { useAppSelector } from 'app/store/storeHooks';
|
||||
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 { getShouldProcessPrompt } from 'features/dynamicPrompts/util/getShouldProcessPrompt';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Box, Textarea } from '@invoke-ai/ui-library';
|
||||
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 { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
||||
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Box, Textarea } from '@invoke-ai/ui-library';
|
||||
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 { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
||||
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { IconButton, Tooltip } from '@invoke-ai/ui-library';
|
||||
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 { useTranslation } from 'react-i18next';
|
||||
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 { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
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 { selectHrfSlice } from 'features/hrf/store/hrfSlice';
|
||||
import ParamScaleBeforeProcessing from 'features/parameters/components/Canvas/InfillAndScaling/ParamScaleBeforeProcessing';
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 { ParamWidth } from 'features/parameters/components/Core/ParamWidth';
|
||||
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 { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
|
||||
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 { Prompts } from 'features/parameters/components/Prompts/Prompts';
|
||||
import QueueControls from 'features/queue/components/QueueControls';
|
||||
|
Loading…
Reference in New Issue
Block a user