mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): rough out undo/redo on canvas
This commit is contained in:
parent
89c79276f3
commit
f126a61f66
@ -4,7 +4,8 @@ import {
|
||||
sessionStagingAreaImageAccepted,
|
||||
sessionStagingAreaReset,
|
||||
} from 'features/controlLayers/store/canvasSessionSlice';
|
||||
import { rasterLayerAdded } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { rasterLayerAdded } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import type { CanvasRasterLayerState } from 'features/controlLayers/store/types';
|
||||
import { imageDTOToImageObject } from 'features/controlLayers/store/types';
|
||||
import { toast } from 'features/toast/toast';
|
||||
@ -58,7 +59,7 @@ export const addStagingListeners = (startAppListening: AppStartListening) => {
|
||||
const stagingAreaImage = state.canvasSession.stagedImages[index];
|
||||
|
||||
assert(stagingAreaImage, 'No staged image found to accept');
|
||||
const { x, y } = state.canvasV2.bbox.rect;
|
||||
const { x, y } = selectCanvasSlice(state).bbox.rect;
|
||||
|
||||
const { imageDTO, offsetX, offsetY } = stagingAreaImage;
|
||||
const imageObject = imageDTOToImageObject(imageDTO);
|
||||
|
@ -1,6 +1,7 @@
|
||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import { getImageUsage } from 'features/deleteImageModal/store/selectors';
|
||||
import { nodeEditorReset } from 'features/nodes/store/nodesSlice';
|
||||
import { nodeEditorReset, selectNodesSlice } from 'features/nodes/store/nodesSlice';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
|
||||
export const addDeleteBoardAndImagesFulfilledListener = (startAppListening: AppStartListening) => {
|
||||
@ -13,10 +14,12 @@ export const addDeleteBoardAndImagesFulfilledListener = (startAppListening: AppS
|
||||
|
||||
let wasNodeEditorReset = false;
|
||||
|
||||
const { nodes, canvasV2 } = getState();
|
||||
const state = getState();
|
||||
const nodes = selectNodesSlice(state);
|
||||
const canvas = selectCanvasSlice(state);
|
||||
|
||||
deleted_images.forEach((image_name) => {
|
||||
const imageUsage = getImageUsage(nodes.present, canvasV2, image_name);
|
||||
const imageUsage = getImageUsage(nodes, canvas, image_name);
|
||||
|
||||
if (imageUsage.isNodesImage && !wasNodeEditorReset) {
|
||||
dispatch(nodeEditorReset());
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||
import type { AppDispatch, RootState } from 'app/store/store';
|
||||
import { entityDeleted, ipaImageChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { entityDeleted, ipaImageChanged } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import { getEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { imageDeletionConfirmed } from 'features/deleteImageModal/store/actions';
|
||||
import { isModalOpenChanged } from 'features/deleteImageModal/store/slice';
|
||||
@ -40,7 +41,7 @@ const deleteNodesImages = (state: RootState, dispatch: AppDispatch, imageDTO: Im
|
||||
};
|
||||
|
||||
// const deleteControlAdapterImages = (state: RootState, dispatch: AppDispatch, imageDTO: ImageDTO) => {
|
||||
// state.canvasV2.controlAdapters.entities.forEach(({ id, imageObject, processedImageObject }) => {
|
||||
// state.canvas.present.controlAdapters.entities.forEach(({ id, imageObject, processedImageObject }) => {
|
||||
// if (
|
||||
// imageObject?.image.image_name === imageDTO.image_name ||
|
||||
// processedImageObject?.image.image_name === imageDTO.image_name
|
||||
@ -52,7 +53,7 @@ const deleteNodesImages = (state: RootState, dispatch: AppDispatch, imageDTO: Im
|
||||
// };
|
||||
|
||||
const deleteIPAdapterImages = (state: RootState, dispatch: AppDispatch, imageDTO: ImageDTO) => {
|
||||
state.canvasV2.ipAdapters.entities.forEach((entity) => {
|
||||
selectCanvasSlice(state).ipAdapters.entities.forEach((entity) => {
|
||||
if (entity.ipAdapter.image?.image_name === imageDTO.image_name) {
|
||||
dispatch(ipaImageChanged({ entityIdentifier: getEntityIdentifier(entity), imageDTO: null }));
|
||||
}
|
||||
@ -60,7 +61,7 @@ const deleteIPAdapterImages = (state: RootState, dispatch: AppDispatch, imageDTO
|
||||
};
|
||||
|
||||
const deleteLayerImages = (state: RootState, dispatch: AppDispatch, imageDTO: ImageDTO) => {
|
||||
state.canvasV2.rasterLayers.entities.forEach(({ id, objects }) => {
|
||||
selectCanvasSlice(state).rasterLayers.entities.forEach(({ id, objects }) => {
|
||||
let shouldDelete = false;
|
||||
for (const obj of objects) {
|
||||
if (obj.type === 'image' && obj.image.image_name === imageDTO.image_name) {
|
||||
|
@ -6,7 +6,8 @@ import {
|
||||
ipaImageChanged,
|
||||
rasterLayerAdded,
|
||||
rgIPAdapterImageChanged,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
} from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import type { CanvasControlLayerState, CanvasRasterLayerState } from 'features/controlLayers/store/types';
|
||||
import { imageDTOToImageObject } from 'features/controlLayers/store/types';
|
||||
import type { TypesafeDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
||||
@ -85,7 +86,7 @@ export const addImageDroppedListener = (startAppListening: AppStartListening) =>
|
||||
activeData.payload.imageDTO
|
||||
) {
|
||||
const imageObject = imageDTOToImageObject(activeData.payload.imageDTO);
|
||||
const { x, y } = getState().canvasV2.bbox.rect;
|
||||
const { x, y } = selectCanvasSlice(getState()).bbox.rect;
|
||||
const overrides: Partial<CanvasRasterLayerState> = {
|
||||
objects: [imageObject],
|
||||
position: { x, y },
|
||||
@ -103,7 +104,7 @@ export const addImageDroppedListener = (startAppListening: AppStartListening) =>
|
||||
activeData.payload.imageDTO
|
||||
) {
|
||||
const imageObject = imageDTOToImageObject(activeData.payload.imageDTO);
|
||||
const { x, y } = getState().canvasV2.bbox.rect;
|
||||
const { x, y } = selectCanvasSlice(getState()).bbox.rect;
|
||||
const overrides: Partial<CanvasControlLayerState> = {
|
||||
objects: [imageObject],
|
||||
position: { x, y },
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||
import { ipaImageChanged, rgIPAdapterImageChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { ipaImageChanged, rgIPAdapterImageChanged } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectListBoardsQueryArgs } from 'features/gallery/store/gallerySelectors';
|
||||
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
|
||||
import { upscaleInitialImageChanged } from 'features/parameters/store/upscaleSlice';
|
||||
|
@ -46,7 +46,7 @@ export const addModelSelectedListener = (startAppListening: AppStartListening) =
|
||||
}
|
||||
|
||||
// handle incompatible controlnets
|
||||
// state.canvasV2.controlAdapters.entities.forEach((ca) => {
|
||||
// state.canvas.present.controlAdapters.entities.forEach((ca) => {
|
||||
// if (ca.model?.base !== newBaseModel) {
|
||||
// modelsCleared += 1;
|
||||
// if (ca.isEnabled) {
|
||||
|
@ -8,9 +8,10 @@ import {
|
||||
controlLayerModelChanged,
|
||||
ipaModelChanged,
|
||||
rgIPAdapterModelChanged,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
} from 'features/controlLayers/store/canvasSlice';
|
||||
import { loraDeleted } from 'features/controlLayers/store/lorasSlice';
|
||||
import { modelChanged, refinerModelChanged, vaeSelected } from 'features/controlLayers/store/paramsSlice';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import { getEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { calculateNewSize } from 'features/parameters/components/DocumentSize/calculateNewSize';
|
||||
import { postProcessingModelChanged, upscaleModelChanged } from 'features/parameters/store/upscaleSlice';
|
||||
@ -81,15 +82,12 @@ const handleMainModels: ModelHandler = (models, state, dispatch, log) => {
|
||||
const result = zParameterModel.safeParse(defaultModelInList);
|
||||
if (result.success) {
|
||||
dispatch(modelChanged({ model: defaultModelInList, previousModel: currentModel }));
|
||||
|
||||
const { bbox } = selectCanvasSlice(state);
|
||||
const optimalDimension = getOptimalDimension(defaultModelInList);
|
||||
if (getIsSizeOptimal(state.canvasV2.bbox.rect.width, state.canvasV2.bbox.rect.height, optimalDimension)) {
|
||||
if (getIsSizeOptimal(bbox.rect.width, bbox.rect.height, optimalDimension)) {
|
||||
return;
|
||||
}
|
||||
const { width, height } = calculateNewSize(
|
||||
state.canvasV2.bbox.aspectRatio.value,
|
||||
optimalDimension * optimalDimension
|
||||
);
|
||||
const { width, height } = calculateNewSize(bbox.aspectRatio.value, optimalDimension * optimalDimension);
|
||||
|
||||
dispatch(bboxWidthChanged({ width }));
|
||||
dispatch(bboxHeightChanged({ height }));
|
||||
@ -172,7 +170,7 @@ const handleLoRAModels: ModelHandler = (models, state, dispatch, _log) => {
|
||||
|
||||
const handleControlAdapterModels: ModelHandler = (models, state, dispatch, _log) => {
|
||||
const caModels = models.filter(isControlNetOrT2IAdapterModelConfig);
|
||||
state.canvasV2.controlLayers.entities.forEach((entity) => {
|
||||
selectCanvasSlice(state).controlLayers.entities.forEach((entity) => {
|
||||
const isModelAvailable = caModels.some((m) => m.key === entity.controlAdapter.model?.key);
|
||||
if (isModelAvailable) {
|
||||
return;
|
||||
@ -183,7 +181,7 @@ const handleControlAdapterModels: ModelHandler = (models, state, dispatch, _log)
|
||||
|
||||
const handleIPAdapterModels: ModelHandler = (models, state, dispatch, _log) => {
|
||||
const ipaModels = models.filter(isIPAdapterModelConfig);
|
||||
state.canvasV2.ipAdapters.entities.forEach((entity) => {
|
||||
selectCanvasSlice(state).ipAdapters.entities.forEach((entity) => {
|
||||
const isModelAvailable = ipaModels.some((m) => m.key === entity.ipAdapter.model?.key);
|
||||
if (isModelAvailable) {
|
||||
return;
|
||||
@ -191,7 +189,7 @@ const handleIPAdapterModels: ModelHandler = (models, state, dispatch, _log) => {
|
||||
dispatch(ipaModelChanged({ entityIdentifier: getEntityIdentifier(entity), modelConfig: null }));
|
||||
});
|
||||
|
||||
state.canvasV2.regions.entities.forEach((entity) => {
|
||||
selectCanvasSlice(state).regions.entities.forEach((entity) => {
|
||||
entity.ipAdapters.forEach(({ id: ipAdapterId, model }) => {
|
||||
const isModelAvailable = ipaModels.some((m) => m.key === model?.key);
|
||||
if (isModelAvailable) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||
import { bboxHeightChanged, bboxWidthChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { bboxHeightChanged, bboxWidthChanged } from 'features/controlLayers/store/canvasSlice';
|
||||
import {
|
||||
setCfgRescaleMultiplier,
|
||||
setCfgScale,
|
||||
|
@ -8,7 +8,7 @@ import { deepClone } from 'common/util/deepClone';
|
||||
import { changeBoardModalSlice } from 'features/changeBoardModal/store/slice';
|
||||
import { canvasSessionPersistConfig, canvasSessionSlice } from 'features/controlLayers/store/canvasSessionSlice';
|
||||
import { canvasSettingsPersistConfig, canvasSettingsSlice } from 'features/controlLayers/store/canvasSettingsSlice';
|
||||
import { canvasV2PersistConfig, canvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { canvasPersistConfig, canvasSlice } from 'features/controlLayers/store/canvasSlice';
|
||||
import { lorasPersistConfig, lorasSlice } from 'features/controlLayers/store/lorasSlice';
|
||||
import { paramsPersistConfig, paramsSlice } from 'features/controlLayers/store/paramsSlice';
|
||||
import { toolPersistConfig, toolSlice } from 'features/controlLayers/store/toolSlice';
|
||||
@ -58,7 +58,7 @@ const allReducers = {
|
||||
[queueSlice.name]: queueSlice.reducer,
|
||||
[workflowSlice.name]: workflowSlice.reducer,
|
||||
[hrfSlice.name]: hrfSlice.reducer,
|
||||
[canvasV2Slice.name]: canvasV2Slice.reducer,
|
||||
[canvasSlice.name]: undoable(canvasSlice.reducer),
|
||||
[workflowSettingsSlice.name]: workflowSettingsSlice.reducer,
|
||||
[upscaleSlice.name]: upscaleSlice.reducer,
|
||||
[stylePresetSlice.name]: stylePresetSlice.reducer,
|
||||
@ -104,7 +104,7 @@ const persistConfigs: { [key in keyof typeof allReducers]?: PersistConfig } = {
|
||||
[dynamicPromptsPersistConfig.name]: dynamicPromptsPersistConfig,
|
||||
[modelManagerV2PersistConfig.name]: modelManagerV2PersistConfig,
|
||||
[hrfPersistConfig.name]: hrfPersistConfig,
|
||||
[canvasV2PersistConfig.name]: canvasV2PersistConfig,
|
||||
[canvasPersistConfig.name]: canvasPersistConfig,
|
||||
[workflowSettingsPersistConfig.name]: workflowSettingsPersistConfig,
|
||||
[upscalePersistConfig.name]: upscalePersistConfig,
|
||||
[stylePresetPersistConfig.name]: stylePresetPersistConfig,
|
||||
|
@ -3,7 +3,7 @@ import { $isConnected } from 'app/hooks/useSocketIO';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectParamsSlice } from 'features/controlLayers/store/paramsSlice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
|
||||
import { getShouldProcessPrompt } from 'features/dynamicPrompts/util/getShouldProcessPrompt';
|
||||
import { $templates, selectNodesSlice } from 'features/nodes/store/nodesSlice';
|
||||
@ -34,14 +34,14 @@ const createSelector = (templates: Templates, isConnected: boolean) =>
|
||||
selectNodesSlice,
|
||||
selectWorkflowSettingsSlice,
|
||||
selectDynamicPromptsSlice,
|
||||
selectCanvasV2Slice,
|
||||
selectCanvasSlice,
|
||||
selectParamsSlice,
|
||||
selectUpscalelice,
|
||||
selectConfigSlice,
|
||||
selectActiveTab,
|
||||
],
|
||||
(system, nodes, workflowSettings, dynamicPrompts, canvasV2, params, upscale, config, activeTabName) => {
|
||||
const { bbox } = canvasV2;
|
||||
(system, nodes, workflowSettings, dynamicPrompts, canvas, params, upscale, config, activeTabName) => {
|
||||
const { bbox } = canvas;
|
||||
const { model, positivePrompt } = params;
|
||||
|
||||
const reasons: { prefix?: string; content: string }[] = [];
|
||||
@ -124,7 +124,7 @@ const createSelector = (templates: Templates, isConnected: boolean) =>
|
||||
reasons.push({ content: i18n.t('parameters.invoke.noModelSelected') });
|
||||
}
|
||||
|
||||
canvasV2.controlLayers.entities
|
||||
canvas.controlLayers.entities
|
||||
.filter((controlLayer) => controlLayer.isEnabled)
|
||||
.forEach((controlLayer, i) => {
|
||||
const layerLiteral = i18n.t('controlLayers.layers_one');
|
||||
@ -154,7 +154,7 @@ const createSelector = (templates: Templates, isConnected: boolean) =>
|
||||
}
|
||||
});
|
||||
|
||||
canvasV2.ipAdapters.entities
|
||||
canvas.ipAdapters.entities
|
||||
.filter((entity) => entity.isEnabled)
|
||||
.forEach((entity, i) => {
|
||||
const layerLiteral = i18n.t('controlLayers.layers_one');
|
||||
@ -182,7 +182,7 @@ const createSelector = (templates: Templates, isConnected: boolean) =>
|
||||
}
|
||||
});
|
||||
|
||||
canvasV2.regions.entities
|
||||
canvas.regions.entities
|
||||
.filter((entity) => entity.isEnabled)
|
||||
.forEach((entity, i) => {
|
||||
const layerLiteral = i18n.t('controlLayers.layers_one');
|
||||
@ -219,7 +219,7 @@ const createSelector = (templates: Templates, isConnected: boolean) =>
|
||||
}
|
||||
});
|
||||
|
||||
canvasV2.rasterLayers.entities
|
||||
canvas.rasterLayers.entities
|
||||
.filter((entity) => entity.isEnabled)
|
||||
.forEach((entity, i) => {
|
||||
const layerLiteral = i18n.t('controlLayers.layers_one');
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
ipaAdded,
|
||||
rasterLayerAdded,
|
||||
rgAdded,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
} from 'features/controlLayers/store/canvasSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiPlusBold } from 'react-icons/pi';
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
ipaAdded,
|
||||
rasterLayerAdded,
|
||||
rgAdded,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
} from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectEntityCount } from 'features/controlLayers/store/selectors';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Badge } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { selectCanvasSlice, selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@ -9,7 +9,7 @@ export const ControlLayerBadges = memo(() => {
|
||||
const entityIdentifier = useEntityIdentifierContext('control_layer');
|
||||
const { t } = useTranslation();
|
||||
const withTransparencyEffect = useAppSelector(
|
||||
(s) => selectEntityOrThrow(s.canvasV2, entityIdentifier).withTransparencyEffect
|
||||
(s) => selectEntityOrThrow(selectCanvasSlice(s), entityIdentifier).withTransparencyEffect
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
controlLayerControlModeChanged,
|
||||
controlLayerModelChanged,
|
||||
controlLayerWeightChanged,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
} from 'features/controlLayers/store/canvasSlice';
|
||||
import type { ControlModeV2 } from 'features/controlLayers/store/types';
|
||||
import { memo, useCallback } from 'react';
|
||||
import type { ControlNetModelConfig, T2IAdapterModelConfig } from 'services/api/types';
|
||||
|
@ -3,15 +3,15 @@ import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasEntityGroupList } from 'features/controlLayers/components/common/CanvasEntityGroupList';
|
||||
import { ControlLayer } from 'features/controlLayers/components/ControlLayer/ControlLayer';
|
||||
import { mapId } from 'features/controlLayers/konva/util';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { selectCanvasSlice, selectSelectedEntityIdentifier } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
|
||||
const selectEntityIds = createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
return canvasV2.controlLayers.entities.map(mapId).reverse();
|
||||
const selectEntityIds = createMemoizedSelector(selectCanvasSlice, (canvas) => {
|
||||
return canvas.controlLayers.entities.map(mapId).reverse();
|
||||
});
|
||||
|
||||
export const ControlLayerEntityList = memo(() => {
|
||||
const isSelected = useAppSelector((s) => Boolean(s.canvasV2.selectedEntityIdentifier?.type === 'control_layer'));
|
||||
const isSelected = useAppSelector((s) => selectSelectedEntityIdentifier(s)?.type === 'control_layer');
|
||||
const layerIds = useAppSelector(selectEntityIds);
|
||||
|
||||
if (layerIds.length === 0) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { MenuItem } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { controlLayerConvertedToRasterLayer } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { controlLayerConvertedToRasterLayer } from 'features/controlLayers/store/canvasSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiLightningBold } from 'react-icons/pi';
|
||||
|
@ -2,8 +2,8 @@ import { MenuItem } from '@invoke-ai/ui-library';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { controlLayerWithTransparencyEffectToggled } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice, selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { controlLayerWithTransparencyEffectToggled } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectCanvasSlice, selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiDropHalfBold } from 'react-icons/pi';
|
||||
@ -14,8 +14,8 @@ export const ControlLayerMenuItemsTransparencyEffect = memo(() => {
|
||||
const entityIdentifier = useEntityIdentifierContext('control_layer');
|
||||
const selectWithTransparencyEffect = useMemo(
|
||||
() =>
|
||||
createSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
const entity = selectEntityOrThrow(canvasV2, entityIdentifier);
|
||||
createSelector(selectCanvasSlice, (canvas) => {
|
||||
const entity = selectEntityOrThrow(canvas, entityIdentifier);
|
||||
return entity.withTransparencyEffect;
|
||||
}),
|
||||
[entityIdentifier]
|
||||
|
@ -1,10 +1,14 @@
|
||||
import { Box, Flex, Text } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import { round } from 'lodash-es';
|
||||
import { memo } from 'react';
|
||||
|
||||
const selectBbox = createSelector(selectCanvasSlice, (canvas) => canvas.bbox);
|
||||
|
||||
export const HeadsUpDisplay = memo(() => {
|
||||
const canvasManager = useCanvasManager();
|
||||
const stageAttrs = useStore(canvasManager.stateApi.$stageAttrs);
|
||||
@ -13,7 +17,7 @@ export const HeadsUpDisplay = memo(() => {
|
||||
const isMouseDown = useStore(canvasManager.stateApi.$isMouseDown);
|
||||
const lastMouseDownPos = useStore(canvasManager.stateApi.$lastMouseDownPos);
|
||||
const lastAddedPoint = useStore(canvasManager.stateApi.$lastAddedPoint);
|
||||
const bbox = useAppSelector((s) => s.canvasV2.bbox);
|
||||
const bbox = useAppSelector(selectBbox);
|
||||
|
||||
return (
|
||||
<Flex flexDir="column" bg="blackAlpha.400" borderBottomEndRadius="base" p={2} minW={64} gap={2}>
|
||||
|
@ -5,7 +5,7 @@ import { $isConnected } from 'app/hooks/useSocketIO';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIDndImage from 'common/components/IAIDndImage';
|
||||
import IAIDndImageIcon from 'common/components/IAIDndImageIcon';
|
||||
import { bboxHeightChanged, bboxWidthChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { bboxHeightChanged, bboxWidthChanged } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectOptimalDimension } from 'features/controlLayers/store/selectors';
|
||||
import type { ImageWithDims } from 'features/controlLayers/store/types';
|
||||
import type { ImageDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
||||
|
@ -1,18 +1,22 @@
|
||||
/* eslint-disable i18next/no-literal-string */
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasEntityGroupList } from 'features/controlLayers/components/common/CanvasEntityGroupList';
|
||||
import { IPAdapter } from 'features/controlLayers/components/IPAdapter/IPAdapter';
|
||||
import { mapId } from 'features/controlLayers/konva/util';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { selectCanvasSlice, selectSelectedEntityIdentifier } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
|
||||
const selectEntityIds = createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
return canvasV2.ipAdapters.entities.map(mapId).reverse();
|
||||
const selectEntityIds = createMemoizedSelector(selectCanvasSlice, (canvas) => {
|
||||
return canvas.ipAdapters.entities.map(mapId).reverse();
|
||||
});
|
||||
const selectIsSelected = createSelector(selectSelectedEntityIdentifier, (selectedEntityIdentifier) => {
|
||||
return selectedEntityIdentifier?.type === 'ip_adapter';
|
||||
});
|
||||
|
||||
export const IPAdapterList = memo(() => {
|
||||
const isSelected = useAppSelector((s) => Boolean(s.canvasV2.selectedEntityIdentifier?.type === 'ip_adapter'));
|
||||
const isSelected = useAppSelector(selectIsSelected);
|
||||
const ipaIds = useAppSelector(selectEntityIds);
|
||||
|
||||
if (ipaIds.length === 0) {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Box, Flex } from '@invoke-ai/ui-library';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { BeginEndStepPct } from 'features/controlLayers/components/common/BeginEndStepPct';
|
||||
import { CanvasEntitySettingsWrapper } from 'features/controlLayers/components/common/CanvasEntitySettingsWrapper';
|
||||
@ -12,8 +13,8 @@ import {
|
||||
ipaMethodChanged,
|
||||
ipaModelChanged,
|
||||
ipaWeightChanged,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
} from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectCanvasSlice, selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import type { CLIPVisionModelV2, IPMethodV2 } from 'features/controlLayers/store/types';
|
||||
import type { IPAImageDropData } from 'features/dnd/types';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
@ -25,7 +26,11 @@ import { IPAdapterModel } from './IPAdapterModel';
|
||||
export const IPAdapterSettings = memo(() => {
|
||||
const dispatch = useAppDispatch();
|
||||
const entityIdentifier = useEntityIdentifierContext('ip_adapter');
|
||||
const ipAdapter = useAppSelector((s) => selectEntityOrThrow(s.canvasV2, entityIdentifier).ipAdapter);
|
||||
const selectIPAdapter = useMemo(
|
||||
() => createSelector(selectCanvasSlice, (s) => selectEntityOrThrow(s, entityIdentifier).ipAdapter),
|
||||
[entityIdentifier]
|
||||
);
|
||||
const ipAdapter = useAppSelector(selectIPAdapter);
|
||||
|
||||
const onChangeBeginEndStepPct = useCallback(
|
||||
(beginEndStepPct: [number, number]) => {
|
||||
|
@ -1,17 +1,22 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasEntityGroupList } from 'features/controlLayers/components/common/CanvasEntityGroupList';
|
||||
import { InpaintMask } from 'features/controlLayers/components/InpaintMask/InpaintMask';
|
||||
import { mapId } from 'features/controlLayers/konva/util';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { selectCanvasSlice, selectSelectedEntityIdentifier } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
|
||||
const selectEntityIds = createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
return canvasV2.inpaintMasks.entities.map(mapId).reverse();
|
||||
const selectEntityIds = createMemoizedSelector(selectCanvasSlice, (canvas) => {
|
||||
return canvas.inpaintMasks.entities.map(mapId).reverse();
|
||||
});
|
||||
|
||||
const selectIsSelected = createSelector(selectSelectedEntityIdentifier, (selectedEntityIdentifier) => {
|
||||
return selectedEntityIdentifier?.type === 'inpaint_mask';
|
||||
});
|
||||
|
||||
export const InpaintMaskList = memo(() => {
|
||||
const isSelected = useAppSelector((s) => Boolean(s.canvasV2.selectedEntityIdentifier?.type === 'inpaint_mask'));
|
||||
const isSelected = useAppSelector(selectIsSelected);
|
||||
const entityIds = useAppSelector(selectEntityIds);
|
||||
|
||||
if (entityIds.length === 0) {
|
||||
|
@ -1,20 +1,25 @@
|
||||
import { Flex, Popover, PopoverBody, PopoverContent, PopoverTrigger } from '@invoke-ai/ui-library';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import RgbColorPicker from 'common/components/RgbColorPicker';
|
||||
import { rgbColorToString } from 'common/util/colorCodeTransformers';
|
||||
import { MaskFillStyle } from 'features/controlLayers/components/common/MaskFillStyle';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { inpaintMaskFillColorChanged, inpaintMaskFillStyleChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { inpaintMaskFillColorChanged, inpaintMaskFillStyleChanged } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectCanvasSlice, selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import type { FillStyle, RgbColor } from 'features/controlLayers/store/types';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const InpaintMaskMaskFillColorPicker = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const entityIdentifier = useEntityIdentifierContext('inpaint_mask');
|
||||
const fill = useAppSelector((s) => selectEntityOrThrow(s.canvasV2, entityIdentifier).fill);
|
||||
const selectFill = useMemo(
|
||||
() => createSelector(selectCanvasSlice, (canvas) => selectEntityOrThrow(canvas, entityIdentifier).fill),
|
||||
[entityIdentifier]
|
||||
);
|
||||
const fill = useAppSelector(selectFill);
|
||||
|
||||
const onChangeFillColor = useCallback(
|
||||
(color: RgbColor) => {
|
||||
|
@ -1,17 +1,21 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasEntityGroupList } from 'features/controlLayers/components/common/CanvasEntityGroupList';
|
||||
import { RasterLayer } from 'features/controlLayers/components/RasterLayer/RasterLayer';
|
||||
import { mapId } from 'features/controlLayers/konva/util';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { selectCanvasSlice, selectSelectedEntityIdentifier } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
|
||||
const selectEntityIds = createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
return canvasV2.rasterLayers.entities.map(mapId).reverse();
|
||||
const selectEntityIds = createMemoizedSelector(selectCanvasSlice, (canvas) => {
|
||||
return canvas.rasterLayers.entities.map(mapId).reverse();
|
||||
});
|
||||
const selectIsSelected = createSelector(selectSelectedEntityIdentifier, (selectedEntityIdentifier) => {
|
||||
return selectedEntityIdentifier?.type === 'raster_layer';
|
||||
});
|
||||
|
||||
export const RasterLayerEntityList = memo(() => {
|
||||
const isSelected = useAppSelector((s) => Boolean(s.canvasV2.selectedEntityIdentifier?.type === 'raster_layer'));
|
||||
const isSelected = useAppSelector(selectIsSelected);
|
||||
const layerIds = useAppSelector(selectEntityIds);
|
||||
|
||||
if (layerIds.length === 0) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { MenuItem } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { rasterLayerConvertedToControlLayer } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { rasterLayerConvertedToControlLayer } from 'features/controlLayers/store/canvasSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiLightningBold } from 'react-icons/pi';
|
||||
|
@ -6,8 +6,8 @@ import {
|
||||
rgIPAdapterAdded,
|
||||
rgNegativePromptChanged,
|
||||
rgPositivePromptChanged,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice, selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
} from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectCanvasSlice, selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiPlusBold } from 'react-icons/pi';
|
||||
@ -18,8 +18,8 @@ export const RegionalGuidanceAddPromptsIPAdapterButtons = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const selectValidActions = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
const entity = selectEntityOrThrow(canvasV2, entityIdentifier);
|
||||
createMemoizedSelector(selectCanvasSlice, (canvas) => {
|
||||
const entity = selectEntityOrThrow(canvas, entityIdentifier);
|
||||
return {
|
||||
canAddPositivePrompt: entity?.positivePrompt === null,
|
||||
canAddNegativePrompt: entity?.negativePrompt === null,
|
||||
|
@ -1,14 +1,19 @@
|
||||
import { Badge } from '@invoke-ai/ui-library';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
import { selectCanvasSlice, selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const RegionalGuidanceBadges = memo(() => {
|
||||
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
|
||||
const { t } = useTranslation();
|
||||
const autoNegative = useAppSelector((s) => selectEntityOrThrow(s.canvasV2, entityIdentifier).autoNegative);
|
||||
const selectAutoNegative = useMemo(
|
||||
() => createSelector(selectCanvasSlice, (canvas) => selectEntityOrThrow(canvas, entityIdentifier).autoNegative),
|
||||
[entityIdentifier]
|
||||
);
|
||||
const autoNegative = useAppSelector(selectAutoNegative);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -1,17 +1,21 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasEntityGroupList } from 'features/controlLayers/components/common/CanvasEntityGroupList';
|
||||
import { RegionalGuidance } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidance';
|
||||
import { mapId } from 'features/controlLayers/konva/util';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { selectCanvasSlice, selectSelectedEntityIdentifier } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
|
||||
const selectEntityIds = createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
return canvasV2.regions.entities.map(mapId).reverse();
|
||||
const selectEntityIds = createMemoizedSelector(selectCanvasSlice, (canvas) => {
|
||||
return canvas.regions.entities.map(mapId).reverse();
|
||||
});
|
||||
const selectIsSelected = createSelector(selectSelectedEntityIdentifier, (selectedEntityIdentifier) => {
|
||||
return selectedEntityIdentifier?.type === 'raster_layer';
|
||||
});
|
||||
|
||||
export const RegionalGuidanceEntityList = memo(() => {
|
||||
const isSelected = useAppSelector((s) => Boolean(s.canvasV2.selectedEntityIdentifier?.type === 'regional_guidance'));
|
||||
const isSelected = useAppSelector(selectIsSelected);
|
||||
const rgIds = useAppSelector(selectEntityIds);
|
||||
|
||||
if (rgIds.length === 0) {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Box, Flex, IconButton, Spacer, Text } from '@invoke-ai/ui-library';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { BeginEndStepPct } from 'features/controlLayers/components/common/BeginEndStepPct';
|
||||
import { Weight } from 'features/controlLayers/components/common/Weight';
|
||||
@ -14,8 +15,8 @@ import {
|
||||
rgIPAdapterMethodChanged,
|
||||
rgIPAdapterModelChanged,
|
||||
rgIPAdapterWeightChanged,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectRegionalGuidanceIPAdapter } from 'features/controlLayers/store/selectors';
|
||||
} from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectCanvasSlice, selectRegionalGuidanceIPAdapter } from 'features/controlLayers/store/selectors';
|
||||
import type { CLIPVisionModelV2, IPMethodV2 } from 'features/controlLayers/store/types';
|
||||
import type { RGIPAdapterImageDropData } from 'features/dnd/types';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
@ -34,11 +35,16 @@ export const RegionalGuidanceIPAdapterSettings = memo(({ ipAdapterId, ipAdapterN
|
||||
const onDeleteIPAdapter = useCallback(() => {
|
||||
dispatch(rgIPAdapterDeleted({ entityIdentifier, ipAdapterId }));
|
||||
}, [dispatch, entityIdentifier, ipAdapterId]);
|
||||
const ipAdapter = useAppSelector((s) => {
|
||||
const ipa = selectRegionalGuidanceIPAdapter(s.canvasV2, entityIdentifier, ipAdapterId);
|
||||
assert(ipa, `Regional GuidanceIP Adapter with id ${ipAdapterId} not found`);
|
||||
return ipa;
|
||||
});
|
||||
const selectIPAdapter = useMemo(
|
||||
() =>
|
||||
createSelector(selectCanvasSlice, (canvas) => {
|
||||
const ipAdapter = selectRegionalGuidanceIPAdapter(canvas, entityIdentifier, ipAdapterId);
|
||||
assert(ipAdapter, `Regional GuidanceIP Adapter with id ${ipAdapterId} not found`);
|
||||
return ipAdapter;
|
||||
}),
|
||||
[entityIdentifier, ipAdapterId]
|
||||
);
|
||||
const ipAdapter = useAppSelector(selectIPAdapter);
|
||||
|
||||
const onChangeBeginEndStepPct = useCallback(
|
||||
(beginEndStepPct: [number, number]) => {
|
||||
|
@ -4,7 +4,7 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { RegionalGuidanceIPAdapterSettings } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceIPAdapterSettings';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { selectCanvasV2Slice, selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { selectCanvasSlice, selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { Fragment, memo, useMemo } from 'react';
|
||||
|
||||
export const RegionalGuidanceIPAdapters = memo(() => {
|
||||
@ -12,8 +12,8 @@ export const RegionalGuidanceIPAdapters = memo(() => {
|
||||
|
||||
const selectIPAdapterIds = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
const ipAdapterIds = selectEntityOrThrow(canvasV2, entityIdentifier).ipAdapters.map(({ id }) => id);
|
||||
createMemoizedSelector(selectCanvasSlice, (canvas) => {
|
||||
const ipAdapterIds = selectEntityOrThrow(canvas, entityIdentifier).ipAdapters.map(({ id }) => id);
|
||||
if (ipAdapterIds.length === 0) {
|
||||
return EMPTY_ARRAY;
|
||||
}
|
||||
|
@ -1,20 +1,25 @@
|
||||
import { Flex, Popover, PopoverBody, PopoverContent, PopoverTrigger } from '@invoke-ai/ui-library';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import RgbColorPicker from 'common/components/RgbColorPicker';
|
||||
import { rgbColorToString } from 'common/util/colorCodeTransformers';
|
||||
import { MaskFillStyle } from 'features/controlLayers/components/common/MaskFillStyle';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { rgFillColorChanged, rgFillStyleChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { rgFillColorChanged, rgFillStyleChanged } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectCanvasSlice, selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import type { FillStyle, RgbColor } from 'features/controlLayers/store/types';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const RegionalGuidanceMaskFillColorPicker = memo(() => {
|
||||
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const fill = useAppSelector((s) => selectEntityOrThrow(s.canvasV2, entityIdentifier).fill);
|
||||
const selectFill = useMemo(
|
||||
() => createSelector(selectCanvasSlice, (canvas) => selectEntityOrThrow(canvas, entityIdentifier).fill),
|
||||
[entityIdentifier]
|
||||
);
|
||||
const fill = useAppSelector(selectFill);
|
||||
const onChangeFillColor = useCallback(
|
||||
(color: RgbColor) => {
|
||||
dispatch(rgFillColorChanged({ entityIdentifier, color }));
|
||||
|
@ -6,8 +6,8 @@ import {
|
||||
rgIPAdapterAdded,
|
||||
rgNegativePromptChanged,
|
||||
rgPositivePromptChanged,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/selectors';
|
||||
} from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectCanvasSlice, selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@ -17,8 +17,8 @@ export const RegionalGuidanceMenuItemsAddPromptsAndIPAdapter = memo(() => {
|
||||
const dispatch = useAppDispatch();
|
||||
const selectValidActions = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
const entity = selectEntity(canvasV2, entityIdentifier);
|
||||
createMemoizedSelector(selectCanvasSlice, (canvas) => {
|
||||
const entity = selectEntity(canvas, entityIdentifier);
|
||||
return {
|
||||
canAddPositivePrompt: entity?.positivePrompt === null,
|
||||
canAddNegativePrompt: entity?.negativePrompt === null,
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { MenuItem } from '@invoke-ai/ui-library';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { rgAutoNegativeToggled } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { rgAutoNegativeToggled } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectCanvasSlice, selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiSelectionInverseBold } from 'react-icons/pi';
|
||||
|
||||
@ -11,7 +12,11 @@ export const RegionalGuidanceMenuItemsAutoNegative = memo(() => {
|
||||
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const autoNegative = useAppSelector((s) => selectEntityOrThrow(s.canvasV2, entityIdentifier).autoNegative);
|
||||
const selectAutoNegative = useMemo(
|
||||
() => createSelector(selectCanvasSlice, (canvas) => selectEntityOrThrow(canvas, entityIdentifier).autoNegative),
|
||||
[entityIdentifier]
|
||||
);
|
||||
const autoNegative = useAppSelector(selectAutoNegative);
|
||||
const onClick = useCallback(() => {
|
||||
dispatch(rgAutoNegativeToggled({ entityIdentifier }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
|
@ -1,19 +1,25 @@
|
||||
import { Box, Textarea } from '@invoke-ai/ui-library';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { RegionalGuidanceDeletePromptButton } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceDeletePromptButton';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { rgNegativePromptChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { rgNegativePromptChanged } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectCanvasSlice, selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
||||
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
|
||||
import { PromptPopover } from 'features/prompt/PromptPopover';
|
||||
import { usePrompt } from 'features/prompt/usePrompt';
|
||||
import { memo, useCallback, useRef } from 'react';
|
||||
import { memo, useCallback, useMemo, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const RegionalGuidanceNegativePrompt = memo(() => {
|
||||
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
|
||||
const prompt = useAppSelector((s) => selectEntityOrThrow(s.canvasV2, entityIdentifier).negativePrompt ?? '');
|
||||
const selectPrompt = useMemo(
|
||||
() =>
|
||||
createSelector(selectCanvasSlice, (canvas) => selectEntityOrThrow(canvas, entityIdentifier).negativePrompt ?? ''),
|
||||
[entityIdentifier]
|
||||
);
|
||||
const prompt = useAppSelector(selectPrompt);
|
||||
const dispatch = useAppDispatch();
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const { t } = useTranslation();
|
||||
|
@ -1,19 +1,25 @@
|
||||
import { Box, Textarea } from '@invoke-ai/ui-library';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { RegionalGuidanceDeletePromptButton } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceDeletePromptButton';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { rgPositivePromptChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { rgPositivePromptChanged } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectCanvasSlice, selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
||||
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
|
||||
import { PromptPopover } from 'features/prompt/PromptPopover';
|
||||
import { usePrompt } from 'features/prompt/usePrompt';
|
||||
import { memo, useCallback, useRef } from 'react';
|
||||
import { memo, useCallback, useMemo, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const RegionalGuidancePositivePrompt = memo(() => {
|
||||
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
|
||||
const prompt = useAppSelector((s) => selectEntityOrThrow(s.canvasV2, entityIdentifier).positivePrompt ?? '');
|
||||
const selectPrompt = useMemo(
|
||||
() =>
|
||||
createSelector(selectCanvasSlice, (canvas) => selectEntityOrThrow(canvas, entityIdentifier).positivePrompt ?? ''),
|
||||
[entityIdentifier]
|
||||
);
|
||||
const prompt = useAppSelector(selectPrompt);
|
||||
const dispatch = useAppDispatch();
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const { t } = useTranslation();
|
||||
|
@ -1,10 +1,11 @@
|
||||
import { Divider } from '@invoke-ai/ui-library';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasEntitySettingsWrapper } from 'features/controlLayers/components/common/CanvasEntitySettingsWrapper';
|
||||
import { RegionalGuidanceAddPromptsIPAdapterButtons } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceAddPromptsIPAdapterButtons';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
import { selectCanvasSlice, selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { memo, useMemo } from 'react';
|
||||
|
||||
import { RegionalGuidanceIPAdapters } from './RegionalGuidanceIPAdapters';
|
||||
import { RegionalGuidanceNegativePrompt } from './RegionalGuidanceNegativePrompt';
|
||||
@ -12,30 +13,38 @@ import { RegionalGuidancePositivePrompt } from './RegionalGuidancePositivePrompt
|
||||
|
||||
export const RegionalGuidanceSettings = memo(() => {
|
||||
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
|
||||
const hasPositivePrompt = useAppSelector(
|
||||
(s) => selectEntityOrThrow(s.canvasV2, entityIdentifier).positivePrompt !== null
|
||||
const selectFlags = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(selectCanvasSlice, (canvas) => {
|
||||
const entity = selectEntityOrThrow(canvas, entityIdentifier);
|
||||
return {
|
||||
hasPositivePrompt: entity.positivePrompt !== null,
|
||||
hasNegativePrompt: entity.negativePrompt !== null,
|
||||
hasIPAdapters: entity.ipAdapters.length > 0,
|
||||
};
|
||||
}),
|
||||
[entityIdentifier]
|
||||
);
|
||||
const hasNegativePrompt = useAppSelector(
|
||||
(s) => selectEntityOrThrow(s.canvasV2, entityIdentifier).negativePrompt !== null
|
||||
);
|
||||
const hasIPAdapters = useAppSelector((s) => selectEntityOrThrow(s.canvasV2, entityIdentifier).ipAdapters.length > 0);
|
||||
const flags = useAppSelector(selectFlags);
|
||||
|
||||
return (
|
||||
<CanvasEntitySettingsWrapper>
|
||||
{!hasPositivePrompt && !hasNegativePrompt && !hasIPAdapters && <RegionalGuidanceAddPromptsIPAdapterButtons />}
|
||||
{hasPositivePrompt && (
|
||||
{!flags.hasPositivePrompt && !flags.hasNegativePrompt && !flags.hasIPAdapters && (
|
||||
<RegionalGuidanceAddPromptsIPAdapterButtons />
|
||||
)}
|
||||
{flags.hasPositivePrompt && (
|
||||
<>
|
||||
<RegionalGuidancePositivePrompt />
|
||||
{(hasNegativePrompt || hasIPAdapters) && <Divider />}
|
||||
{(flags.hasNegativePrompt || flags.hasIPAdapters) && <Divider />}
|
||||
</>
|
||||
)}
|
||||
{hasNegativePrompt && (
|
||||
{flags.hasNegativePrompt && (
|
||||
<>
|
||||
<RegionalGuidanceNegativePrompt />
|
||||
{hasIPAdapters && <Divider />}
|
||||
{flags.hasIPAdapters && <Divider />}
|
||||
</>
|
||||
)}
|
||||
{hasIPAdapters && <RegionalGuidanceIPAdapters />}
|
||||
{flags.hasIPAdapters && <RegionalGuidanceIPAdapters />}
|
||||
</CanvasEntitySettingsWrapper>
|
||||
);
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Button } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { canvasReset } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { canvasReset } from 'features/controlLayers/store/canvasSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
|
@ -3,7 +3,8 @@ import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
|
||||
import { useIsFiltering } from 'features/controlLayers/hooks/useIsFiltering';
|
||||
import { useIsTransforming } from 'features/controlLayers/hooks/useIsTransforming';
|
||||
import { isDrawableEntityType } from 'features/controlLayers/store/types';
|
||||
import { selectIsStaging } from 'features/controlLayers/store/canvasSessionSlice';
|
||||
import { selectIsSelectedEntityDrawable } from 'features/controlLayers/store/selectors';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -13,15 +14,10 @@ export const ToolBrushButton = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const isFiltering = useIsFiltering();
|
||||
const isTransforming = useIsTransforming();
|
||||
const isStaging = useAppSelector((s) => s.canvasSession.isStaging);
|
||||
const isStaging = useAppSelector(selectIsStaging);
|
||||
const selectBrush = useSelectTool('brush');
|
||||
const isSelected = useToolIsSelected('brush');
|
||||
const isDrawingToolAllowed = useAppSelector((s) => {
|
||||
if (!s.canvasV2.selectedEntityIdentifier?.type) {
|
||||
return false;
|
||||
}
|
||||
return isDrawableEntityType(s.canvasV2.selectedEntityIdentifier.type);
|
||||
});
|
||||
const isDrawingToolAllowed = useAppSelector(selectIsSelectedEntityDrawable);
|
||||
|
||||
const isDisabled = useMemo(() => {
|
||||
return isTransforming || isFiltering || isStaging || !isDrawingToolAllowed;
|
||||
|
@ -3,7 +3,8 @@ import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
|
||||
import { useIsFiltering } from 'features/controlLayers/hooks/useIsFiltering';
|
||||
import { useIsTransforming } from 'features/controlLayers/hooks/useIsTransforming';
|
||||
import { isDrawableEntityType } from 'features/controlLayers/store/types';
|
||||
import { selectIsStaging } from 'features/controlLayers/store/canvasSessionSlice';
|
||||
import { selectIsSelectedEntityDrawable } from 'features/controlLayers/store/selectors';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -13,15 +14,10 @@ export const ToolEraserButton = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const isFiltering = useIsFiltering();
|
||||
const isTransforming = useIsTransforming();
|
||||
const isStaging = useAppSelector((s) => s.canvasSession.isStaging);
|
||||
const isStaging = useAppSelector(selectIsStaging);
|
||||
const selectEraser = useSelectTool('eraser');
|
||||
const isSelected = useToolIsSelected('eraser');
|
||||
const isDrawingToolAllowed = useAppSelector((s) => {
|
||||
if (!s.canvasV2.selectedEntityIdentifier?.type) {
|
||||
return false;
|
||||
}
|
||||
return isDrawableEntityType(s.canvasV2.selectedEntityIdentifier.type);
|
||||
});
|
||||
const isDrawingToolAllowed = useAppSelector(selectIsSelectedEntityDrawable);
|
||||
const isDisabled = useMemo(() => {
|
||||
return isTransforming || isFiltering || isStaging || !isDrawingToolAllowed;
|
||||
}, [isDrawingToolAllowed, isFiltering, isStaging, isTransforming]);
|
||||
|
@ -3,7 +3,8 @@ import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
|
||||
import { useIsFiltering } from 'features/controlLayers/hooks/useIsFiltering';
|
||||
import { useIsTransforming } from 'features/controlLayers/hooks/useIsTransforming';
|
||||
import { isDrawableEntityType } from 'features/controlLayers/store/types';
|
||||
import { selectIsStaging } from 'features/controlLayers/store/canvasSessionSlice';
|
||||
import { selectIsSelectedEntityDrawable } from 'features/controlLayers/store/selectors';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -15,13 +16,8 @@ export const ToolMoveButton = memo(() => {
|
||||
const isTransforming = useIsTransforming();
|
||||
const selectMove = useSelectTool('move');
|
||||
const isSelected = useToolIsSelected('move');
|
||||
const isStaging = useAppSelector((s) => s.canvasSession.isStaging);
|
||||
const isDrawingToolAllowed = useAppSelector((s) => {
|
||||
if (!s.canvasV2.selectedEntityIdentifier?.type) {
|
||||
return false;
|
||||
}
|
||||
return isDrawableEntityType(s.canvasV2.selectedEntityIdentifier.type);
|
||||
});
|
||||
const isStaging = useAppSelector(selectIsStaging);
|
||||
const isDrawingToolAllowed = useAppSelector(selectIsSelectedEntityDrawable);
|
||||
const isDisabled = useMemo(() => {
|
||||
return isTransforming || isFiltering || isStaging || !isDrawingToolAllowed;
|
||||
}, [isDrawingToolAllowed, isFiltering, isStaging, isTransforming]);
|
||||
|
@ -3,7 +3,8 @@ import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
|
||||
import { useIsFiltering } from 'features/controlLayers/hooks/useIsFiltering';
|
||||
import { useIsTransforming } from 'features/controlLayers/hooks/useIsTransforming';
|
||||
import { isDrawableEntityType } from 'features/controlLayers/store/types';
|
||||
import { selectIsStaging } from 'features/controlLayers/store/canvasSessionSlice';
|
||||
import { selectIsSelectedEntityDrawable } from 'features/controlLayers/store/selectors';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -15,13 +16,8 @@ export const ToolRectButton = memo(() => {
|
||||
const isSelected = useToolIsSelected('rect');
|
||||
const isFiltering = useIsFiltering();
|
||||
const isTransforming = useIsTransforming();
|
||||
const isStaging = useAppSelector((s) => s.canvasSession.isStaging);
|
||||
const isDrawingToolAllowed = useAppSelector((s) => {
|
||||
if (!s.canvasV2.selectedEntityIdentifier?.type) {
|
||||
return false;
|
||||
}
|
||||
return isDrawableEntityType(s.canvasV2.selectedEntityIdentifier.type);
|
||||
});
|
||||
const isStaging = useAppSelector(selectIsStaging);
|
||||
const isDrawingToolAllowed = useAppSelector(selectIsSelectedEntityDrawable);
|
||||
|
||||
const isDisabled = useMemo(() => {
|
||||
return isTransforming || isFiltering || isStaging || !isDrawingToolAllowed;
|
||||
|
@ -5,22 +5,25 @@ import { memo, useCallback } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiArrowClockwiseBold, PiArrowCounterClockwiseBold } from 'react-icons/pi';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { ActionCreators } from 'redux-undo';
|
||||
|
||||
export const UndoRedoButtonGroup = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const mayUndo = useAppSelector(() => false);
|
||||
const mayUndo = useAppSelector(() => true);
|
||||
const handleUndo = useCallback(() => {
|
||||
// TODO(psyche): Implement undo
|
||||
// dispatch(undo());
|
||||
}, []);
|
||||
dispatch(ActionCreators.undo());
|
||||
}, [dispatch]);
|
||||
useHotkeys(['meta+z', 'ctrl+z'], handleUndo, { enabled: mayUndo, preventDefault: true }, [mayUndo, handleUndo]);
|
||||
|
||||
const mayRedo = useAppSelector(() => false);
|
||||
const mayRedo = useAppSelector(() => true);
|
||||
const handleRedo = useCallback(() => {
|
||||
// TODO(psyche): Implement redo
|
||||
// dispatch(redo());
|
||||
}, []);
|
||||
dispatch(ActionCreators.redo());
|
||||
}, [dispatch]);
|
||||
useHotkeys(['meta+shift+z', 'ctrl+shift+z'], handleRedo, { enabled: mayRedo, preventDefault: true }, [
|
||||
mayRedo,
|
||||
handleRedo,
|
||||
|
@ -3,7 +3,7 @@ import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { useEntityIsSelected } from 'features/controlLayers/hooks/useEntityIsSelected';
|
||||
import { useEntitySelectionColor } from 'features/controlLayers/hooks/useEntitySelectionColor';
|
||||
import { entitySelected } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { entitySelected } from 'features/controlLayers/store/canvasSlice';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { memo, useCallback } from 'react';
|
||||
|
||||
|
@ -3,7 +3,7 @@ import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { stopPropagation } from 'common/util/stopPropagation';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { useEntityIsEnabled } from 'features/controlLayers/hooks/useEntityIsEnabled';
|
||||
import { entityIsEnabledToggled } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { entityIsEnabledToggled } from 'features/controlLayers/store/canvasSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiCheckBold } from 'react-icons/pi';
|
||||
|
@ -7,41 +7,41 @@ import {
|
||||
entityArrangedForwardOne,
|
||||
entityArrangedToBack,
|
||||
entityArrangedToFront,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import type { CanvasEntityIdentifier, CanvasV2State } from 'features/controlLayers/store/types';
|
||||
} from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import type { CanvasEntityIdentifier, CanvasState } from 'features/controlLayers/store/types';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiArrowDownBold, PiArrowLineDownBold, PiArrowLineUpBold, PiArrowUpBold } from 'react-icons/pi';
|
||||
|
||||
const getIndexAndCount = (
|
||||
canvasV2: CanvasV2State,
|
||||
canvas: CanvasState,
|
||||
{ id, type }: CanvasEntityIdentifier
|
||||
): { index: number; count: number } => {
|
||||
if (type === 'raster_layer') {
|
||||
return {
|
||||
index: canvasV2.rasterLayers.entities.findIndex((entity) => entity.id === id),
|
||||
count: canvasV2.rasterLayers.entities.length,
|
||||
index: canvas.rasterLayers.entities.findIndex((entity) => entity.id === id),
|
||||
count: canvas.rasterLayers.entities.length,
|
||||
};
|
||||
} else if (type === 'control_layer') {
|
||||
return {
|
||||
index: canvasV2.controlLayers.entities.findIndex((entity) => entity.id === id),
|
||||
count: canvasV2.controlLayers.entities.length,
|
||||
index: canvas.controlLayers.entities.findIndex((entity) => entity.id === id),
|
||||
count: canvas.controlLayers.entities.length,
|
||||
};
|
||||
} else if (type === 'regional_guidance') {
|
||||
return {
|
||||
index: canvasV2.regions.entities.findIndex((entity) => entity.id === id),
|
||||
count: canvasV2.regions.entities.length,
|
||||
index: canvas.regions.entities.findIndex((entity) => entity.id === id),
|
||||
count: canvas.regions.entities.length,
|
||||
};
|
||||
} else if (type === 'inpaint_mask') {
|
||||
return {
|
||||
index: canvasV2.inpaintMasks.entities.findIndex((entity) => entity.id === id),
|
||||
count: canvasV2.inpaintMasks.entities.length,
|
||||
index: canvas.inpaintMasks.entities.findIndex((entity) => entity.id === id),
|
||||
count: canvas.inpaintMasks.entities.length,
|
||||
};
|
||||
} else if (type === 'ip_adapter') {
|
||||
return {
|
||||
index: canvasV2.ipAdapters.entities.findIndex((entity) => entity.id === id),
|
||||
count: canvasV2.ipAdapters.entities.length,
|
||||
index: canvas.ipAdapters.entities.findIndex((entity) => entity.id === id),
|
||||
count: canvas.ipAdapters.entities.length,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
@ -57,8 +57,8 @@ export const CanvasEntityMenuItemsArrange = memo(() => {
|
||||
const entityIdentifier = useEntityIdentifierContext();
|
||||
const selectValidActions = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
const { index, count } = getIndexAndCount(canvasV2, entityIdentifier);
|
||||
createMemoizedSelector(selectCanvasSlice, (canvas) => {
|
||||
const { index, count } = getIndexAndCount(canvas, entityIdentifier);
|
||||
return {
|
||||
canMoveForwardOne: index < count - 1,
|
||||
canMoveBackwardOne: index > 0,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { MenuItem } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { entityDeleted } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { entityDeleted } from 'features/controlLayers/store/canvasSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiTrashSimpleBold } from 'react-icons/pi';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { MenuItem } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { entityDuplicated } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { entityDuplicated } from 'features/controlLayers/store/canvasSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiCopyFill } from 'react-icons/pi';
|
||||
|
@ -15,8 +15,12 @@ import {
|
||||
} from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { snapToNearest } from 'features/controlLayers/konva/util';
|
||||
import { entityOpacityChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import { entityOpacityChanged } from 'features/controlLayers/store/canvasSlice';
|
||||
import {
|
||||
selectCanvasSlice,
|
||||
selectEntity,
|
||||
selectSelectedEntityIdentifier,
|
||||
} from 'features/controlLayers/store/selectors';
|
||||
import { isDrawableEntity } from 'features/controlLayers/store/types';
|
||||
import { clamp, round } from 'lodash-es';
|
||||
import type { KeyboardEvent } from 'react';
|
||||
@ -59,13 +63,14 @@ const snapCandidates = marks.slice(1, marks.length - 1);
|
||||
export const CanvasEntityOpacity = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const selectedEntityIdentifier = useAppSelector((s) => s.canvasV2.selectedEntityIdentifier);
|
||||
const selectedEntityIdentifier = useAppSelector(selectSelectedEntityIdentifier);
|
||||
const opacity = useAppSelector((s) => {
|
||||
const selectedEntityIdentifier = s.canvasV2.selectedEntityIdentifier;
|
||||
const selectedEntityIdentifier = selectSelectedEntityIdentifier(s);
|
||||
if (!selectedEntityIdentifier) {
|
||||
return null;
|
||||
}
|
||||
const selectedEntity = selectEntity(s.canvasV2, selectedEntityIdentifier);
|
||||
const canvas = selectCanvasSlice(s);
|
||||
const selectedEntity = selectEntity(canvas, selectedEntityIdentifier);
|
||||
if (!selectedEntity) {
|
||||
return null;
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import { rgbColorToString } from 'common/util/colorCodeTransformers';
|
||||
import { useEntityAdapter } from 'features/controlLayers/contexts/EntityAdapterContext';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { TRANSPARENCY_CHECKER_PATTERN } from 'features/controlLayers/konva/constants';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import { selectCanvasSlice, selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import { memo, useEffect, useMemo, useRef } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
@ -18,7 +18,7 @@ export const CanvasEntityPreviewImage = memo(() => {
|
||||
const adapter = useEntityAdapter();
|
||||
const selectMaskColor = useMemo(
|
||||
() =>
|
||||
createSelector(selectCanvasV2Slice, (state) => {
|
||||
createSelector(selectCanvasSlice, (state) => {
|
||||
const entity = selectEntity(state, entityIdentifier);
|
||||
if (!entity) {
|
||||
return null;
|
||||
|
@ -4,7 +4,7 @@ import { useBoolean } from 'common/hooks/useBoolean';
|
||||
import { CanvasEntityTitle } from 'features/controlLayers/components/common/CanvasEntityTitle';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { useEntityTitle } from 'features/controlLayers/hooks/useEntityTitle';
|
||||
import { entityNameChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { entityNameChanged } from 'features/controlLayers/store/canvasSlice';
|
||||
import type { ChangeEvent, KeyboardEvent } from 'react';
|
||||
import { memo, useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { IconButton } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { useEntityTypeIsHidden } from 'features/controlLayers/hooks/useEntityTypeIsHidden';
|
||||
import { useEntityTypeString } from 'features/controlLayers/hooks/useEntityTypeString';
|
||||
import { allEntitiesOfTypeIsHiddenToggled } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { allEntitiesOfTypeIsHiddenToggled } from 'features/controlLayers/store/canvasSlice';
|
||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
|
||||
import { entityDeleted } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { entityDeleted } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
|
||||
const selectSelectedEntityIdentifier = createMemoizedSelector(
|
||||
selectCanvasV2Slice,
|
||||
(canvasV2State) => canvasV2State.selectedEntityIdentifier
|
||||
selectCanvasSlice,
|
||||
(canvasState) => canvasState.selectedEntityIdentifier
|
||||
);
|
||||
|
||||
export function useCanvasDeleteLayerHotkey() {
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
|
||||
import { entityReset } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { entityReset } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
|
||||
const selectSelectedEntityIdentifier = createMemoizedSelector(
|
||||
selectCanvasV2Slice,
|
||||
(canvasV2State) => canvasV2State.selectedEntityIdentifier
|
||||
selectCanvasSlice,
|
||||
(canvasState) => canvasState.selectedEntityIdentifier
|
||||
);
|
||||
|
||||
export function useCanvasResetLayerHotkey() {
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import { selectCanvasSlice, selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const useEntityIsEnabled = (entityIdentifier: CanvasEntityIdentifier) => {
|
||||
const selectIsEnabled = useMemo(
|
||||
() =>
|
||||
createSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
const entity = selectEntity(canvasV2, entityIdentifier);
|
||||
createSelector(selectCanvasSlice, (canvas) => {
|
||||
const entity = selectEntity(canvas, entityIdentifier);
|
||||
if (!entity) {
|
||||
return false;
|
||||
} else {
|
||||
|
@ -1,12 +1,18 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectSelectedEntityIdentifier } from 'features/controlLayers/store/selectors';
|
||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const useEntityIsSelected = (entityIdentifier: CanvasEntityIdentifier) => {
|
||||
const selectedEntityIdentifier = useAppSelector((s) => s.canvasV2.selectedEntityIdentifier);
|
||||
const isSelected = useMemo(() => {
|
||||
return selectedEntityIdentifier?.id === entityIdentifier.id;
|
||||
}, [selectedEntityIdentifier, entityIdentifier.id]);
|
||||
const selectIsSelected = useMemo(
|
||||
() =>
|
||||
createSelector(selectSelectedEntityIdentifier, (selectedEntityIdentifier) => {
|
||||
return selectedEntityIdentifier?.id === entityIdentifier.id;
|
||||
}),
|
||||
[entityIdentifier.id]
|
||||
);
|
||||
const isSelected = useAppSelector(selectIsSelected);
|
||||
|
||||
return isSelected;
|
||||
};
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import { selectCanvasSlice, selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import { type CanvasEntityIdentifier, isDrawableEntity } from 'features/controlLayers/store/types';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const useEntityObjectCount = (entityIdentifier: CanvasEntityIdentifier) => {
|
||||
const selectObjectCount = useMemo(
|
||||
() =>
|
||||
createSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
const entity = selectEntity(canvasV2, entityIdentifier);
|
||||
createSelector(selectCanvasSlice, (canvas) => {
|
||||
const entity = selectEntity(canvas, entityIdentifier);
|
||||
if (!entity) {
|
||||
return 0;
|
||||
} else if (isDrawableEntity(entity)) {
|
||||
|
@ -1,15 +1,15 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { rgbColorToString } from 'common/util/colorCodeTransformers';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import { selectCanvasSlice, selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const useEntitySelectionColor = (entityIdentifier: CanvasEntityIdentifier) => {
|
||||
const selectSelectionColor = useMemo(
|
||||
() =>
|
||||
createSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
const entity = selectEntity(canvasV2, entityIdentifier);
|
||||
createSelector(selectCanvasSlice, (canvas) => {
|
||||
const entity = selectEntity(canvas, entityIdentifier);
|
||||
if (!entity) {
|
||||
return 'base.400';
|
||||
} else if (entity.type === 'inpaint_mask') {
|
||||
|
@ -1,15 +1,15 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useEntityObjectCount } from 'features/controlLayers/hooks/useEntityObjectCount';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import { selectCanvasSlice, selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
const createSelectName = (entityIdentifier: CanvasEntityIdentifier) =>
|
||||
createSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
const entity = selectEntity(canvasV2, entityIdentifier);
|
||||
createSelector(selectCanvasSlice, (canvas) => {
|
||||
const entity = selectEntity(canvas, entityIdentifier);
|
||||
if (!entity) {
|
||||
return null;
|
||||
}
|
||||
|
@ -1,24 +1,24 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const useEntityTypeCount = (type: CanvasEntityIdentifier['type']): number => {
|
||||
const selectEntityCount = useMemo(
|
||||
() =>
|
||||
createSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
createSelector(selectCanvasSlice, (canvas) => {
|
||||
switch (type) {
|
||||
case 'control_layer':
|
||||
return canvasV2.controlLayers.entities.length;
|
||||
return canvas.controlLayers.entities.length;
|
||||
case 'raster_layer':
|
||||
return canvasV2.rasterLayers.entities.length;
|
||||
return canvas.rasterLayers.entities.length;
|
||||
case 'inpaint_mask':
|
||||
return canvasV2.inpaintMasks.entities.length;
|
||||
return canvas.inpaintMasks.entities.length;
|
||||
case 'regional_guidance':
|
||||
return canvasV2.regions.entities.length;
|
||||
return canvas.regions.entities.length;
|
||||
case 'ip_adapter':
|
||||
return canvasV2.ipAdapters.entities.length;
|
||||
return canvas.ipAdapters.entities.length;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,22 +1,22 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const useEntityTypeIsHidden = (type: CanvasEntityIdentifier['type']): boolean => {
|
||||
const selectIsHidden = useMemo(
|
||||
() =>
|
||||
createSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
createSelector(selectCanvasSlice, (canvas) => {
|
||||
switch (type) {
|
||||
case 'control_layer':
|
||||
return canvasV2.controlLayers.isHidden;
|
||||
return canvas.controlLayers.isHidden;
|
||||
case 'raster_layer':
|
||||
return canvasV2.rasterLayers.isHidden;
|
||||
return canvas.rasterLayers.isHidden;
|
||||
case 'inpaint_mask':
|
||||
return canvasV2.inpaintMasks.isHidden;
|
||||
return canvas.inpaintMasks.isHidden;
|
||||
case 'regional_guidance':
|
||||
return canvasV2.regions.isHidden;
|
||||
return canvas.regions.isHidden;
|
||||
case 'ip_adapter':
|
||||
default:
|
||||
return false;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { createMemoizedAppSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import { selectCanvasV2Slice, selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { selectCanvasSlice, selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import type {
|
||||
CanvasEntityIdentifier,
|
||||
ControlNetConfig,
|
||||
@ -16,8 +16,8 @@ import { useControlNetAndT2IAdapterModels, useIPAdapterModels } from 'services/a
|
||||
export const useControlLayerControlAdapter = (entityIdentifier: CanvasEntityIdentifier<'control_layer'>) => {
|
||||
const selectControlAdapter = useMemo(
|
||||
() =>
|
||||
createMemoizedAppSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
const layer = selectEntityOrThrow(canvasV2, entityIdentifier);
|
||||
createMemoizedAppSelector(selectCanvasSlice, (canvas) => {
|
||||
const layer = selectEntityOrThrow(canvas, entityIdentifier);
|
||||
return layer.controlAdapter;
|
||||
}),
|
||||
[entityIdentifier]
|
||||
|
@ -6,7 +6,7 @@ import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase'
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import type { CanvasSessionState } from 'features/controlLayers/store/canvasSessionSlice';
|
||||
import type { CanvasSettingsState } from 'features/controlLayers/store/canvasSettingsSlice';
|
||||
import type { CanvasV2State } from 'features/controlLayers/store/types';
|
||||
import type { CanvasState } from 'features/controlLayers/store/types';
|
||||
import type { Logger } from 'roarr';
|
||||
|
||||
export class CanvasRenderingModule extends CanvasModuleBase {
|
||||
@ -18,7 +18,7 @@ export class CanvasRenderingModule extends CanvasModuleBase {
|
||||
manager: CanvasManager;
|
||||
subscriptions = new Set<() => void>();
|
||||
|
||||
state: CanvasV2State | null = null;
|
||||
state: CanvasState | null = null;
|
||||
settings: CanvasSettingsState | null = null;
|
||||
session: CanvasSessionState | null = null;
|
||||
|
||||
@ -119,7 +119,7 @@ export class CanvasRenderingModule extends CanvasModuleBase {
|
||||
}
|
||||
};
|
||||
|
||||
renderRasterLayers = async (state: CanvasV2State, prevState: CanvasV2State | null) => {
|
||||
renderRasterLayers = async (state: CanvasState, prevState: CanvasState | null) => {
|
||||
const adapterMap = this.manager.adapters.rasterLayers;
|
||||
|
||||
if (!prevState || state.rasterLayers.isHidden !== prevState.rasterLayers.isHidden) {
|
||||
@ -148,7 +148,7 @@ export class CanvasRenderingModule extends CanvasModuleBase {
|
||||
}
|
||||
};
|
||||
|
||||
renderControlLayers = async (prevState: CanvasV2State | null, state: CanvasV2State) => {
|
||||
renderControlLayers = async (prevState: CanvasState | null, state: CanvasState) => {
|
||||
const adapterMap = this.manager.adapters.controlLayers;
|
||||
|
||||
if (!prevState || state.controlLayers.isHidden !== prevState.controlLayers.isHidden) {
|
||||
@ -177,7 +177,7 @@ export class CanvasRenderingModule extends CanvasModuleBase {
|
||||
}
|
||||
};
|
||||
|
||||
renderRegionalGuidance = async (prevState: CanvasV2State | null, state: CanvasV2State) => {
|
||||
renderRegionalGuidance = async (prevState: CanvasState | null, state: CanvasState) => {
|
||||
const adapterMap = this.manager.adapters.regionMasks;
|
||||
|
||||
if (!prevState || state.regions.isHidden !== prevState.regions.isHidden) {
|
||||
@ -211,7 +211,7 @@ export class CanvasRenderingModule extends CanvasModuleBase {
|
||||
}
|
||||
};
|
||||
|
||||
renderInpaintMasks = async (state: CanvasV2State, prevState: CanvasV2State | null) => {
|
||||
renderInpaintMasks = async (state: CanvasState, prevState: CanvasState | null) => {
|
||||
const adapterMap = this.manager.adapters.inpaintMasks;
|
||||
|
||||
if (!prevState || state.inpaintMasks.isHidden !== prevState.inpaintMasks.isHidden) {
|
||||
@ -245,7 +245,7 @@ export class CanvasRenderingModule extends CanvasModuleBase {
|
||||
}
|
||||
};
|
||||
|
||||
renderBbox = (state: CanvasV2State, prevState: CanvasV2State | null) => {
|
||||
renderBbox = (state: CanvasState, prevState: CanvasState | null) => {
|
||||
if (!prevState || state.bbox !== prevState.bbox) {
|
||||
this.manager.preview.bbox.render();
|
||||
}
|
||||
@ -257,7 +257,7 @@ export class CanvasRenderingModule extends CanvasModuleBase {
|
||||
}
|
||||
};
|
||||
|
||||
arrangeEntities = (state: CanvasV2State, prevState: CanvasV2State | null) => {
|
||||
arrangeEntities = (state: CanvasState, prevState: CanvasState | null) => {
|
||||
if (
|
||||
!prevState ||
|
||||
state.rasterLayers.entities !== prevState.rasterLayers.entities ||
|
||||
|
@ -14,8 +14,8 @@ import {
|
||||
entityRectAdded,
|
||||
entityReset,
|
||||
entitySelected,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectAllRenderableEntities } from 'features/controlLayers/store/selectors';
|
||||
} from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectAllRenderableEntities, selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import {
|
||||
brushWidthChanged,
|
||||
eraserWidthChanged,
|
||||
@ -100,7 +100,7 @@ export class CanvasStateApiModule extends CanvasModuleBase {
|
||||
|
||||
// Reminder - use arrow functions to avoid binding issues
|
||||
getCanvasState = () => {
|
||||
return this.store.getState().canvasV2;
|
||||
return selectCanvasSlice(this.store.getState());
|
||||
};
|
||||
resetEntity = (arg: EntityIdentifierPayload) => {
|
||||
this.store.dispatch(entityReset(arg));
|
||||
|
@ -1,14 +1,14 @@
|
||||
import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import { roundDownToMultiple, roundToMultiple } from 'common/util/roundDownToMultiple';
|
||||
import type { BoundingBoxScaleMethod, CanvasV2State, Dimensions } from 'features/controlLayers/store/types';
|
||||
import type { BoundingBoxScaleMethod, CanvasState, Dimensions } from 'features/controlLayers/store/types';
|
||||
import { getScaledBoundingBoxDimensions } from 'features/controlLayers/util/getScaledBoundingBoxDimensions';
|
||||
import { calculateNewSize } from 'features/parameters/components/DocumentSize/calculateNewSize';
|
||||
import { ASPECT_RATIO_MAP, initialAspectRatioState } from 'features/parameters/components/DocumentSize/constants';
|
||||
import type { AspectRatioID } from 'features/parameters/components/DocumentSize/types';
|
||||
import type { IRect } from 'konva/lib/types';
|
||||
|
||||
const syncScaledSize = (state: CanvasV2State) => {
|
||||
const syncScaledSize = (state: CanvasState) => {
|
||||
if (state.bbox.scaleMethod === 'auto') {
|
||||
const { width, height } = state.bbox.rect;
|
||||
state.bbox.scaledSize = getScaledBoundingBoxDimensions({ width, height }, state.bbox.optimalDimension);
|
||||
@ -116,4 +116,4 @@ export const bboxReducers = {
|
||||
|
||||
syncScaledSize(state);
|
||||
},
|
||||
} satisfies SliceCaseReducers<CanvasV2State>;
|
||||
} satisfies SliceCaseReducers<CanvasState>;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { createAction, createSlice, type PayloadAction } from '@reduxjs/toolkit';
|
||||
import type { PersistConfig } from 'app/store/store';
|
||||
import { canvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { createAction, createSelector, createSlice, type PayloadAction } from '@reduxjs/toolkit';
|
||||
import type { PersistConfig, RootState } from 'app/store/store';
|
||||
import { canvasSlice } from 'features/controlLayers/store/canvasSlice';
|
||||
import type { SessionMode, StagingAreaImage } from 'features/controlLayers/store/types';
|
||||
|
||||
export type CanvasSessionState = {
|
||||
@ -79,5 +79,9 @@ export const canvasSessionPersistConfig: PersistConfig<CanvasSessionState> = {
|
||||
persistDenylist: [],
|
||||
};
|
||||
export const sessionStagingAreaImageAccepted = createAction<{ index: number }>(
|
||||
`${canvasV2Slice.name}/sessionStagingAreaImageAccepted`
|
||||
`${canvasSlice.name}/sessionStagingAreaImageAccepted`
|
||||
);
|
||||
|
||||
export const selectCanvasSessionSlice = (s: RootState) => s.canvasSession;
|
||||
|
||||
export const selectIsStaging = createSelector(selectCanvasSessionSlice, (canvasSession) => canvasSession.isStaging);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
|
||||
import type { PersistConfig } from 'app/store/store';
|
||||
import type { PersistConfig, RootState } from 'app/store/store';
|
||||
|
||||
export type CanvasSettingsState = {
|
||||
imageSmoothing: boolean;
|
||||
@ -51,3 +51,4 @@ export const canvasSettingsPersistConfig: PersistConfig<CanvasSettingsState> = {
|
||||
migrate,
|
||||
persistDenylist: [],
|
||||
};
|
||||
export const selectCanvasSettingsSlice = (s: RootState) => s.canvasSettings;
|
||||
|
@ -22,7 +22,7 @@ import { assert } from 'tsafe';
|
||||
|
||||
import type {
|
||||
CanvasEntityIdentifier,
|
||||
CanvasV2State,
|
||||
CanvasState,
|
||||
EntityBrushLineAddedPayload,
|
||||
EntityEraserLineAddedPayload,
|
||||
EntityIdentifierPayload,
|
||||
@ -32,7 +32,7 @@ import type {
|
||||
} from './types';
|
||||
import { getEntityIdentifier, isDrawableEntity } from './types';
|
||||
|
||||
const initialState: CanvasV2State = {
|
||||
const initialState: CanvasState = {
|
||||
_version: 3,
|
||||
selectedEntityIdentifier: null,
|
||||
rasterLayers: {
|
||||
@ -64,8 +64,8 @@ const initialState: CanvasV2State = {
|
||||
},
|
||||
};
|
||||
|
||||
export const canvasV2Slice = createSlice({
|
||||
name: 'canvasV2',
|
||||
export const canvasSlice = createSlice({
|
||||
name: 'canvas',
|
||||
initialState,
|
||||
reducers: {
|
||||
// undoable canvas state
|
||||
@ -217,7 +217,7 @@ export const canvasV2Slice = createSlice({
|
||||
entityDeleted: (state, action: PayloadAction<EntityIdentifierPayload>) => {
|
||||
const { entityIdentifier } = action.payload;
|
||||
|
||||
let selectedEntityIdentifier: CanvasV2State['selectedEntityIdentifier'] = null;
|
||||
let selectedEntityIdentifier: CanvasState['selectedEntityIdentifier'] = null;
|
||||
const allEntities = selectAllEntities(state);
|
||||
const index = allEntities.findIndex((entity) => entity.id === entityIdentifier.id);
|
||||
const nextIndex = allEntities.length > 1 ? (index + 1) % allEntities.length : -1;
|
||||
@ -438,15 +438,15 @@ export const {
|
||||
// inpaintMaskRecalled,
|
||||
inpaintMaskFillColorChanged,
|
||||
inpaintMaskFillStyleChanged,
|
||||
} = canvasV2Slice.actions;
|
||||
} = canvasSlice.actions;
|
||||
|
||||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||
const migrate = (state: any): any => {
|
||||
return state;
|
||||
};
|
||||
|
||||
export const canvasV2PersistConfig: PersistConfig<CanvasV2State> = {
|
||||
name: canvasV2Slice.name,
|
||||
export const canvasPersistConfig: PersistConfig<CanvasState> = {
|
||||
name: canvasSlice.name,
|
||||
initialState,
|
||||
migrate,
|
||||
persistDenylist: [],
|
@ -9,7 +9,7 @@ import type { ControlNetModelConfig, T2IAdapterModelConfig } from 'services/api/
|
||||
import type {
|
||||
CanvasControlLayerState,
|
||||
CanvasRasterLayerState,
|
||||
CanvasV2State,
|
||||
CanvasState,
|
||||
ControlModeV2,
|
||||
ControlNetConfig,
|
||||
EntityIdentifierPayload,
|
||||
@ -159,4 +159,4 @@ export const controlLayersReducers = {
|
||||
}
|
||||
layer.withTransparencyEffect = !layer.withTransparencyEffect;
|
||||
},
|
||||
} satisfies SliceCaseReducers<CanvasV2State>;
|
||||
} satisfies SliceCaseReducers<CanvasState>;
|
||||
|
@ -3,7 +3,7 @@ import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import { selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import type {
|
||||
CanvasInpaintMaskState,
|
||||
CanvasV2State,
|
||||
CanvasState,
|
||||
EntityIdentifierPayload,
|
||||
FillStyle,
|
||||
RgbColor,
|
||||
@ -68,4 +68,4 @@ export const inpaintMaskReducers = {
|
||||
}
|
||||
entity.fill.style = style;
|
||||
},
|
||||
} satisfies SliceCaseReducers<CanvasV2State>;
|
||||
} satisfies SliceCaseReducers<CanvasState>;
|
||||
|
@ -8,7 +8,7 @@ import type { ImageDTO, IPAdapterModelConfig } from 'services/api/types';
|
||||
|
||||
import type {
|
||||
CanvasIPAdapterState,
|
||||
CanvasV2State,
|
||||
CanvasState,
|
||||
CLIPVisionModelV2,
|
||||
EntityIdentifierPayload,
|
||||
IPMethodV2,
|
||||
@ -104,4 +104,4 @@ export const ipAdaptersReducers = {
|
||||
}
|
||||
entity.ipAdapter.beginEndStepPct = beginEndStepPct;
|
||||
},
|
||||
} satisfies SliceCaseReducers<CanvasV2State>;
|
||||
} satisfies SliceCaseReducers<CanvasState>;
|
||||
|
@ -4,7 +4,7 @@ import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import { selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import { merge } from 'lodash-es';
|
||||
|
||||
import type { CanvasControlLayerState, CanvasRasterLayerState, CanvasV2State, EntityIdentifierPayload } from './types';
|
||||
import type { CanvasControlLayerState, CanvasRasterLayerState, CanvasState, EntityIdentifierPayload } from './types';
|
||||
import { getEntityIdentifier, initialControlNet } from './types';
|
||||
|
||||
export const rasterLayersReducers = {
|
||||
@ -67,4 +67,4 @@ export const rasterLayersReducers = {
|
||||
payload: { ...payload, newId: getPrefixedId('control_layer') },
|
||||
}),
|
||||
},
|
||||
} satisfies SliceCaseReducers<CanvasV2State>;
|
||||
} satisfies SliceCaseReducers<CanvasState>;
|
||||
|
@ -3,7 +3,7 @@ import { deepClone } from 'common/util/deepClone';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import { selectEntity, selectRegionalGuidanceIPAdapter } from 'features/controlLayers/store/selectors';
|
||||
import type {
|
||||
CanvasV2State,
|
||||
CanvasState,
|
||||
CLIPVisionModelV2,
|
||||
EntityIdentifierPayload,
|
||||
FillStyle,
|
||||
@ -29,7 +29,7 @@ const DEFAULT_MASK_COLORS: RgbColor[] = [
|
||||
{ r: 161, g: 120, b: 214 }, // rgb(161, 120, 214)
|
||||
];
|
||||
|
||||
const getRGMaskFill = (state: CanvasV2State): RgbColor => {
|
||||
const getRGMaskFill = (state: CanvasState): RgbColor => {
|
||||
const lastFill = state.regions.entities.slice(-1)[0]?.fill.color;
|
||||
let i = DEFAULT_MASK_COLORS.findIndex((c) => isEqual(c, lastFill));
|
||||
if (i === -1) {
|
||||
@ -249,4 +249,4 @@ export const regionsReducers = {
|
||||
}
|
||||
ipAdapter.clipVisionModel = clipVisionModel;
|
||||
},
|
||||
} satisfies SliceCaseReducers<CanvasV2State>;
|
||||
} satisfies SliceCaseReducers<CanvasState>;
|
||||
|
@ -1,22 +1,23 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { selectParamsSlice } from 'features/controlLayers/store/paramsSlice';
|
||||
import type {
|
||||
CanvasControlLayerState,
|
||||
CanvasEntityIdentifier,
|
||||
CanvasEntityState,
|
||||
CanvasInpaintMaskState,
|
||||
CanvasRasterLayerState,
|
||||
CanvasRegionalGuidanceState,
|
||||
CanvasV2State,
|
||||
import {
|
||||
type CanvasControlLayerState,
|
||||
type CanvasEntityIdentifier,
|
||||
type CanvasEntityState,
|
||||
type CanvasInpaintMaskState,
|
||||
type CanvasRasterLayerState,
|
||||
type CanvasRegionalGuidanceState,
|
||||
type CanvasState,
|
||||
isDrawableEntityType,
|
||||
} from 'features/controlLayers/store/types';
|
||||
import { getOptimalDimension } from 'features/parameters/util/optimalDimension';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
/**
|
||||
* Selects the canvasV2 slice from the root state
|
||||
* Selects the canvas slice from the root state
|
||||
*/
|
||||
export const selectCanvasV2Slice = (state: RootState) => state.canvasV2;
|
||||
export const selectCanvasSlice = (state: RootState) => state.canvas.present;
|
||||
|
||||
/**
|
||||
* Selects the total canvas entity count:
|
||||
@ -28,13 +29,13 @@ export const selectCanvasV2Slice = (state: RootState) => state.canvasV2;
|
||||
*
|
||||
* It does not check for validity of the entities.
|
||||
*/
|
||||
export const selectEntityCount = createSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
export const selectEntityCount = createSelector(selectCanvasSlice, (canvas) => {
|
||||
return (
|
||||
canvasV2.regions.entities.length +
|
||||
canvasV2.ipAdapters.entities.length +
|
||||
canvasV2.rasterLayers.entities.length +
|
||||
canvasV2.controlLayers.entities.length +
|
||||
canvasV2.inpaintMasks.entities.length
|
||||
canvas.regions.entities.length +
|
||||
canvas.ipAdapters.entities.length +
|
||||
canvas.rasterLayers.entities.length +
|
||||
canvas.controlLayers.entities.length +
|
||||
canvas.inpaintMasks.entities.length
|
||||
);
|
||||
});
|
||||
|
||||
@ -46,11 +47,11 @@ export const selectOptimalDimension = createSelector(selectParamsSlice, (params)
|
||||
});
|
||||
|
||||
/**
|
||||
* Selects a single entity from the canvasV2 slice. If the entity identifier is narrowed to a specific type, the
|
||||
* Selects a single entity from the canvas 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,
|
||||
state: CanvasState,
|
||||
entityIdentifier: T
|
||||
): Extract<CanvasEntityState, T> | undefined {
|
||||
const { id, type } = entityIdentifier;
|
||||
@ -80,11 +81,11 @@ export function selectEntity<T extends CanvasEntityIdentifier>(
|
||||
}
|
||||
|
||||
/**
|
||||
* Selected an entity from the canvasV2 slice. If the entity is not found, an error is thrown.
|
||||
* Selected an entity from the canvas slice. If the entity is not found, an error is thrown.
|
||||
* Wrapper around {@link selectEntity}.
|
||||
*/
|
||||
export function selectEntityOrThrow<T extends CanvasEntityIdentifier>(
|
||||
state: CanvasV2State,
|
||||
state: CanvasState,
|
||||
entityIdentifier: T
|
||||
): Extract<CanvasEntityState, T> {
|
||||
const entity = selectEntity(state, entityIdentifier);
|
||||
@ -96,7 +97,7 @@ export function selectEntityOrThrow<T extends CanvasEntityIdentifier>(
|
||||
* Selects all entities of the given type.
|
||||
*/
|
||||
export function selectAllEntitiesOfType<T extends CanvasEntityState['type']>(
|
||||
state: CanvasV2State,
|
||||
state: CanvasState,
|
||||
type: T
|
||||
): Extract<CanvasEntityState, { type: T }>[] {
|
||||
let entities: CanvasEntityState[] = [];
|
||||
@ -126,7 +127,7 @@ export function selectAllEntitiesOfType<T extends CanvasEntityState['type']>(
|
||||
/**
|
||||
* Selects all entities, in the order they are displayed in the list.
|
||||
*/
|
||||
export function selectAllEntities(state: CanvasV2State): CanvasEntityState[] {
|
||||
export function selectAllEntities(state: CanvasState): CanvasEntityState[] {
|
||||
// These are in the same order as they are displayed in the list!
|
||||
return [
|
||||
...state.inpaintMasks.entities.toReversed(),
|
||||
@ -145,7 +146,7 @@ export function selectAllEntities(state: CanvasV2State): CanvasEntityState[] {
|
||||
* - Regional guidance
|
||||
*/
|
||||
export function selectAllRenderableEntities(
|
||||
state: CanvasV2State
|
||||
state: CanvasState
|
||||
): (CanvasRasterLayerState | CanvasControlLayerState | CanvasInpaintMaskState | CanvasRegionalGuidanceState)[] {
|
||||
return [
|
||||
...state.rasterLayers.entities,
|
||||
@ -159,7 +160,7 @@ export function selectAllRenderableEntities(
|
||||
* Selects the IP adapter for the specific Regional Guidance layer.
|
||||
*/
|
||||
export function selectRegionalGuidanceIPAdapter(
|
||||
state: CanvasV2State,
|
||||
state: CanvasState,
|
||||
entityIdentifier: CanvasEntityIdentifier<'regional_guidance'>,
|
||||
ipAdapterId: string
|
||||
) {
|
||||
@ -169,3 +170,19 @@ export function selectRegionalGuidanceIPAdapter(
|
||||
}
|
||||
return entity.ipAdapters.find((ipAdapter) => ipAdapter.id === ipAdapterId);
|
||||
}
|
||||
|
||||
export const selectSelectedEntityIdentifier = createSelector(
|
||||
selectCanvasSlice,
|
||||
(canvas) => canvas.selectedEntityIdentifier
|
||||
);
|
||||
|
||||
export const selectIsSelectedEntityDrawable = createSelector(
|
||||
selectSelectedEntityIdentifier,
|
||||
(selectedEntityIdentifier) => {
|
||||
if (!selectedEntityIdentifier) {
|
||||
return false;
|
||||
}
|
||||
return isDrawableEntityType(selectedEntityIdentifier.type);
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -692,7 +692,7 @@ export type StagingAreaImage = {
|
||||
|
||||
export type SessionMode = 'generate' | 'compose';
|
||||
|
||||
export type CanvasV2State = {
|
||||
export type CanvasState = {
|
||||
_version: 3;
|
||||
selectedEntityIdentifier: CanvasEntityIdentifier | null;
|
||||
inpaintMasks: {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { ConfirmationAlertDialog, Divider, Flex, FormControl, FormLabel, Switch, Text } from '@invoke-ai/ui-library';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import { imageDeletionConfirmed } from 'features/deleteImageModal/store/actions';
|
||||
import { getImageUsage, selectImageUsage } from 'features/deleteImageModal/store/selectors';
|
||||
import {
|
||||
@ -20,11 +20,11 @@ import { useTranslation } from 'react-i18next';
|
||||
import ImageUsageMessage from './ImageUsageMessage';
|
||||
|
||||
const selectImageUsages = createMemoizedSelector(
|
||||
[selectDeleteImageModalSlice, selectNodesSlice, selectCanvasV2Slice, selectImageUsage],
|
||||
(deleteImageModal, nodes, canvasV2, imagesUsage) => {
|
||||
[selectDeleteImageModalSlice, selectNodesSlice, selectCanvasSlice, selectImageUsage],
|
||||
(deleteImageModal, nodes, canvas, imagesUsage) => {
|
||||
const { imagesToDelete } = deleteImageModal;
|
||||
|
||||
const allImageUsage = (imagesToDelete ?? []).map(({ image_name }) => getImageUsage(nodes, canvasV2, image_name));
|
||||
const allImageUsage = (imagesToDelete ?? []).map(({ image_name }) => getImageUsage(nodes, canvas, image_name));
|
||||
|
||||
const imageUsageSummary: ImageUsage = {
|
||||
isLayerImage: some(allImageUsage, (i) => i.isLayerImage),
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import type { CanvasV2State } from 'features/controlLayers/store/types';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import type { CanvasState } from 'features/controlLayers/store/types';
|
||||
import { selectDeleteImageModalSlice } from 'features/deleteImageModal/store/slice';
|
||||
import { selectNodesSlice } from 'features/nodes/store/nodesSlice';
|
||||
import type { NodesState } from 'features/nodes/store/types';
|
||||
@ -10,14 +10,14 @@ import { some } from 'lodash-es';
|
||||
|
||||
import type { ImageUsage } from './types';
|
||||
// TODO(psyche): handle image deletion (canvas sessions?)
|
||||
export const getImageUsage = (nodes: NodesState, canvasV2: CanvasV2State, image_name: string) => {
|
||||
export const getImageUsage = (nodes: NodesState, canvas: CanvasState, image_name: string) => {
|
||||
const isNodesImage = nodes.nodes
|
||||
.filter(isInvocationNode)
|
||||
.some((node) =>
|
||||
some(node.data.inputs, (input) => isImageFieldInputInstance(input) && input.value?.image_name === image_name)
|
||||
);
|
||||
|
||||
const isIPAdapterImage = canvasV2.ipAdapters.entities.some(
|
||||
const isIPAdapterImage = canvas.ipAdapters.entities.some(
|
||||
({ ipAdapter }) => ipAdapter.image?.image_name === image_name
|
||||
);
|
||||
|
||||
@ -34,15 +34,15 @@ export const getImageUsage = (nodes: NodesState, canvasV2: CanvasV2State, image_
|
||||
export const selectImageUsage = createMemoizedSelector(
|
||||
selectDeleteImageModalSlice,
|
||||
selectNodesSlice,
|
||||
selectCanvasV2Slice,
|
||||
(deleteImageModal, nodes, canvasV2) => {
|
||||
selectCanvasSlice,
|
||||
(deleteImageModal, nodes, canvas) => {
|
||||
const { imagesToDelete } = deleteImageModal;
|
||||
|
||||
if (!imagesToDelete.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const imagesUsage = imagesToDelete.map((i) => getImageUsage(nodes, canvasV2, i.image_name));
|
||||
const imagesUsage = imagesToDelete.map((i) => getImageUsage(nodes, canvas, i.image_name));
|
||||
|
||||
return imagesUsage;
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ import {
|
||||
import { skipToken } from '@reduxjs/toolkit/query';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import ImageUsageMessage from 'features/deleteImageModal/components/ImageUsageMessage';
|
||||
import { getImageUsage } from 'features/deleteImageModal/store/selectors';
|
||||
import type { ImageUsage } from 'features/deleteImageModal/store/types';
|
||||
@ -39,8 +39,8 @@ const DeleteBoardModal = (props: Props) => {
|
||||
|
||||
const selectImageUsageSummary = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector([selectNodesSlice, selectCanvasV2Slice], (nodes, canvasV2) => {
|
||||
const allImageUsage = (boardImageNames ?? []).map((imageName) => getImageUsage(nodes, canvasV2, imageName));
|
||||
createMemoizedSelector([selectNodesSlice, selectCanvasSlice], (nodes, canvas) => {
|
||||
const allImageUsage = (boardImageNames ?? []).map((imageName) => getImageUsage(nodes, canvas, imageName));
|
||||
|
||||
const imageUsageSummary: ImageUsage = {
|
||||
isLayerImage: some(allImageUsage, (i) => i.isLayerImage),
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { getStore } from 'app/store/nanostores/store';
|
||||
import { bboxHeightChanged, bboxWidthChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { bboxHeightChanged, bboxWidthChanged } from 'features/controlLayers/store/canvasSlice';
|
||||
import { loraAllDeleted, loraRecalled } from 'features/controlLayers/store/lorasSlice';
|
||||
import {
|
||||
negativePrompt2Changed,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import type { CanvasV2State, Dimensions } from 'features/controlLayers/store/types';
|
||||
import type { CanvasState, Dimensions } from 'features/controlLayers/store/types';
|
||||
import type { Graph } from 'features/nodes/util/graph/generation/Graph';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import type { Invocation } from 'services/api/types';
|
||||
@ -13,7 +13,7 @@ export const addImageToImage = async (
|
||||
vaeSource: Invocation<'main_model_loader' | 'sdxl_model_loader' | 'seamless' | 'vae_loader'>,
|
||||
originalSize: Dimensions,
|
||||
scaledSize: Dimensions,
|
||||
bbox: CanvasV2State['bbox'],
|
||||
bbox: CanvasState['bbox'],
|
||||
denoising_start: number,
|
||||
fp32: boolean
|
||||
): Promise<Invocation<'img_resize' | 'l2i'>> => {
|
||||
|
@ -1,6 +1,9 @@
|
||||
import type { RootState } from 'app/store/store';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import { selectCanvasSessionSlice } from 'features/controlLayers/store/canvasSessionSlice';
|
||||
import { selectParamsSlice } from 'features/controlLayers/store/paramsSlice';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import type { Dimensions } from 'features/controlLayers/store/types';
|
||||
import type { Graph } from 'features/nodes/util/graph/generation/Graph';
|
||||
import { isEqual } from 'lodash-es';
|
||||
@ -21,8 +24,11 @@ export const addInpaint = async (
|
||||
): Promise<Invocation<'canvas_v2_mask_and_crop'>> => {
|
||||
denoise.denoising_start = denoising_start;
|
||||
|
||||
const { params, canvasV2, canvasSession } = state;
|
||||
const { bbox } = canvasV2;
|
||||
const params = selectParamsSlice(state);
|
||||
const canvasSession = selectCanvasSessionSlice(state);
|
||||
const canvas = selectCanvasSlice(state);
|
||||
|
||||
const { bbox } = canvas;
|
||||
const { mode } = canvasSession;
|
||||
|
||||
const initialImage = await manager.compositor.getCompositeRasterLayerImageDTO(bbox.rect);
|
||||
|
@ -1,6 +1,9 @@
|
||||
import type { RootState } from 'app/store/store';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import { selectCanvasSessionSlice } from 'features/controlLayers/store/canvasSessionSlice';
|
||||
import { selectParamsSlice } from 'features/controlLayers/store/paramsSlice';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import type { Dimensions } from 'features/controlLayers/store/types';
|
||||
import type { Graph } from 'features/nodes/util/graph/generation/Graph';
|
||||
import { getInfill } from 'features/nodes/util/graph/graphBuilderUtils';
|
||||
@ -22,8 +25,11 @@ export const addOutpaint = async (
|
||||
): Promise<Invocation<'canvas_v2_mask_and_crop'>> => {
|
||||
denoise.denoising_start = denoising_start;
|
||||
|
||||
const { params, canvasV2, canvasSession } = state;
|
||||
const { bbox } = canvasV2;
|
||||
const params = selectParamsSlice(state);
|
||||
const canvasSession = selectCanvasSessionSlice(state);
|
||||
const canvas = selectCanvasSlice(state);
|
||||
|
||||
const { bbox } = canvas;
|
||||
const { mode } = canvasSession;
|
||||
|
||||
const initialImage = await manager.compositor.getCompositeRasterLayerImageDTO(bbox.rect);
|
||||
|
@ -2,6 +2,10 @@ import { logger } from 'app/logging/logger';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import { selectCanvasSessionSlice } from 'features/controlLayers/store/canvasSessionSlice';
|
||||
import { selectCanvasSettingsSlice } from 'features/controlLayers/store/canvasSettingsSlice';
|
||||
import { selectParamsSlice } from 'features/controlLayers/store/paramsSlice';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import { fetchModelConfigWithTypeGuard } from 'features/metadata/util/modelFetchingHelpers';
|
||||
import { addControlNets, addT2IAdapters } from 'features/nodes/util/graph/generation/addControlAdapters';
|
||||
import { addImageToImage } from 'features/nodes/util/graph/generation/addImageToImage';
|
||||
@ -31,8 +35,12 @@ export const buildSD1Graph = async (
|
||||
const generationMode = manager.compositor.getGenerationMode();
|
||||
log.debug({ generationMode }, 'Building SD1/SD2 graph');
|
||||
|
||||
const { canvasV2, params, canvasSettings, canvasSession } = state;
|
||||
const { bbox } = canvasV2;
|
||||
const params = selectParamsSlice(state);
|
||||
const canvasSession = selectCanvasSessionSlice(state);
|
||||
const canvasSettings = selectCanvasSettingsSlice(state);
|
||||
const canvas = selectCanvasSlice(state);
|
||||
|
||||
const { bbox } = canvas;
|
||||
|
||||
const {
|
||||
model,
|
||||
@ -208,9 +216,9 @@ export const buildSD1Graph = async (
|
||||
});
|
||||
const controlNetResult = await addControlNets(
|
||||
manager,
|
||||
state.canvasV2.controlLayers.entities,
|
||||
canvas.controlLayers.entities,
|
||||
g,
|
||||
state.canvasV2.bbox.rect,
|
||||
canvas.bbox.rect,
|
||||
controlNetCollector,
|
||||
modelConfig.base
|
||||
);
|
||||
@ -226,9 +234,9 @@ export const buildSD1Graph = async (
|
||||
});
|
||||
const t2iAdapterResult = await addT2IAdapters(
|
||||
manager,
|
||||
state.canvasV2.controlLayers.entities,
|
||||
canvas.controlLayers.entities,
|
||||
g,
|
||||
state.canvasV2.bbox.rect,
|
||||
canvas.bbox.rect,
|
||||
t2iAdapterCollector,
|
||||
modelConfig.base
|
||||
);
|
||||
@ -242,13 +250,13 @@ export const buildSD1Graph = async (
|
||||
type: 'collect',
|
||||
id: getPrefixedId('ip_adapter_collector'),
|
||||
});
|
||||
const ipAdapterResult = addIPAdapters(state.canvasV2.ipAdapters.entities, g, ipAdapterCollector, modelConfig.base);
|
||||
const ipAdapterResult = addIPAdapters(canvas.ipAdapters.entities, g, ipAdapterCollector, modelConfig.base);
|
||||
|
||||
const regionsResult = await addRegions(
|
||||
manager,
|
||||
state.canvasV2.regions.entities,
|
||||
canvas.regions.entities,
|
||||
g,
|
||||
state.canvasV2.bbox.rect,
|
||||
canvas.bbox.rect,
|
||||
modelConfig.base,
|
||||
denoise,
|
||||
posCond,
|
||||
|
@ -2,6 +2,10 @@ import { logger } from 'app/logging/logger';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import { selectCanvasSessionSlice } from 'features/controlLayers/store/canvasSessionSlice';
|
||||
import { selectCanvasSettingsSlice } from 'features/controlLayers/store/canvasSettingsSlice';
|
||||
import { selectParamsSlice } from 'features/controlLayers/store/paramsSlice';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import { fetchModelConfigWithTypeGuard } from 'features/metadata/util/modelFetchingHelpers';
|
||||
import { addControlNets, addT2IAdapters } from 'features/nodes/util/graph/generation/addControlAdapters';
|
||||
import { addImageToImage } from 'features/nodes/util/graph/generation/addImageToImage';
|
||||
@ -31,8 +35,12 @@ export const buildSDXLGraph = async (
|
||||
const generationMode = manager.compositor.getGenerationMode();
|
||||
log.debug({ generationMode }, 'Building SDXL graph');
|
||||
|
||||
const { params, canvasV2, canvasSettings, canvasSession } = state;
|
||||
const { bbox } = canvasV2;
|
||||
const params = selectParamsSlice(state);
|
||||
const canvasSession = selectCanvasSessionSlice(state);
|
||||
const canvasSettings = selectCanvasSettingsSlice(state);
|
||||
const canvas = selectCanvasSlice(state);
|
||||
|
||||
const { bbox } = canvas;
|
||||
|
||||
const {
|
||||
model,
|
||||
@ -211,9 +219,9 @@ export const buildSDXLGraph = async (
|
||||
});
|
||||
const controlNetResult = await addControlNets(
|
||||
manager,
|
||||
state.canvasV2.controlLayers.entities,
|
||||
canvas.controlLayers.entities,
|
||||
g,
|
||||
state.canvasV2.bbox.rect,
|
||||
canvas.bbox.rect,
|
||||
controlNetCollector,
|
||||
modelConfig.base
|
||||
);
|
||||
@ -229,9 +237,9 @@ export const buildSDXLGraph = async (
|
||||
});
|
||||
const t2iAdapterResult = await addT2IAdapters(
|
||||
manager,
|
||||
state.canvasV2.controlLayers.entities,
|
||||
canvas.controlLayers.entities,
|
||||
g,
|
||||
state.canvasV2.bbox.rect,
|
||||
canvas.bbox.rect,
|
||||
t2iAdapterCollector,
|
||||
modelConfig.base
|
||||
);
|
||||
@ -245,13 +253,13 @@ export const buildSDXLGraph = async (
|
||||
type: 'collect',
|
||||
id: getPrefixedId('ip_adapter_collector'),
|
||||
});
|
||||
const ipAdapterResult = addIPAdapters(state.canvasV2.ipAdapters.entities, g, ipAdapterCollector, modelConfig.base);
|
||||
const ipAdapterResult = addIPAdapters(canvas.ipAdapters.entities, g, ipAdapterCollector, modelConfig.base);
|
||||
|
||||
const regionsResult = await addRegions(
|
||||
manager,
|
||||
state.canvasV2.regions.entities,
|
||||
canvas.regions.entities,
|
||||
g,
|
||||
state.canvasV2.bbox.rect,
|
||||
canvas.bbox.rect,
|
||||
modelConfig.base,
|
||||
denoise,
|
||||
posCond,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { RootState } from 'app/store/store';
|
||||
import type { ParamsState } from 'features/controlLayers/store/paramsSlice';
|
||||
import type { CanvasV2State } from 'features/controlLayers/store/types';
|
||||
import type { CanvasState } from 'features/controlLayers/store/types';
|
||||
import type { BoardField } from 'features/nodes/types/common';
|
||||
import type { Graph } from 'features/nodes/util/graph/generation/Graph';
|
||||
import { buildPresetModifiedPrompt } from 'features/stylePresets/hooks/usePresetModifiedPrompts';
|
||||
@ -62,7 +62,7 @@ export const getPresetModifiedPrompts = (
|
||||
};
|
||||
};
|
||||
|
||||
export const getSizes = (bboxState: CanvasV2State['bbox']) => {
|
||||
export const getSizes = (bboxState: CanvasState['bbox']) => {
|
||||
const originalSize = pick(bboxState.rect, 'width', 'height');
|
||||
const scaledSize = ['auto', 'manual'].includes(bboxState.scaleMethod) ? bboxState.scaledSize : originalSize;
|
||||
return { originalSize, scaledSize };
|
||||
|
@ -1,16 +1,20 @@
|
||||
import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui-library';
|
||||
import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
|
||||
import { bboxScaleMethodChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { bboxScaleMethodChanged } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import { isBoundingBoxScaleMethod } from 'features/controlLayers/store/types';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const selectScaleMethod = createSelector(selectCanvasSlice, (canvas) => canvas.bbox.scaleMethod);
|
||||
|
||||
const ParamScaleBeforeProcessing = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
const scaleMethod = useAppSelector((s) => s.canvasV2.bbox.scaleMethod);
|
||||
const scaleMethod = useAppSelector(selectScaleMethod);
|
||||
|
||||
const OPTIONS: ComboboxOption[] = useMemo(
|
||||
() => [
|
||||
|
@ -1,22 +1,26 @@
|
||||
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { bboxScaledSizeChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectOptimalDimension } from 'features/controlLayers/store/selectors';
|
||||
import { bboxScaledSizeChanged } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectCanvasSlice, selectOptimalDimension } from 'features/controlLayers/store/selectors';
|
||||
import { selectConfigSlice } from 'features/system/store/configSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const selectIsManual = createSelector(selectCanvasSlice, (canvas) => canvas.bbox.scaleMethod === 'manual');
|
||||
const selectScaledHeight = createSelector(selectCanvasSlice, (canvas) => canvas.bbox.scaledSize.height);
|
||||
const selectScaledBoundingBoxHeightConfig = createSelector(
|
||||
selectConfigSlice,
|
||||
(config) => config.sd.scaledBoundingBoxHeight
|
||||
);
|
||||
|
||||
const ParamScaledHeight = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const optimalDimension = useAppSelector(selectOptimalDimension);
|
||||
const isManual = useAppSelector((s) => s.canvasV2.bbox.scaleMethod === 'manual');
|
||||
const height = useAppSelector((s) => s.canvasV2.bbox.scaledSize.height);
|
||||
const sliderMin = useAppSelector((s) => s.config.sd.scaledBoundingBoxHeight.sliderMin);
|
||||
const sliderMax = useAppSelector((s) => s.config.sd.scaledBoundingBoxHeight.sliderMax);
|
||||
const numberInputMin = useAppSelector((s) => s.config.sd.scaledBoundingBoxHeight.numberInputMin);
|
||||
const numberInputMax = useAppSelector((s) => s.config.sd.scaledBoundingBoxHeight.numberInputMax);
|
||||
const coarseStep = useAppSelector((s) => s.config.sd.scaledBoundingBoxHeight.coarseStep);
|
||||
const fineStep = useAppSelector((s) => s.config.sd.scaledBoundingBoxHeight.fineStep);
|
||||
const isManual = useAppSelector(selectIsManual);
|
||||
const scaledHeight = useAppSelector(selectScaledHeight);
|
||||
const config = useAppSelector(selectScaledBoundingBoxHeightConfig);
|
||||
|
||||
const onChange = useCallback(
|
||||
(height: number) => {
|
||||
@ -29,21 +33,21 @@ const ParamScaledHeight = () => {
|
||||
<FormControl isDisabled={!isManual}>
|
||||
<FormLabel>{t('parameters.scaledHeight')}</FormLabel>
|
||||
<CompositeSlider
|
||||
min={sliderMin}
|
||||
max={sliderMax}
|
||||
step={coarseStep}
|
||||
fineStep={fineStep}
|
||||
value={height}
|
||||
min={config.sliderMin}
|
||||
max={config.sliderMax}
|
||||
step={config.coarseStep}
|
||||
fineStep={config.fineStep}
|
||||
value={scaledHeight}
|
||||
onChange={onChange}
|
||||
marks
|
||||
defaultValue={optimalDimension}
|
||||
/>
|
||||
<CompositeNumberInput
|
||||
min={numberInputMin}
|
||||
max={numberInputMax}
|
||||
step={coarseStep}
|
||||
fineStep={fineStep}
|
||||
value={height}
|
||||
min={config.numberInputMin}
|
||||
max={config.numberInputMax}
|
||||
step={config.coarseStep}
|
||||
fineStep={config.fineStep}
|
||||
value={scaledHeight}
|
||||
onChange={onChange}
|
||||
defaultValue={optimalDimension}
|
||||
/>
|
||||
|
@ -1,22 +1,26 @@
|
||||
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { bboxScaledSizeChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectOptimalDimension } from 'features/controlLayers/store/selectors';
|
||||
import { bboxScaledSizeChanged } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectCanvasSlice, selectOptimalDimension } from 'features/controlLayers/store/selectors';
|
||||
import { selectConfigSlice } from 'features/system/store/configSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const selectIsManual = createSelector(selectCanvasSlice, (canvas) => canvas.bbox.scaleMethod === 'manual');
|
||||
const selectScaledWidth = createSelector(selectCanvasSlice, (canvas) => canvas.bbox.scaledSize.width);
|
||||
const selectScaledBoundingBoxWidthConfig = createSelector(
|
||||
selectConfigSlice,
|
||||
(config) => config.sd.scaledBoundingBoxWidth
|
||||
);
|
||||
|
||||
const ParamScaledWidth = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const optimalDimension = useAppSelector(selectOptimalDimension);
|
||||
const isManual = useAppSelector((s) => s.canvasV2.bbox.scaleMethod === 'manual');
|
||||
const width = useAppSelector((s) => s.canvasV2.bbox.scaledSize.width);
|
||||
const sliderMin = useAppSelector((s) => s.config.sd.scaledBoundingBoxWidth.sliderMin);
|
||||
const sliderMax = useAppSelector((s) => s.config.sd.scaledBoundingBoxWidth.sliderMax);
|
||||
const numberInputMin = useAppSelector((s) => s.config.sd.scaledBoundingBoxWidth.numberInputMin);
|
||||
const numberInputMax = useAppSelector((s) => s.config.sd.scaledBoundingBoxWidth.numberInputMax);
|
||||
const coarseStep = useAppSelector((s) => s.config.sd.scaledBoundingBoxWidth.coarseStep);
|
||||
const fineStep = useAppSelector((s) => s.config.sd.scaledBoundingBoxWidth.fineStep);
|
||||
const isManual = useAppSelector(selectIsManual);
|
||||
const scaledWidth = useAppSelector(selectScaledWidth);
|
||||
const config = useAppSelector(selectScaledBoundingBoxWidthConfig);
|
||||
const onChange = useCallback(
|
||||
(width: number) => {
|
||||
dispatch(bboxScaledSizeChanged({ width }));
|
||||
@ -28,21 +32,21 @@ const ParamScaledWidth = () => {
|
||||
<FormControl isDisabled={!isManual}>
|
||||
<FormLabel>{t('parameters.scaledWidth')}</FormLabel>
|
||||
<CompositeSlider
|
||||
min={sliderMin}
|
||||
max={sliderMax}
|
||||
step={coarseStep}
|
||||
fineStep={fineStep}
|
||||
value={width}
|
||||
min={config.sliderMin}
|
||||
max={config.sliderMax}
|
||||
step={config.coarseStep}
|
||||
fineStep={config.fineStep}
|
||||
value={scaledWidth}
|
||||
onChange={onChange}
|
||||
defaultValue={optimalDimension}
|
||||
marks
|
||||
/>
|
||||
<CompositeNumberInput
|
||||
min={numberInputMin}
|
||||
max={numberInputMax}
|
||||
step={coarseStep}
|
||||
fineStep={fineStep}
|
||||
value={width}
|
||||
min={config.numberInputMin}
|
||||
max={config.numberInputMax}
|
||||
step={config.coarseStep}
|
||||
fineStep={config.fineStep}
|
||||
value={scaledWidth}
|
||||
onChange={onChange}
|
||||
defaultValue={optimalDimension}
|
||||
/>
|
||||
|
@ -1,22 +1,22 @@
|
||||
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
|
||||
import { bboxHeightChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectOptimalDimension } from 'features/controlLayers/store/selectors';
|
||||
import { bboxHeightChanged } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectCanvasSlice, selectOptimalDimension } from 'features/controlLayers/store/selectors';
|
||||
import { selectConfigSlice } from 'features/system/store/configSlice';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const selectHeight = createSelector(selectCanvasSlice, (canvas) => canvas.bbox.rect.height);
|
||||
const selectHeightConfig = createSelector(selectConfigSlice, (config) => config.sd.height);
|
||||
|
||||
export const ParamHeight = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const optimalDimension = useAppSelector(selectOptimalDimension);
|
||||
const height = useAppSelector((s) => s.canvasV2.bbox.rect.height);
|
||||
const sliderMin = useAppSelector((s) => s.config.sd.height.sliderMin);
|
||||
const sliderMax = useAppSelector((s) => s.config.sd.height.sliderMax);
|
||||
const numberInputMin = useAppSelector((s) => s.config.sd.height.numberInputMin);
|
||||
const numberInputMax = useAppSelector((s) => s.config.sd.height.numberInputMax);
|
||||
const coarseStep = useAppSelector((s) => s.config.sd.height.coarseStep);
|
||||
const fineStep = useAppSelector((s) => s.config.sd.height.fineStep);
|
||||
const height = useAppSelector(selectHeight);
|
||||
const config = useAppSelector(selectHeightConfig);
|
||||
|
||||
const onChange = useCallback(
|
||||
(v: number) => {
|
||||
@ -25,7 +25,10 @@ export const ParamHeight = memo(() => {
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const marks = useMemo(() => [sliderMin, optimalDimension, sliderMax], [sliderMin, optimalDimension, sliderMax]);
|
||||
const marks = useMemo(
|
||||
() => [config.sliderMin, optimalDimension, config.sliderMax],
|
||||
[config.sliderMin, config.sliderMax, optimalDimension]
|
||||
);
|
||||
|
||||
return (
|
||||
<FormControl>
|
||||
@ -36,20 +39,20 @@ export const ParamHeight = memo(() => {
|
||||
value={height}
|
||||
defaultValue={optimalDimension}
|
||||
onChange={onChange}
|
||||
min={sliderMin}
|
||||
max={sliderMax}
|
||||
step={coarseStep}
|
||||
fineStep={fineStep}
|
||||
min={config.sliderMin}
|
||||
max={config.sliderMax}
|
||||
step={config.coarseStep}
|
||||
fineStep={config.fineStep}
|
||||
marks={marks}
|
||||
/>
|
||||
<CompositeNumberInput
|
||||
value={height}
|
||||
defaultValue={optimalDimension}
|
||||
onChange={onChange}
|
||||
min={numberInputMin}
|
||||
max={numberInputMax}
|
||||
step={coarseStep}
|
||||
fineStep={fineStep}
|
||||
min={config.numberInputMin}
|
||||
max={config.numberInputMax}
|
||||
step={config.coarseStep}
|
||||
fineStep={config.fineStep}
|
||||
/>
|
||||
</FormControl>
|
||||
);
|
||||
|
@ -1,22 +1,22 @@
|
||||
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
|
||||
import { bboxWidthChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectOptimalDimension } from 'features/controlLayers/store/selectors';
|
||||
import { bboxWidthChanged } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectCanvasSlice, selectOptimalDimension } from 'features/controlLayers/store/selectors';
|
||||
import { selectConfigSlice } from 'features/system/store/configSlice';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const selectWidth = createSelector(selectCanvasSlice, (canvas) => canvas.bbox.rect.width);
|
||||
const selectWidthConfig = createSelector(selectConfigSlice, (config) => config.sd.width);
|
||||
|
||||
export const ParamWidth = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const width = useAppSelector((s) => s.canvasV2.bbox.rect.width);
|
||||
const width = useAppSelector(selectWidth);
|
||||
const optimalDimension = useAppSelector(selectOptimalDimension);
|
||||
const sliderMin = useAppSelector((s) => s.config.sd.width.sliderMin);
|
||||
const sliderMax = useAppSelector((s) => s.config.sd.width.sliderMax);
|
||||
const numberInputMin = useAppSelector((s) => s.config.sd.width.numberInputMin);
|
||||
const numberInputMax = useAppSelector((s) => s.config.sd.width.numberInputMax);
|
||||
const coarseStep = useAppSelector((s) => s.config.sd.width.coarseStep);
|
||||
const fineStep = useAppSelector((s) => s.config.sd.width.fineStep);
|
||||
const config = useAppSelector(selectWidthConfig);
|
||||
|
||||
const onChange = useCallback(
|
||||
(v: number) => {
|
||||
@ -25,7 +25,10 @@ export const ParamWidth = memo(() => {
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const marks = useMemo(() => [sliderMin, optimalDimension, sliderMax], [sliderMin, optimalDimension, sliderMax]);
|
||||
const marks = useMemo(
|
||||
() => [config.sliderMin, optimalDimension, config.sliderMax],
|
||||
[config.sliderMax, config.sliderMin, optimalDimension]
|
||||
);
|
||||
|
||||
return (
|
||||
<FormControl>
|
||||
@ -36,20 +39,20 @@ export const ParamWidth = memo(() => {
|
||||
value={width}
|
||||
onChange={onChange}
|
||||
defaultValue={optimalDimension}
|
||||
min={sliderMin}
|
||||
max={sliderMax}
|
||||
step={coarseStep}
|
||||
fineStep={fineStep}
|
||||
min={config.sliderMin}
|
||||
max={config.sliderMax}
|
||||
step={config.coarseStep}
|
||||
fineStep={config.fineStep}
|
||||
marks={marks}
|
||||
/>
|
||||
<CompositeNumberInput
|
||||
value={width}
|
||||
onChange={onChange}
|
||||
defaultValue={optimalDimension}
|
||||
min={numberInputMin}
|
||||
max={numberInputMax}
|
||||
step={coarseStep}
|
||||
fineStep={fineStep}
|
||||
min={config.numberInputMin}
|
||||
max={config.numberInputMax}
|
||||
step={config.coarseStep}
|
||||
fineStep={config.fineStep}
|
||||
/>
|
||||
</FormControl>
|
||||
);
|
||||
|
@ -1,18 +1,22 @@
|
||||
import type { ComboboxOption, SystemStyleObject } from '@invoke-ai/ui-library';
|
||||
import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import type { SingleValue } from 'chakra-react-select';
|
||||
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
|
||||
import { bboxAspectRatioIdChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { bboxAspectRatioIdChanged } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import { ASPECT_RATIO_OPTIONS } from 'features/parameters/components/DocumentSize/constants';
|
||||
import { isAspectRatioID } from 'features/parameters/components/DocumentSize/types';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const selectAspectRatioID = createSelector(selectCanvasSlice, (canvas) => canvas.bbox.aspectRatio.id);
|
||||
|
||||
export const AspectRatioSelect = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const id = useAppSelector((s) => s.canvasV2.bbox.aspectRatio.id);
|
||||
const id = useAppSelector(selectAspectRatioID);
|
||||
|
||||
const onChange = useCallback(
|
||||
(v: SingleValue<ComboboxOption>) => {
|
||||
|
@ -1,14 +1,18 @@
|
||||
import { IconButton } from '@invoke-ai/ui-library';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { bboxAspectRatioLockToggled } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { bboxAspectRatioLockToggled } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiLockSimpleFill, PiLockSimpleOpenBold } from 'react-icons/pi';
|
||||
|
||||
const selectAspectRatioIsLocked = createSelector(selectCanvasSlice, (canvas) => canvas.bbox.aspectRatio.isLocked);
|
||||
|
||||
export const LockAspectRatioButton = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const isLocked = useAppSelector((s) => s.canvasV2.bbox.aspectRatio.isLocked);
|
||||
const isLocked = useAppSelector(selectAspectRatioIsLocked);
|
||||
const onClick = useCallback(() => {
|
||||
dispatch(bboxAspectRatioLockToggled());
|
||||
}, [dispatch]);
|
||||
|
@ -1,17 +1,21 @@
|
||||
import { IconButton } from '@invoke-ai/ui-library';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { bboxSizeOptimized } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectOptimalDimension } from 'features/controlLayers/store/selectors';
|
||||
import { bboxSizeOptimized } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectCanvasSlice, selectOptimalDimension } from 'features/controlLayers/store/selectors';
|
||||
import { getIsSizeTooLarge, getIsSizeTooSmall } from 'features/parameters/util/optimalDimension';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { RiSparklingFill } from 'react-icons/ri';
|
||||
|
||||
const selectWidth = createSelector(selectCanvasSlice, (canvas) => canvas.bbox.rect.width);
|
||||
const selectHeight = createSelector(selectCanvasSlice, (canvas) => canvas.bbox.rect.height);
|
||||
|
||||
export const SetOptimalSizeButton = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const width = useAppSelector((s) => s.canvasV2.bbox.rect.width);
|
||||
const height = useAppSelector((s) => s.canvasV2.bbox.rect.height);
|
||||
const width = useAppSelector(selectWidth);
|
||||
const height = useAppSelector(selectHeight);
|
||||
const optimalDimension = useAppSelector(selectOptimalDimension);
|
||||
const isSizeTooSmall = useMemo(
|
||||
() => getIsSizeTooSmall(width, height, optimalDimension),
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { IconButton } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { bboxDimensionsSwapped } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { bboxDimensionsSwapped } from 'features/controlLayers/store/canvasSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiArrowsDownUpBold } from 'react-icons/pi';
|
||||
|
@ -3,7 +3,7 @@ import { Expander, Flex, FormControlGroup, StandaloneAccordion } from '@invoke-a
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectParamsSlice } from 'features/controlLayers/store/paramsSlice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import { HrfSettings } from 'features/hrf/components/HrfSettings';
|
||||
import { selectHrfSlice } from 'features/hrf/store/hrfSlice';
|
||||
import ParamScaleBeforeProcessing from 'features/parameters/components/Canvas/InfillAndScaling/ParamScaleBeforeProcessing';
|
||||
@ -20,15 +20,15 @@ import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const selector = createMemoizedSelector(
|
||||
[selectHrfSlice, selectCanvasV2Slice, selectParamsSlice],
|
||||
(hrf, canvasV2, params) => {
|
||||
[selectHrfSlice, selectCanvasSlice, selectParamsSlice],
|
||||
(hrf, canvas, params) => {
|
||||
const { shouldRandomizeSeed, model } = params;
|
||||
const { hrfEnabled } = hrf;
|
||||
const badges: string[] = [];
|
||||
const isSDXL = model?.base === 'sdxl';
|
||||
|
||||
const { aspectRatio } = canvasV2.bbox;
|
||||
const { width, height } = canvasV2.bbox.rect;
|
||||
const { aspectRatio } = canvas.bbox;
|
||||
const { width, height } = canvas.bbox.rect;
|
||||
|
||||
badges.push(`${width}×${height}`);
|
||||
badges.push(aspectRatio.id);
|
||||
|
Loading…
x
Reference in New Issue
Block a user