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