refactor(ui): update components & logic to use new unified slice

This commit is contained in:
psychedelicious 2024-06-16 00:08:00 +10:00
parent bfa496e37f
commit 02ad7a0f93
100 changed files with 176 additions and 727 deletions

View File

@ -1,5 +1,5 @@
import { PropsWithChildren, memo, useEffect } from 'react';
import { modelChanged } from '../src/features/parameters/store/generationSlice';
import { modelChanged } from '../src/features/controlLayers/store/canvasV2Slice';
import { useAppDispatch } from '../src/app/store/storeHooks';
import { useGlobalModifiersInit } from '@invoke-ai/ui-library';
/**

View File

@ -1,5 +1,5 @@
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
import { setInfillMethod } from 'features/parameters/store/generationSlice';
import { setInfillMethod } from 'features/canvas/store/canvasSlice';
import { shouldUseNSFWCheckerChanged, shouldUseWatermarkerChanged } from 'features/system/store/systemSlice';
import { appInfoApi } from 'services/api/endpoints/appInfo';

View File

@ -13,7 +13,7 @@ export const addEnqueueRequestedLinear = (startAppListening: AppStartListening)
effect: async (action, { getState, dispatch }) => {
const state = getState();
const { shouldShowProgressInViewer } = state.ui;
const model = state.generation.model;
const model = state.canvasV2.params.model;
const { prepend } = action.payload;
let graph;

View File

@ -29,7 +29,7 @@ export const addEnqueueRequestedNodes = (startAppListening: AppStartListening) =
batch: {
graph,
workflow: builtWorkflow,
runs: state.generation.iterations,
runs: state.canvasV2.params.iterations,
},
prepend: action.payload.prepend,
};

View File

@ -6,7 +6,7 @@ import {
} from 'features/controlAdapters/store/controlAdaptersSlice';
import { loraRemoved } from 'features/lora/store/loraSlice';
import { modelSelected } from 'features/parameters/store/actions';
import { modelChanged, vaeSelected } from 'features/parameters/store/generationSlice';
import { modelChanged, vaeSelected } from 'features/canvas/store/canvasSlice';
import { zParameterModel } from 'features/parameters/types/parameterSchemas';
import { toast } from 'features/toast/toast';
import { t } from 'i18next';
@ -29,7 +29,7 @@ export const addModelSelectedListener = (startAppListening: AppStartListening) =
const newModel = result.data;
const newBaseModel = newModel.base;
const didBaseModelChange = state.generation.model?.base !== newBaseModel;
const didBaseModelChange = state.canvasV2.params.model?.base !== newBaseModel;
if (didBaseModelChange) {
// we may need to reset some incompatible submodels
@ -44,7 +44,7 @@ export const addModelSelectedListener = (startAppListening: AppStartListening) =
});
// handle incompatible vae
const { vae } = state.generation;
const { vae } = state.canvasV2.params;
if (vae && vae.base !== newBaseModel) {
dispatch(vaeSelected(null));
modelsCleared += 1;
@ -70,7 +70,7 @@ export const addModelSelectedListener = (startAppListening: AppStartListening) =
}
}
dispatch(modelChanged(newModel, state.generation.model));
dispatch(modelChanged(newModel, state.canvasV2.params.model));
},
});
};

View File

@ -3,17 +3,18 @@ import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'
import type { AppDispatch, RootState } from 'app/store/store';
import type { JSONObject } from 'common/types';
import {
controlAdapterModelCleared,
selectControlAdapterAll,
} from 'features/controlAdapters/store/controlAdaptersSlice';
import { heightChanged, widthChanged } from 'features/controlLayers/store/canvasV2Slice';
caModelChanged,
heightChanged,
modelChanged,
refinerModelChanged,
vaeSelected,
widthChanged,
} from 'features/controlLayers/store/canvasV2Slice';
import { loraRemoved } from 'features/lora/store/loraSlice';
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
import { modelChanged, vaeSelected } from 'features/parameters/store/generationSlice';
import { postProcessingModelChanged, upscaleModelChanged } from 'features/parameters/store/upscaleSlice';
import { zParameterModel, zParameterVAEModel } from 'features/parameters/types/parameterSchemas';
import { getIsSizeOptimal, getOptimalDimension } from 'features/parameters/util/optimalDimension';
import { refinerModelChanged } from 'features/sdxl/store/sdxlSlice';
import { forEach } from 'lodash-es';
import type { Logger } from 'roarr';
import { modelConfigsAdapterSelectors, modelsApi } from 'services/api/endpoints/models';
@ -55,11 +56,11 @@ type ModelHandler = (
) => undefined;
const handleMainModels: ModelHandler = (models, state, dispatch, log) => {
const currentModel = state.generation.model;
const currentModel = state.canvasV2.params.model;
const mainModels = models.filter(isNonRefinerMainModelConfig);
if (mainModels.length === 0) {
// No models loaded at all
dispatch(modelChanged(null));
dispatch(modelChanged({ model: null }));
return;
}
@ -74,16 +75,10 @@ const handleMainModels: ModelHandler = (models, state, dispatch, log) => {
if (defaultModelInList) {
const result = zParameterModel.safeParse(defaultModelInList);
if (result.success) {
dispatch(modelChanged(defaultModelInList, currentModel));
dispatch(modelChanged({ model: defaultModelInList, previousModel: currentModel ?? undefined }));
const optimalDimension = getOptimalDimension(defaultModelInList);
if (
getIsSizeOptimal(
state.canvasV2.document.width,
state.canvasV2.document.height,
optimalDimension
)
) {
if (getIsSizeOptimal(state.canvasV2.document.width, state.canvasV2.document.height, optimalDimension)) {
return;
}
const { width, height } = calculateNewSize(
@ -104,11 +99,11 @@ const handleMainModels: ModelHandler = (models, state, dispatch, log) => {
return;
}
dispatch(modelChanged(result.data, currentModel));
dispatch(modelChanged({ model: result.data, previousModel: currentModel ?? undefined }));
};
const handleRefinerModels: ModelHandler = (models, state, dispatch, _log) => {
const currentRefinerModel = state.sdxl.refinerModel;
const currentRefinerModel = state.canvasV2.params.refinerModel;
const refinerModels = models.filter(isRefinerMainModelModelConfig);
if (models.length === 0) {
// No models loaded at all
@ -127,7 +122,7 @@ const handleRefinerModels: ModelHandler = (models, state, dispatch, _log) => {
};
const handleVAEModels: ModelHandler = (models, state, dispatch, log) => {
const currentVae = state.generation.vae;
const currentVae = state.canvasV2.params.vae;
if (currentVae === null) {
// null is a valid VAE! it means "use the default with the main model"
@ -174,14 +169,14 @@ const handleLoRAModels: ModelHandler = (models, state, dispatch, _log) => {
};
const handleControlAdapterModels: ModelHandler = (models, state, dispatch, _log) => {
selectControlAdapterAll(state.controlAdapters).forEach((ca) => {
state.canvasV2.controlAdapters.forEach((ca) => {
const isModelAvailable = models.some((m) => m.key === ca.model?.key);
if (isModelAvailable) {
return;
}
dispatch(controlAdapterModelCleared({ id: ca.id }));
dispatch(caModelChanged({ id: ca.id, modelConfig: null }));
});
};

View File

@ -8,7 +8,7 @@ import {
setSteps,
vaePrecisionChanged,
vaeSelected,
} from 'features/parameters/store/generationSlice';
} from 'features/canvas/store/canvasSlice';
import {
isParameterCFGRescaleMultiplier,
isParameterCFGScale,
@ -30,7 +30,7 @@ export const addSetDefaultSettingsListener = (startAppListening: AppStartListeni
effect: async (action, { dispatch, getState }) => {
const state = getState();
const currentModel = state.generation.model;
const currentModel = state.canvasV2.params.model;
if (!currentModel) {
return;

View File

@ -32,7 +32,7 @@ export const useGroupedModelCombobox = <T extends AnyModelConfig>(
arg: UseGroupedModelComboboxArg<T>
): UseGroupedModelComboboxReturn => {
const { t } = useTranslation();
const base_model = useAppSelector((s) => s.generation.model?.base ?? 'sdxl');
const base_model = useAppSelector((s) => s.canvasV2.params.model?.base ?? 'sdxl');
const { modelConfigs, selectedModel, getIsDisabled, onChange, isLoading, groupByType = false } = arg;
const options = useMemo<GroupBase<ComboboxOption>[]>(() => {
if (!modelConfigs) {

View File

@ -9,7 +9,6 @@ import { $templates, selectNodesSlice } from 'features/nodes/store/nodesSlice';
import type { Templates } from 'features/nodes/store/types';
import { selectWorkflowSettingsSlice } from 'features/nodes/store/workflowSettingsSlice';
import { isInvocationNode } from 'features/nodes/types/invocation';
import { selectGenerationSlice } from 'features/parameters/store/generationSlice';
import { selectUpscalelice } from 'features/parameters/store/upscaleSlice';
import { selectConfigSlice } from 'features/system/store/configSlice';
import { selectSystemSlice } from 'features/system/store/systemSlice';
@ -30,7 +29,6 @@ const LAYER_TYPE_TO_TKEY: Record<CanvasEntity['type'], string> = {
const createSelector = (templates: Templates) =>
createMemoizedSelector(
[
selectGenerationSlice,
selectSystemSlice,
selectNodesSlice,
selectWorkflowSettingsSlice,
@ -40,9 +38,9 @@ const createSelector = (templates: Templates) =>
selectConfigSlice,
activeTabNameSelector,
],
(generation, system, nodes, workflowSettings, dynamicPrompts, canvasV2, upscale, config, activeTabName) => {
const { model, positivePrompt } = generation;
(system, nodes, workflowSettings, dynamicPrompts, canvasV2, upscale, config, activeTabName) => {
const { bbox } = canvasV2;
const { model, positivePrompt } = canvasV2.params;
const { isConnected } = system;

View File

@ -17,8 +17,8 @@ import {
setShouldSnapToGrid,
} from 'features/canvas/store/canvasSlice';
import { CANVAS_GRID_SIZE_COARSE, CANVAS_GRID_SIZE_FINE } from 'features/canvas/store/constants';
import { selectOptimalDimension } from 'features/controlLayers/store/selectors';
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
import type Konva from 'konva';
import type { GroupConfig } from 'konva/lib/Group';
import type { KonvaEventObject } from 'konva/lib/Node';

View File

@ -3,6 +3,7 @@ import { createSlice } from '@reduxjs/toolkit';
import type { PersistConfig, RootState } from 'app/store/store';
import { deepClone } from 'common/util/deepClone';
import { roundDownToMultiple, roundToMultiple } from 'common/util/roundDownToMultiple';
import { modelChanged } from 'features/canvas/store/canvasSlice';
import calculateCoordinates from 'features/canvas/util/calculateCoordinates';
import calculateScale from 'features/canvas/util/calculateScale';
import { STAGE_PADDING_PERCENTAGE } from 'features/canvas/util/constants';
@ -11,7 +12,6 @@ import getScaledBoundingBoxDimensions from 'features/canvas/util/getScaledBoundi
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
import { initialAspectRatioState } from 'features/parameters/components/ImageSize/constants';
import type { AspectRatioState } from 'features/parameters/components/ImageSize/types';
import { modelChanged } from 'features/parameters/store/generationSlice';
import type { PayloadActionWithOptimalDimension } from 'features/parameters/store/types';
import { getIsSizeOptimal, getOptimalDimension } from 'features/parameters/util/optimalDimension';
import type { IRect, Vector2d } from 'konva/lib/types';

View File

@ -4,10 +4,10 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIDndImage from 'common/components/IAIDndImage';
import IAIDndImageIcon from 'common/components/IAIDndImageIcon';
import { heightChanged, widthChanged } from 'features/controlLayers/store/canvasV2Slice';
import { selectOptimalDimension } from 'features/controlLayers/store/selectors';
import type { ControlAdapterData } from 'features/controlLayers/store/types';
import type { ImageDraggableData, TypesafeDroppableData } from 'features/dnd/types';
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { PiArrowCounterClockwiseBold, PiFloppyDiskBold, PiRulerBold } from 'react-icons/pi';

View File

@ -13,7 +13,7 @@ type Props = {
export const CAModelCombobox = memo(({ modelKey, onChange: onChangeModel }: Props) => {
const { t } = useTranslation();
const currentBaseModel = useAppSelector((s) => s.generation.model?.base);
const currentBaseModel = useAppSelector((s) => s.canvasV2.params.model?.base);
const [modelConfigs, { isLoading }] = useControlNetAndT2IAdapterModels();
const selectedModel = useMemo(() => modelConfigs.find((m) => m.key === modelKey), [modelConfigs, modelKey]);

View File

@ -7,7 +7,7 @@ import { heightChanged, widthChanged } from 'features/controlLayers/store/canvas
import type { ImageWithDims } from 'features/controlLayers/store/types';
import type { ImageDraggableData, TypesafeDroppableData } from 'features/dnd/types';
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
import { selectOptimalDimension } from 'features/controlLayers/store/selectors';
import { memo, useCallback, useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiArrowCounterClockwiseBold, PiRulerBold } from 'react-icons/pi';

View File

@ -24,7 +24,7 @@ type Props = {
export const IPAModelCombobox = memo(({ modelKey, onChangeModel, clipVisionModel, onChangeCLIPVisionModel }: Props) => {
const { t } = useTranslation();
const currentBaseModel = useAppSelector((s) => s.generation.model?.base);
const currentBaseModel = useAppSelector((s) => s.canvasV2.params.model?.base);
const [modelConfigs, { isLoading }] = useIPAdapterModels();
const selectedModel = useMemo(() => modelConfigs.find((m) => m.key === modelKey), [modelConfigs, modelKey]);

View File

@ -15,7 +15,7 @@ import { v4 as uuidv4 } from 'uuid';
export const useAddCALayer = () => {
const dispatch = useAppDispatch();
const baseModel = useAppSelector((s) => s.generation.model?.base);
const baseModel = useAppSelector((s) => s.canvasV2.params.model?.base);
const [modelConfigs] = useControlNetAndT2IAdapterModels();
const model: ControlNetModelConfig | T2IAdapterModelConfig | null = useMemo(() => {
// prefer to use a model that matches the base model
@ -48,7 +48,7 @@ export const useAddCALayer = () => {
export const useAddIPALayer = () => {
const dispatch = useAppDispatch();
const baseModel = useAppSelector((s) => s.generation.model?.base);
const baseModel = useAppSelector((s) => s.canvasV2.params.model?.base);
const [modelConfigs] = useIPAdapterModels();
const model: IPAdapterModelConfig | null = useMemo(() => {
// prefer to use a model that matches the base model
@ -72,7 +72,7 @@ export const useAddIPALayer = () => {
export const useAddIPAdapterToRGLayer = (id: string) => {
const dispatch = useAppDispatch();
const baseModel = useAppSelector((s) => s.generation.model?.base);
const baseModel = useAppSelector((s) => s.canvasV2.params.model?.base);
const [modelConfigs] = useIPAdapterModels();
const model: IPAdapterModelConfig | null = useMemo(() => {
// prefer to use a model that matches the base model

View File

@ -1,8 +1,13 @@
import { createSelector } from '@reduxjs/toolkit';
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
import { getOptimalDimension } from 'features/parameters/util/optimalDimension';
export const selectEntityCount = createSelector(selectCanvasSlice, (canvasV2) => {
return (
canvasV2.regions.length + canvasV2.controlAdapters.length + canvasV2.ipAdapters.length + canvasV2.layers.length
);
});
export const selectOptimalDimension = createSelector(selectCanvasSlice, (canvasV2) => {
return getOptimalDimension(canvasV2.params.model);
});

View File

@ -7,6 +7,7 @@ import { useDownloadImage } from 'common/hooks/useDownloadImage';
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
import { imagesToChangeSelected, isModalOpenChanged } from 'features/changeBoardModal/store/slice';
import { iiLayerAdded } from 'features/controlLayers/store/canvasV2Slice';
import { selectOptimalDimension } from 'features/controlLayers/store/selectors';
import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice';
import { useImageActions } from 'features/gallery/hooks/useImageActions';
import { sentImageToCanvas, sentImageToImg2Img } from 'features/gallery/store/actions';

View File

@ -17,7 +17,7 @@ const LoRASelect = () => {
const [modelConfigs, { isLoading }] = useLoRAModels();
const { t } = useTranslation();
const addedLoRAs = useAppSelector(selectAddedLoRAs);
const currentBaseModel = useAppSelector((s) => s.generation.model?.base);
const currentBaseModel = useAppSelector((s) => s.canvasV2.params.model?.base);
const getIsDisabled = (lora: LoRAModelConfig): boolean => {
const isCompatible = currentBaseModel === lora.base;

View File

@ -40,7 +40,7 @@ import {
setSeed,
setSteps,
vaeSelected,
} from 'features/parameters/store/generationSlice';
} from 'features/canvas/store/canvasSlice';
import type {
ParameterCFGRescaleMultiplier,
ParameterCFGScale,

View File

@ -9,7 +9,7 @@ import { getHasMetadata, removeMetadata } from './canvas/metadata';
import { CANVAS_COHERENCE_NOISE, METADATA, NOISE, POSITIVE_CONDITIONING } from './constants';
export const prepareLinearUIBatch = (state: RootState, graph: NonNullableGraph, prepend: boolean): BatchConfig => {
const { iterations, model, shouldRandomizeSeed, seed } = state.generation;
const { iterations, model, shouldRandomizeSeed, seed } = state.canvasV2.params;
const { shouldConcatPrompts } = state.canvasV2;
const { prompts, seedBehaviour } = state.dynamicPrompts;

View File

@ -17,7 +17,7 @@ export const addControlNetToLinearGraph = async (
const controlNets = selectValidControlNets(state.controlAdapters).filter(
({ model, processedControlImage, processorType, controlImage, isEnabled }) => {
const hasModel = Boolean(model);
const doesBaseMatch = model?.base === state.generation.model?.base;
const doesBaseMatch = model?.base === state.canvasV2.params.model?.base;
const hasControlImage = (processedControlImage && processorType !== 'none') || controlImage;
return isEnabled && hasModel && doesBaseMatch && hasControlImage;

View File

@ -19,7 +19,7 @@ export const addIPAdapterToLinearGraph = async (
const ipAdapters = selectValidIPAdapters(state.controlAdapters).filter(({ model, controlImage, isEnabled }) => {
const hasModel = Boolean(model);
const doesBaseMatch = model?.base === state.generation.model?.base;
const doesBaseMatch = model?.base === state.canvasV2.params.model?.base;
const hasControlImage = controlImage;
return isEnabled && hasModel && doesBaseMatch && hasControlImage;
});

View File

@ -34,13 +34,13 @@ export const addSDXLRefinerToGraph = async (
refinerScheduler,
refinerCFGScale,
refinerStart,
} = state.sdxl;
} = state.canvasV2.params;
if (!refinerModel) {
return;
}
const { seamlessXAxis, seamlessYAxis } = state.generation;
const { seamlessXAxis, seamlessYAxis } = state.canvasV2.params;
const { boundingBoxScaleMethod } = state.canvas;
const isUsingScaledDimensions = ['auto', 'manual'].includes(boundingBoxScaleMethod);

View File

@ -19,7 +19,7 @@ export const addSeamlessToLinearGraph = (
modelLoaderNodeId: string
): void => {
// Remove Existing UNet Connections
const { seamlessXAxis, seamlessYAxis, vae } = state.generation;
const { seamlessXAxis, seamlessYAxis, vae } = state.canvasV2.params;
const isAutoVae = !vae;
graph.nodes[SEAMLESS] = {

View File

@ -20,7 +20,7 @@ export const addT2IAdaptersToLinearGraph = async (
const t2iAdapters = selectValidT2IAdapters(state.controlAdapters).filter(
({ model, processedControlImage, processorType, controlImage, isEnabled }) => {
const hasModel = Boolean(model);
const doesBaseMatch = model?.base === state.generation.model?.base;
const doesBaseMatch = model?.base === state.canvasV2.params.model?.base;
const hasControlImage = (processedControlImage && processorType !== 'none') || controlImage;
return isEnabled && hasModel && doesBaseMatch && hasControlImage;

View File

@ -28,9 +28,9 @@ export const addVAEToGraph = async (
graph: NonNullableGraph,
modelLoaderNodeId: string = MAIN_MODEL_LOADER
): Promise<void> => {
const { vae, seamlessXAxis, seamlessYAxis } = state.generation;
const { vae, seamlessXAxis, seamlessYAxis } = state.canvasV2.params;
const { boundingBoxScaleMethod } = state.canvas;
const { refinerModel } = state.sdxl;
const { refinerModel } = state.canvasV2.params;
const isUsingScaledDimensions = ['auto', 'manual'].includes(boundingBoxScaleMethod);

View File

@ -19,7 +19,7 @@ export const buildCanvasGraph = async (
let graph: NonNullableGraph;
if (generationMode === 'txt2img') {
if (state.generation.model && state.generation.model.base === 'sdxl') {
if (state.canvasV2.params.model && state.canvasV2.params.model.base === 'sdxl') {
graph = await buildCanvasSDXLTextToImageGraph(state);
} else {
graph = await buildCanvasTextToImageGraph(state);
@ -28,7 +28,7 @@ export const buildCanvasGraph = async (
if (!canvasInitImage) {
throw new Error('Missing canvas init image');
}
if (state.generation.model && state.generation.model.base === 'sdxl') {
if (state.canvasV2.params.model && state.canvasV2.params.model.base === 'sdxl') {
graph = await buildCanvasSDXLImageToImageGraph(state, canvasInitImage);
} else {
graph = await buildCanvasImageToImageGraph(state, canvasInitImage);
@ -37,7 +37,7 @@ export const buildCanvasGraph = async (
if (!canvasInitImage || !canvasMaskImage) {
throw new Error('Missing canvas init and mask images');
}
if (state.generation.model && state.generation.model.base === 'sdxl') {
if (state.canvasV2.params.model && state.canvasV2.params.model.base === 'sdxl') {
graph = await buildCanvasSDXLInpaintGraph(state, canvasInitImage, canvasMaskImage);
} else {
graph = await buildCanvasInpaintGraph(state, canvasInitImage, canvasMaskImage);
@ -46,7 +46,7 @@ export const buildCanvasGraph = async (
if (!canvasInitImage) {
throw new Error('Missing canvas init image');
}
if (state.generation.model && state.generation.model.base === 'sdxl') {
if (state.canvasV2.params.model && state.canvasV2.params.model.base === 'sdxl') {
graph = await buildCanvasSDXLOutpaintGraph(state, canvasInitImage, canvasMaskImage);
} else {
graph = await buildCanvasOutpaintGraph(state, canvasInitImage, canvasMaskImage);

View File

@ -54,7 +54,7 @@ export const buildCanvasImageToImageGraph = async (
shouldUseCpuNoise,
seamlessXAxis,
seamlessYAxis,
} = state.generation;
} = state.canvasV2.params;
// The bounding box determines width and height, not the width and height params
const { width, height } = state.canvas.boundingBoxDimensions;

View File

@ -61,7 +61,7 @@ export const buildCanvasInpaintGraph = async (
canvasCoherenceMinDenoise,
canvasCoherenceEdgeSize,
maskBlur,
} = state.generation;
} = state.canvasV2.params;
if (!model) {
log.error('No model found in state');

View File

@ -73,7 +73,7 @@ export const buildCanvasOutpaintGraph = async (
canvasCoherenceMinDenoise,
canvasCoherenceEdgeSize,
maskBlur,
} = state.generation;
} = state.canvasV2.params;
if (!model) {
log.error('No model found in state');

View File

@ -54,9 +54,9 @@ export const buildCanvasSDXLImageToImageGraph = async (
seamlessXAxis,
seamlessYAxis,
img2imgStrength: strength,
} = state.generation;
} = state.canvasV2.params;
const { refinerModel, refinerStart } = state.sdxl;
const { refinerModel, refinerStart } = state.canvasV2.params;
// The bounding box determines width and height, not the width and height params
const { width, height } = state.canvas.boundingBoxDimensions;

View File

@ -61,9 +61,9 @@ export const buildCanvasSDXLInpaintGraph = async (
canvasCoherenceMinDenoise,
canvasCoherenceEdgeSize,
maskBlur,
} = state.generation;
} = state.canvasV2.params;
const { refinerModel, refinerStart } = state.sdxl;
const { refinerModel, refinerStart } = state.canvasV2.params;
if (!model) {
log.error('No model found in state');

View File

@ -73,9 +73,9 @@ export const buildCanvasSDXLOutpaintGraph = async (
canvasCoherenceMinDenoise,
canvasCoherenceEdgeSize,
maskBlur,
} = state.generation;
} = state.canvasV2.params;
const { refinerModel, refinerStart } = state.sdxl;
const { refinerModel, refinerStart } = state.canvasV2.params;
if (!model) {
log.error('No model found in state');

View File

@ -47,7 +47,7 @@ export const buildCanvasSDXLTextToImageGraph = async (state: RootState): Promise
shouldUseCpuNoise,
seamlessXAxis,
seamlessYAxis,
} = state.generation;
} = state.canvasV2.params;
// The bounding box determines width and height, not the width and height params
const { width, height } = state.canvas.boundingBoxDimensions;
@ -58,7 +58,7 @@ export const buildCanvasSDXLTextToImageGraph = async (state: RootState): Promise
const is_intermediate = true;
const isUsingScaledDimensions = ['auto', 'manual'].includes(boundingBoxScaleMethod);
const { refinerModel, refinerStart } = state.sdxl;
const { refinerModel, refinerStart } = state.canvasV2.params;
if (!model) {
log.error('No model found in state');

View File

@ -47,7 +47,7 @@ export const buildCanvasTextToImageGraph = async (state: RootState): Promise<Non
shouldUseCpuNoise,
seamlessXAxis,
seamlessYAxis,
} = state.generation;
} = state.canvasV2.params;
// The bounding box determines width and height, not the width and height params
const { width, height } = state.canvas.boundingBoxDimensions;

View File

@ -419,8 +419,8 @@ const addInitialImageLayerToGraph = (
| Invocation<'sdxl_model_loader'>,
layer: InitialImageLayer
) => {
const { vaePrecision } = state.generation;
const { refinerModel, refinerStart } = state.sdxl;
const { vaePrecision } = state.canvasV2.params;
const { refinerModel, refinerStart } = state.canvasV2.params;
const { width, height } = state.canvasV2.document;
assert(layer.isEnabled, 'Initial image layer is not enabled');
assert(layer.image, 'Initial image layer has no image');

View File

@ -12,7 +12,7 @@ import {
} from 'features/nodes/util/graph/constants';
import type { Graph } from 'features/nodes/util/graph/generation/Graph';
import { getBoardField } from 'features/nodes/util/graph/graphBuilderUtils';
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
import { selectOptimalDimension } from 'features/controlLayers/store/selectors';
import type { Invocation } from 'services/api/types';
/**

View File

@ -30,7 +30,7 @@ export const addSDXLRefiner = async (
refinerScheduler,
refinerCFGScale,
refinerStart,
} = state.sdxl;
} = state.canvasV2.params;
assert(refinerModel, 'No refiner model found in state');

View File

@ -21,7 +21,7 @@ export const addSeamless = (
modelLoader: Invocation<'main_model_loader'> | Invocation<'sdxl_model_loader'>,
vaeLoader: Invocation<'vae_loader'> | null
): Invocation<'seamless'> | null => {
const { seamlessXAxis: seamless_x, seamlessYAxis: seamless_y } = state.generation;
const { seamlessXAxis: seamless_x, seamlessYAxis: seamless_y } = state.canvasV2.params;
if (!seamless_x && !seamless_y) {
return null;

View File

@ -39,7 +39,7 @@ export const buildGenerationTabGraph = async (state: RootState): Promise<GraphTy
vaePrecision,
seed,
vae,
} = state.generation;
} = state.canvasV2.params;
const { width, height } = state.canvasV2.document;
assert(model, 'No model found in state');

View File

@ -35,10 +35,10 @@ export const buildGenerationTabSDXLGraph = async (state: RootState): Promise<Non
shouldUseCpuNoise,
vaePrecision,
vae,
} = state.generation;
} = state.canvasV2.params;
const { width, height } = state.canvasV2.document;
const { refinerModel, refinerStart } = state.sdxl;
const { refinerModel, refinerStart } = state.canvasV2.params;
assert(model, 'No model found in state');

View File

@ -1,12 +1,12 @@
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { setCfgRescaleMultiplier } from 'features/parameters/store/generationSlice';
import { setCfgRescaleMultiplier } from 'features/controlLayers/store/canvasV2Slice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
const ParamCFGRescaleMultiplier = () => {
const cfgRescaleMultiplier = useAppSelector((s) => s.generation.cfgRescaleMultiplier);
const cfgRescaleMultiplier = useAppSelector((s) => s.canvasV2.params.cfgRescaleMultiplier);
const initial = useAppSelector((s) => s.config.sd.cfgRescaleMultiplier.initial);
const sliderMin = useAppSelector((s) => s.config.sd.cfgRescaleMultiplier.sliderMin);
const sliderMax = useAppSelector((s) => s.config.sd.cfgRescaleMultiplier.sliderMax);

View File

@ -1,19 +1,19 @@
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { setClipSkip } from 'features/parameters/store/generationSlice';
import { setClipSkip } from 'features/controlLayers/store/canvasV2Slice';
import { CLIP_SKIP_MAP } from 'features/parameters/types/constants';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
const ParamClipSkip = () => {
const clipSkip = useAppSelector((s) => s.generation.clipSkip);
const clipSkip = useAppSelector((s) => s.canvasV2.params.clipSkip);
const initial = useAppSelector((s) => s.config.sd.clipSkip.initial);
const sliderMin = useAppSelector((s) => s.config.sd.clipSkip.sliderMin);
const numberInputMin = useAppSelector((s) => s.config.sd.clipSkip.numberInputMin);
const coarseStep = useAppSelector((s) => s.config.sd.clipSkip.coarseStep);
const fineStep = useAppSelector((s) => s.config.sd.clipSkip.fineStep);
const { model } = useAppSelector((s) => s.generation);
const model = useAppSelector((s) => s.canvasV2.params.model);
const dispatch = useAppDispatch();
const { t } = useTranslation();

View File

@ -1,8 +1,8 @@
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks';
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
import { selectOptimalDimension } from 'features/controlLayers/store/selectors';
import { useImageSizeContext } from 'features/parameters/components/ImageSize/ImageSizeContext';
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';

View File

@ -2,7 +2,7 @@ import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@
import { useAppSelector } from 'app/store/storeHooks';
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
import { useImageSizeContext } from 'features/parameters/components/ImageSize/ImageSizeContext';
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
import { selectOptimalDimension } from 'features/controlLayers/store/selectors';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';

View File

@ -1,13 +1,13 @@
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { setCanvasCoherenceEdgeSize } from 'features/parameters/store/generationSlice';
import { setCanvasCoherenceEdgeSize } from 'features/controlLayers/store/canvasV2Slice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
const ParamCanvasCoherenceEdgeSize = () => {
const dispatch = useAppDispatch();
const canvasCoherenceEdgeSize = useAppSelector((s) => s.generation.canvasCoherenceEdgeSize);
const canvasCoherenceEdgeSize = useAppSelector((s) => s.canvasV2.compositing.canvasCoherenceEdgeSize);
const initial = useAppSelector((s) => s.config.sd.canvasCoherenceEdgeSize.initial);
const sliderMin = useAppSelector((s) => s.config.sd.canvasCoherenceEdgeSize.sliderMin);
const sliderMax = useAppSelector((s) => s.config.sd.canvasCoherenceEdgeSize.sliderMax);

View File

@ -1,13 +1,13 @@
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { setCanvasCoherenceMinDenoise } from 'features/parameters/store/generationSlice';
import { setCanvasCoherenceMinDenoise } from 'features/controlLayers/store/canvasV2Slice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
const ParamCanvasCoherenceMinDenoise = () => {
const dispatch = useAppDispatch();
const canvasCoherenceMinDenoise = useAppSelector((s) => s.generation.canvasCoherenceMinDenoise);
const canvasCoherenceMinDenoise = useAppSelector((s) => s.canvasV2.compositing.canvasCoherenceMinDenoise);
const { t } = useTranslation();
const handleChange = useCallback(

View File

@ -2,14 +2,14 @@ import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui-library';
import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { setCanvasCoherenceMode } from 'features/parameters/store/generationSlice';
import { setCanvasCoherenceMode } from 'features/controlLayers/store/canvasV2Slice';
import { isParameterCanvasCoherenceMode } from 'features/parameters/types/parameterSchemas';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
const ParamCanvasCoherenceMode = () => {
const dispatch = useAppDispatch();
const canvasCoherenceMode = useAppSelector((s) => s.generation.canvasCoherenceMode);
const canvasCoherenceMode = useAppSelector((s) => s.canvasV2.compositing.canvasCoherenceMode);
const { t } = useTranslation();
const options = useMemo<ComboboxOption[]>(

View File

@ -1,14 +1,14 @@
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { setMaskBlur } from 'features/parameters/store/generationSlice';
import { setMaskBlur } from 'features/controlLayers/store/canvasV2Slice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
const ParamMaskBlur = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const maskBlur = useAppSelector((s) => s.generation.maskBlur);
const maskBlur = useAppSelector((s) => s.canvasV2.compositing.maskBlur);
const initial = useAppSelector((s) => s.config.sd.maskBlur.initial);
const sliderMin = useAppSelector((s) => s.config.sd.maskBlur.sliderMin);
const sliderMax = useAppSelector((s) => s.config.sd.maskBlur.sliderMax);

View File

@ -1,20 +1,16 @@
import { Box, Flex, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIColorPicker from 'common/components/IAIColorPicker';
import { selectGenerationSlice, setInfillColorValue } from 'features/parameters/store/generationSlice';
import { setInfillColorValue } from 'features/controlLayers/store/canvasV2Slice';
import { memo, useCallback } from 'react';
import type { RgbaColor } from 'react-colorful';
import { useTranslation } from 'react-i18next';
const selectInfillColor = createMemoizedSelector(selectGenerationSlice, (generation) => generation.infillColorValue);
const ParamInfillColorOptions = () => {
const dispatch = useAppDispatch();
const infillColor = useAppSelector(selectInfillColor);
const infillMethod = useAppSelector((s) => s.generation.infillMethod);
const infillColor = useAppSelector((s) => s.canvasV2.compositing.infillColorValue);
const infillMethod = useAppSelector((s) => s.canvasV2.compositing.infillMethod);
const { t } = useTranslation();

View File

@ -2,7 +2,7 @@ import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui-library';
import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { setInfillMethod } from 'features/parameters/store/generationSlice';
import { setInfillMethod } from 'features/controlLayers/store/canvasV2Slice';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useGetAppConfigQuery } from 'services/api/endpoints/appInfo';
@ -10,7 +10,7 @@ import { useGetAppConfigQuery } from 'services/api/endpoints/appInfo';
const ParamInfillMethod = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const infillMethod = useAppSelector((s) => s.generation.infillMethod);
const infillMethod = useAppSelector((s) => s.canvasV2.compositing.infillMethod);
const { data: appConfigData } = useGetAppConfigQuery();
const options = useMemo<ComboboxOption[]>(
() =>

View File

@ -1,115 +0,0 @@
import { Box, CompositeNumberInput, CompositeSlider, Flex, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIColorPicker from 'common/components/IAIColorPicker';
import {
setInfillMosaicMaxColor,
setInfillMosaicMinColor,
setInfillMosaicTileHeight,
setInfillMosaicTileWidth,
} from 'features/parameters/store/generationSlice';
import { memo, useCallback } from 'react';
import type { RgbaColor } from 'react-colorful';
import { useTranslation } from 'react-i18next';
const ParamInfillMosaicTileSize = () => {
const dispatch = useAppDispatch();
const infillMosaicTileWidth = useAppSelector((s) => s.generation.infillMosaicTileWidth);
const infillMosaicTileHeight = useAppSelector((s) => s.generation.infillMosaicTileHeight);
const infillMosaicMinColor = useAppSelector((s) => s.generation.infillMosaicMinColor);
const infillMosaicMaxColor = useAppSelector((s) => s.generation.infillMosaicMaxColor);
const infillMethod = useAppSelector((s) => s.generation.infillMethod);
const { t } = useTranslation();
const handleInfillMosaicTileWidthChange = useCallback(
(v: number) => {
dispatch(setInfillMosaicTileWidth(v));
},
[dispatch]
);
const handleInfillMosaicTileHeightChange = useCallback(
(v: number) => {
dispatch(setInfillMosaicTileHeight(v));
},
[dispatch]
);
const handleInfillMosaicMinColor = useCallback(
(v: RgbaColor) => {
dispatch(setInfillMosaicMinColor(v));
},
[dispatch]
);
const handleInfillMosaicMaxColor = useCallback(
(v: RgbaColor) => {
dispatch(setInfillMosaicMaxColor(v));
},
[dispatch]
);
return (
<Flex flexDir="column" gap={4}>
<FormControl isDisabled={infillMethod !== 'mosaic'}>
<FormLabel>{t('parameters.infillMosaicTileWidth')}</FormLabel>
<CompositeSlider
min={8}
max={256}
value={infillMosaicTileWidth}
defaultValue={64}
onChange={handleInfillMosaicTileWidthChange}
step={8}
fineStep={8}
marks
/>
<CompositeNumberInput
min={8}
max={1024}
value={infillMosaicTileWidth}
defaultValue={64}
onChange={handleInfillMosaicTileWidthChange}
step={8}
fineStep={8}
/>
</FormControl>
<FormControl isDisabled={infillMethod !== 'mosaic'}>
<FormLabel>{t('parameters.infillMosaicTileHeight')}</FormLabel>
<CompositeSlider
min={8}
max={256}
value={infillMosaicTileHeight}
defaultValue={64}
onChange={handleInfillMosaicTileHeightChange}
step={8}
fineStep={8}
marks
/>
<CompositeNumberInput
min={8}
max={1024}
value={infillMosaicTileHeight}
defaultValue={64}
onChange={handleInfillMosaicTileHeightChange}
step={8}
fineStep={8}
/>
</FormControl>
<FormControl isDisabled={infillMethod !== 'mosaic'}>
<FormLabel>{t('parameters.infillMosaicMinColor')}</FormLabel>
<Box w="full" pt={2} pb={2}>
<IAIColorPicker color={infillMosaicMinColor} onChange={handleInfillMosaicMinColor} />
</Box>
</FormControl>
<FormControl isDisabled={infillMethod !== 'mosaic'}>
<FormLabel>{t('parameters.infillMosaicMaxColor')}</FormLabel>
<Box w="full" pt={2} pb={2}>
<IAIColorPicker color={infillMosaicMaxColor} onChange={handleInfillMosaicMaxColor} />
</Box>
</FormControl>
</Flex>
);
};
export default memo(ParamInfillMosaicTileSize);

View File

@ -2,12 +2,11 @@ import { useAppSelector } from 'app/store/storeHooks';
import { memo } from 'react';
import ParamInfillColorOptions from './ParamInfillColorOptions';
import ParamInfillMosaicOptions from './ParamInfillMosaicOptions';
import ParamInfillPatchmatchDownscaleSize from './ParamInfillPatchmatchDownscaleSize';
import ParamInfillTilesize from './ParamInfillTilesize';
const ParamInfillOptions = () => {
const infillMethod = useAppSelector((s) => s.generation.infillMethod);
const infillMethod = useAppSelector((s) => s.canvasV2.compositing.infillMethod);
if (infillMethod === 'tile') {
return <ParamInfillTilesize />;
}
@ -16,10 +15,6 @@ const ParamInfillOptions = () => {
return <ParamInfillPatchmatchDownscaleSize />;
}
if (infillMethod === 'mosaic') {
return <ParamInfillMosaicOptions />;
}
if (infillMethod === 'color') {
return <ParamInfillColorOptions />;
}

View File

@ -1,14 +1,14 @@
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { setInfillPatchmatchDownscaleSize } from 'features/parameters/store/generationSlice';
import { setInfillPatchmatchDownscaleSize } from 'features/controlLayers/store/canvasV2Slice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
const ParamInfillPatchmatchDownscaleSize = () => {
const dispatch = useAppDispatch();
const infillMethod = useAppSelector((s) => s.generation.infillMethod);
const infillPatchmatchDownscaleSize = useAppSelector((s) => s.generation.infillPatchmatchDownscaleSize);
const infillMethod = useAppSelector((s) => s.canvasV2.compositing.infillMethod);
const infillPatchmatchDownscaleSize = useAppSelector((s) => s.canvasV2.compositing.infillPatchmatchDownscaleSize);
const initial = useAppSelector((s) => s.config.sd.infillPatchmatchDownscaleSize.initial);
const sliderMin = useAppSelector((s) => s.config.sd.infillPatchmatchDownscaleSize.sliderMin);
const sliderMax = useAppSelector((s) => s.config.sd.infillPatchmatchDownscaleSize.sliderMax);

View File

@ -1,12 +1,12 @@
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { setInfillTileSize } from 'features/parameters/store/generationSlice';
import { setInfillTileSize } from 'features/controlLayers/store/canvasV2Slice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
const ParamInfillTileSize = () => {
const dispatch = useAppDispatch();
const infillTileSize = useAppSelector((s) => s.generation.infillTileSize);
const infillTileSize = useAppSelector((s) => s.canvasV2.compositing.infillTileSize);
const initial = useAppSelector((s) => s.config.sd.infillTileSize.initial);
const sliderMin = useAppSelector((s) => s.config.sd.infillTileSize.sliderMin);
const sliderMax = useAppSelector((s) => s.config.sd.infillTileSize.sliderMax);
@ -15,7 +15,7 @@ const ParamInfillTileSize = () => {
const coarseStep = useAppSelector((s) => s.config.sd.infillTileSize.coarseStep);
const fineStep = useAppSelector((s) => s.config.sd.infillTileSize.fineStep);
const infillMethod = useAppSelector((s) => s.generation.infillMethod);
const infillMethod = useAppSelector((s) => s.canvasV2.compositing.infillMethod);
const { t } = useTranslation();

View File

@ -4,14 +4,14 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { setBoundingBoxScaleMethod } from 'features/canvas/store/canvasSlice';
import { isBoundingBoxScaleMethod } from 'features/canvas/store/canvasTypes';
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
import { selectOptimalDimension } from 'features/controlLayers/store/selectors';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
const ParamScaleBeforeProcessing = () => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const boundingBoxScaleMethod = useAppSelector((s) => s.canvas.boundingBoxScaleMethod);
const boundingBoxScaleMethod = useAppSelector((s) => s.canvasV2.scaledBbox.scaleMethod);
const optimalDimension = useAppSelector(selectOptimalDimension);
const OPTIONS: ComboboxOption[] = useMemo(

View File

@ -1,7 +1,7 @@
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { setScaledBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
import { selectOptimalDimension } from 'features/controlLayers/store/selectors';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
@ -9,8 +9,8 @@ const ParamScaledHeight = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const optimalDimension = useAppSelector(selectOptimalDimension);
const isManual = useAppSelector((s) => s.canvas.boundingBoxScaleMethod === 'manual');
const height = useAppSelector((s) => s.canvas.scaledBoundingBoxDimensions.height);
const isManual = useAppSelector((s) => s.canvasV2.scaledBbox.scaleMethod === 'manual');
const height = useAppSelector((s) => s.canvasV2.scaledBbox.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);

View File

@ -1,7 +1,7 @@
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { setScaledBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
import { selectOptimalDimension } from 'features/controlLayers/store/selectors';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
@ -9,8 +9,8 @@ const ParamScaledWidth = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const optimalDimension = useAppSelector(selectOptimalDimension);
const isManual = useAppSelector((s) => s.canvas.boundingBoxScaleMethod === 'manual');
const width = useAppSelector((s) => s.canvas.scaledBoundingBoxDimensions.width);
const isManual = useAppSelector((s) => s.canvasV2.scaledBbox.scaleMethod === 'manual');
const width = useAppSelector((s) => s.canvasV2.scaledBbox.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);

View File

@ -1,10 +1,10 @@
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { setImg2imgStrength } from 'features/controlLayers/store/canvasV2Slice';
import ImageToImageStrength from 'features/parameters/components/ImageToImage/ImageToImageStrength';
import { setImg2imgStrength } from 'features/parameters/store/generationSlice';
import { memo, useCallback } from 'react';
const ParamImageToImageStrength = () => {
const img2imgStrength = useAppSelector((s) => s.generation.img2imgStrength);
const img2imgStrength = useAppSelector((s) => s.canvasV2.params.img2imgStrength);
const dispatch = useAppDispatch();
const onChange = useCallback(

View File

@ -1,12 +1,12 @@
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { setCfgScale } from 'features/parameters/store/generationSlice';
import { setCfgScale } from 'features/controlLayers/store/canvasV2Slice';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
const ParamCFGScale = () => {
const cfgScale = useAppSelector((s) => s.generation.cfgScale);
const cfgScale = useAppSelector((s) => s.canvasV2.params.cfgScale);
const sliderMin = useAppSelector((s) => s.config.sd.guidance.sliderMin);
const sliderMax = useAppSelector((s) => s.config.sd.guidance.sliderMax);
const numberInputMin = useAppSelector((s) => s.config.sd.guidance.numberInputMin);

View File

@ -2,7 +2,7 @@ import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@
import { useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { useImageSizeContext } from 'features/parameters/components/ImageSize/ImageSizeContext';
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
import { selectOptimalDimension } from 'features/controlLayers/store/selectors';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';

View File

@ -1,9 +1,9 @@
import { Box, Textarea } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { negativePromptChanged } from 'features/controlLayers/store/canvasV2Slice';
import { PromptLabel } from 'features/parameters/components/Prompts/PromptLabel';
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
import { ViewModePrompt } from 'features/parameters/components/Prompts/ViewModePrompt';
import { negativePromptChanged } from 'features/parameters/store/generationSlice';
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
import { PromptPopover } from 'features/prompt/PromptPopover';
import { usePrompt } from 'features/prompt/usePrompt';
@ -13,7 +13,7 @@ import { useListStylePresetsQuery } from 'services/api/endpoints/stylePresets';
export const ParamNegativePrompt = memo(() => {
const dispatch = useAppDispatch();
const prompt = useAppSelector((s) => s.generation.negativePrompt);
const prompt = useAppSelector((s) => s.canvasV2.params.negativePrompt);
const viewMode = useAppSelector((s) => s.stylePreset.viewMode);
const activeStylePresetId = useAppSelector((s) => s.stylePreset.activeStylePresetId);

View File

@ -1,10 +1,10 @@
import { Box, Textarea } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { positivePromptChanged } from 'features/controlLayers/store/canvasV2Slice';
import { ShowDynamicPromptsPreviewButton } from 'features/dynamicPrompts/components/ShowDynamicPromptsPreviewButton';
import { PromptLabel } from 'features/parameters/components/Prompts/PromptLabel';
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
import { ViewModePrompt } from 'features/parameters/components/Prompts/ViewModePrompt';
import { positivePromptChanged } from 'features/parameters/store/generationSlice';
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
import { PromptPopover } from 'features/prompt/PromptPopover';
import { usePrompt } from 'features/prompt/usePrompt';
@ -17,8 +17,8 @@ import { useListStylePresetsQuery } from 'services/api/endpoints/stylePresets';
export const ParamPositivePrompt = memo(() => {
const dispatch = useAppDispatch();
const prompt = useAppSelector((s) => s.generation.positivePrompt);
const baseModel = useAppSelector((s) => s.generation.model)?.base;
const prompt = useAppSelector((s) => s.canvasV2.params.positivePrompt);
const baseModel = useAppSelector((s) => s.canvasV2.params.model)?.base;
const viewMode = useAppSelector((s) => s.stylePreset.viewMode);
const activeStylePresetId = useAppSelector((s) => s.stylePreset.activeStylePresetId);

View File

@ -2,7 +2,7 @@ import type { ComboboxOnChange } from '@invoke-ai/ui-library';
import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { setScheduler } from 'features/parameters/store/generationSlice';
import { setScheduler } from 'features/controlLayers/store/canvasV2Slice';
import { SCHEDULER_OPTIONS } from 'features/parameters/types/constants';
import { isParameterScheduler } from 'features/parameters/types/parameterSchemas';
import { memo, useCallback, useMemo } from 'react';
@ -11,7 +11,7 @@ import { useTranslation } from 'react-i18next';
const ParamScheduler = () => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const scheduler = useAppSelector((s) => s.generation.scheduler);
const scheduler = useAppSelector((s) => s.canvasV2.params.scheduler);
const onChange = useCallback<ComboboxOnChange>(
(v) => {

View File

@ -1,12 +1,12 @@
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { setSteps } from 'features/parameters/store/generationSlice';
import { setSteps } from 'features/controlLayers/store/canvasV2Slice';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
const ParamSteps = () => {
const steps = useAppSelector((s) => s.generation.steps);
const steps = useAppSelector((s) => s.canvasV2.params.steps);
const initial = useAppSelector((s) => s.config.sd.steps.initial);
const sliderMin = useAppSelector((s) => s.config.sd.steps.sliderMin);
const sliderMax = useAppSelector((s) => s.config.sd.steps.sliderMax);

View File

@ -2,7 +2,7 @@ import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@
import { useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { useImageSizeContext } from 'features/parameters/components/ImageSize/ImageSizeContext';
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
import { selectOptimalDimension } from 'features/controlLayers/store/selectors';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';

View File

@ -3,7 +3,7 @@ import { roundToMultiple } from 'common/util/roundDownToMultiple';
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
import { ASPECT_RATIO_MAP, initialAspectRatioState } from 'features/parameters/components/ImageSize/constants';
import type { AspectRatioID, AspectRatioState } from 'features/parameters/components/ImageSize/types';
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
import { selectOptimalDimension } from 'features/controlLayers/store/selectors';
import { createContext, useCallback, useContext, useMemo } from 'react';
export type ImageSizeContextInnerValue = {

View File

@ -1,7 +1,7 @@
import { IconButton } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks';
import { useImageSizeContext } from 'features/parameters/components/ImageSize/ImageSizeContext';
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
import { 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';

View File

@ -1,22 +1,18 @@
import { Box, Combobox, FormControl, FormLabel, Tooltip } from '@invoke-ai/ui-library';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { useGroupedModelCombobox } from 'common/hooks/useGroupedModelCombobox';
import { zModelIdentifierField } from 'features/nodes/types/common';
import { modelSelected } from 'features/parameters/store/actions';
import { selectGenerationSlice } from 'features/parameters/store/generationSlice';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useSDMainModels } from 'services/api/hooks/modelsByType';
import type { MainModelConfig } from 'services/api/types';
const selectModel = createMemoizedSelector(selectGenerationSlice, (generation) => generation.model);
const ParamMainModelSelect = () => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const selectedModel = useAppSelector(selectModel);
const selectedModel = useAppSelector((s) => s.canvasV2.params.model);
const [modelConfigs, { isLoading }] = useSDMainModels();
const tooltipLabel = useMemo(() => {
if (!modelConfigs.length || !selectedModel) {

View File

@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next';
import { RiSparklingFill } from 'react-icons/ri';
export const UseDefaultSettingsButton = () => {
const model = useAppSelector((s) => s.generation.model);
const model = useAppSelector((s) => s.canvasV2.params.model);
const { t } = useTranslation();
const dispatch = useAppDispatch();

View File

@ -1,14 +1,14 @@
import { FormControl, FormLabel, Switch } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { setSeamlessXAxis } from 'features/parameters/store/generationSlice';
import { setSeamlessXAxis } from 'features/controlLayers/store/canvasV2Slice';
import type { ChangeEvent } from 'react';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
const ParamSeamlessXAxis = () => {
const { t } = useTranslation();
const seamlessXAxis = useAppSelector((s) => s.generation.seamlessXAxis);
const seamlessXAxis = useAppSelector((s) => s.canvasV2.params.seamlessXAxis);
const dispatch = useAppDispatch();

View File

@ -1,14 +1,14 @@
import { FormControl, FormLabel, Switch } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { setSeamlessYAxis } from 'features/parameters/store/generationSlice';
import { setSeamlessYAxis } from 'features/controlLayers/store/canvasV2Slice';
import type { ChangeEvent } from 'react';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
const ParamSeamlessYAxis = () => {
const { t } = useTranslation();
const seamlessYAxis = useAppSelector((s) => s.generation.seamlessYAxis);
const seamlessYAxis = useAppSelector((s) => s.canvasV2.params.seamlessYAxis);
const dispatch = useAppDispatch();
const handleChange = useCallback(
(e: ChangeEvent<HTMLInputElement>) => {

View File

@ -2,13 +2,13 @@ import { CompositeNumberInput, FormControl, FormLabel } from '@invoke-ai/ui-libr
import { NUMPY_RAND_MAX, NUMPY_RAND_MIN } from 'app/constants';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { setSeed } from 'features/parameters/store/generationSlice';
import { setSeed } from 'features/controlLayers/store/canvasV2Slice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
export const ParamSeedNumberInput = memo(() => {
const seed = useAppSelector((s) => s.generation.seed);
const shouldRandomizeSeed = useAppSelector((s) => s.generation.shouldRandomizeSeed);
const seed = useAppSelector((s) => s.canvasV2.params.seed);
const shouldRandomizeSeed = useAppSelector((s) => s.canvasV2.params.shouldRandomizeSeed);
const { t } = useTranslation();

View File

@ -1,6 +1,6 @@
import { FormControl, FormLabel, Switch } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { setShouldRandomizeSeed } from 'features/parameters/store/generationSlice';
import { setShouldRandomizeSeed } from 'features/controlLayers/store/canvasV2Slice';
import type { ChangeEvent } from 'react';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
@ -9,7 +9,7 @@ export const ParamSeedRandomize = memo(() => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const shouldRandomizeSeed = useAppSelector((s) => s.generation.shouldRandomizeSeed);
const shouldRandomizeSeed = useAppSelector((s) => s.canvasV2.params.shouldRandomizeSeed);
const handleChangeShouldRandomizeSeed = useCallback(
(e: ChangeEvent<HTMLInputElement>) => dispatch(setShouldRandomizeSeed(e.target.checked)),

View File

@ -2,14 +2,14 @@ import { Button } from '@invoke-ai/ui-library';
import { NUMPY_RAND_MAX, NUMPY_RAND_MIN } from 'app/constants';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import randomInt from 'common/util/randomInt';
import { setSeed } from 'features/parameters/store/generationSlice';
import { setSeed } from 'features/controlLayers/store/canvasV2Slice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiShuffleBold } from 'react-icons/pi';
export const ParamSeedShuffle = memo(() => {
const dispatch = useAppDispatch();
const shouldRandomizeSeed = useAppSelector((s) => s.generation.shouldRandomizeSeed);
const shouldRandomizeSeed = useAppSelector((s) => s.canvasV2.params.shouldRandomizeSeed);
const { t } = useTranslation();
const handleClickRandomizeSeed = useCallback(

View File

@ -1,24 +1,19 @@
import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { useGroupedModelCombobox } from 'common/hooks/useGroupedModelCombobox';
import { vaeSelected } from 'features/controlLayers/store/canvasV2Slice';
import { zModelIdentifierField } from 'features/nodes/types/common';
import { selectGenerationSlice, vaeSelected } from 'features/parameters/store/generationSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { useVAEModels } from 'services/api/hooks/modelsByType';
import type { VAEModelConfig } from 'services/api/types';
const selector = createMemoizedSelector(selectGenerationSlice, (generation) => {
const { model, vae } = generation;
return { model, vae };
});
const ParamVAEModelSelect = () => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const { model, vae } = useAppSelector(selector);
const model = useAppSelector((s) => s.canvasV2.params.model);
const vae = useAppSelector((s) => s.canvasV2.params.vae);
const [modelConfigs, { isLoading }] = useVAEModels();
const getIsDisabled = useCallback(
(vae: VAEModelConfig): boolean => {

View File

@ -2,7 +2,7 @@ import type { ComboboxOnChange } from '@invoke-ai/ui-library';
import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { vaePrecisionChanged } from 'features/parameters/store/generationSlice';
import { vaePrecisionChanged } from 'features/controlLayers/store/canvasV2Slice';
import { isParameterPrecision } from 'features/parameters/types/parameterSchemas';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
@ -15,7 +15,7 @@ const options = [
const ParamVAEModelSelect = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const vaePrecision = useAppSelector((s) => s.generation.vaePrecision);
const vaePrecision = useAppSelector((s) => s.canvasV2.params.vaePrecision);
const onChange = useCallback<ComboboxOnChange>(
(v) => {

View File

@ -2,8 +2,8 @@ import { skipToken } from '@reduxjs/toolkit/query';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
import { iiLayerAdded } from 'features/controlLayers/store/canvasV2Slice';
import { selectOptimalDimension } from 'features/controlLayers/store/selectors';
import { parseAndRecallAllMetadata } from 'features/metadata/util/handlers';
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
import { toast } from 'features/toast/toast';
import { setActiveTab } from 'features/ui/store/uiSlice';
import { t } from 'i18next';

View File

@ -1,264 +0,0 @@
import type { PayloadAction } from '@reduxjs/toolkit';
import { createSlice } from '@reduxjs/toolkit';
import type { PersistConfig, RootState } from 'app/store/store';
import { initialAspectRatioState } from 'features/parameters/components/ImageSize/constants';
import { CLIP_SKIP_MAP } from 'features/parameters/types/constants';
import type {
ParameterCanvasCoherenceMode,
ParameterCFGRescaleMultiplier,
ParameterCFGScale,
ParameterModel,
ParameterPrecision,
ParameterScheduler,
ParameterVAEModel,
} from 'features/parameters/types/parameterSchemas';
import { getOptimalDimension } from 'features/parameters/util/optimalDimension';
import { configChanged } from 'features/system/store/configSlice';
import { clamp } from 'lodash-es';
import type { RgbaColor } from 'react-colorful';
import type { GenerationState } from './types';
const initialGenerationState: GenerationState = {
_version: 2,
cfgScale: 7.5,
cfgRescaleMultiplier: 0,
img2imgStrength: 0.75,
iterations: 1,
scheduler: 'euler',
seed: 0,
shouldRandomizeSeed: true,
steps: 50,
model: null,
vae: null,
vaePrecision: 'fp32',
seamlessXAxis: false,
seamlessYAxis: false,
clipSkip: 0,
shouldUseCpuNoise: true,
shouldShowAdvancedOptions: false,
maskBlur: 16,
maskBlurMethod: 'box',
canvasCoherenceMode: 'Gaussian Blur',
canvasCoherenceMinDenoise: 0,
canvasCoherenceEdgeSize: 16,
infillMethod: 'patchmatch',
infillTileSize: 32,
infillPatchmatchDownscaleSize: 1,
infillMosaicTileWidth: 64,
infillMosaicTileHeight: 64,
infillMosaicMinColor: { r: 0, g: 0, b: 0, a: 1 },
infillMosaicMaxColor: { r: 255, g: 255, b: 255, a: 1 },
infillColorValue: { r: 0, g: 0, b: 0, a: 1 },
positivePrompt: '',
negativePrompt: '',
positivePrompt2: '',
negativePrompt2: '',
shouldConcatPrompts: true,
};
export const generationSlice = createSlice({
name: 'generation',
initialState: initialGenerationState,
reducers: {
setIterations: (state, action: PayloadAction<number>) => {
state.iterations = action.payload;
},
setSteps: (state, action: PayloadAction<number>) => {
state.steps = action.payload;
},
setCfgScale: (state, action: PayloadAction<ParameterCFGScale>) => {
state.cfgScale = action.payload;
},
setCfgRescaleMultiplier: (state, action: PayloadAction<ParameterCFGRescaleMultiplier>) => {
state.cfgRescaleMultiplier = action.payload;
},
setScheduler: (state, action: PayloadAction<ParameterScheduler>) => {
state.scheduler = action.payload;
},
setSeed: (state, action: PayloadAction<number>) => {
state.seed = action.payload;
state.shouldRandomizeSeed = false;
},
setImg2imgStrength: (state, action: PayloadAction<number>) => {
state.img2imgStrength = action.payload;
},
setSeamlessXAxis: (state, action: PayloadAction<boolean>) => {
state.seamlessXAxis = action.payload;
},
setSeamlessYAxis: (state, action: PayloadAction<boolean>) => {
state.seamlessYAxis = action.payload;
},
setShouldRandomizeSeed: (state, action: PayloadAction<boolean>) => {
state.shouldRandomizeSeed = action.payload;
},
setMaskBlur: (state, action: PayloadAction<number>) => {
state.maskBlur = action.payload;
},
setCanvasCoherenceMode: (state, action: PayloadAction<ParameterCanvasCoherenceMode>) => {
state.canvasCoherenceMode = action.payload;
},
setCanvasCoherenceEdgeSize: (state, action: PayloadAction<number>) => {
state.canvasCoherenceEdgeSize = action.payload;
},
setCanvasCoherenceMinDenoise: (state, action: PayloadAction<number>) => {
state.canvasCoherenceMinDenoise = action.payload;
},
modelChanged: {
reducer: (
state,
action: PayloadAction<ParameterModel | null, string, { previousModel?: ParameterModel | null }>
) => {
const newModel = action.payload;
state.model = newModel;
if (newModel === null) {
return;
}
// Clamp ClipSkip Based On Selected Model
// TODO(psyche): remove this special handling when https://github.com/invoke-ai/InvokeAI/issues/4583 is resolved
// WIP PR here: https://github.com/invoke-ai/InvokeAI/pull/4624
if (newModel.base === 'sdxl') {
// We don't support clip skip for SDXL yet - it's not in the graphs
state.clipSkip = 0;
} else {
const { maxClip } = CLIP_SKIP_MAP[newModel.base];
state.clipSkip = clamp(state.clipSkip, 0, maxClip);
}
},
prepare: (payload: ParameterModel | null, previousModel?: ParameterModel | null) => ({
payload,
meta: {
previousModel,
},
}),
},
vaeSelected: (state, action: PayloadAction<ParameterVAEModel | null>) => {
// null is a valid VAE!
state.vae = action.payload;
},
vaePrecisionChanged: (state, action: PayloadAction<ParameterPrecision>) => {
state.vaePrecision = action.payload;
},
setClipSkip: (state, action: PayloadAction<number>) => {
state.clipSkip = action.payload;
},
shouldUseCpuNoiseChanged: (state, action: PayloadAction<boolean>) => {
state.shouldUseCpuNoise = action.payload;
},
setInfillMethod: (state, action: PayloadAction<string>) => {
state.infillMethod = action.payload;
},
setInfillTileSize: (state, action: PayloadAction<number>) => {
state.infillTileSize = action.payload;
},
setInfillPatchmatchDownscaleSize: (state, action: PayloadAction<number>) => {
state.infillPatchmatchDownscaleSize = action.payload;
},
setInfillMosaicTileWidth: (state, action: PayloadAction<number>) => {
state.infillMosaicTileWidth = action.payload;
},
setInfillMosaicTileHeight: (state, action: PayloadAction<number>) => {
state.infillMosaicTileHeight = action.payload;
},
setInfillMosaicMinColor: (state, action: PayloadAction<RgbaColor>) => {
state.infillMosaicMinColor = action.payload;
},
setInfillMosaicMaxColor: (state, action: PayloadAction<RgbaColor>) => {
state.infillMosaicMaxColor = action.payload;
},
setInfillColorValue: (state, action: PayloadAction<RgbaColor>) => {
state.infillColorValue = action.payload;
},
positivePromptChanged: (state, action: PayloadAction<string>) => {
state.positivePrompt = action.payload;
},
negativePromptChanged: (state, action: PayloadAction<string>) => {
state.negativePrompt = action.payload;
},
positivePrompt2Changed: (state, action: PayloadAction<string>) => {
state.positivePrompt2 = action.payload;
},
negativePrompt2Changed: (state, action: PayloadAction<string>) => {
state.negativePrompt2 = action.payload;
},
shouldConcatPromptsChanged: (state, action: PayloadAction<boolean>) => {
state.shouldConcatPrompts = action.payload;
},
},
extraReducers: (builder) => {
builder.addCase(configChanged, (state, action) => {
if (action.payload.sd?.scheduler) {
state.scheduler = action.payload.sd.scheduler;
}
if (action.payload.sd?.vaePrecision) {
state.vaePrecision = action.payload.sd.vaePrecision;
}
});
},
selectors: {
selectOptimalDimension: (slice) => getOptimalDimension(slice.model),
},
});
export const {
setCfgScale,
setCfgRescaleMultiplier,
setImg2imgStrength,
setInfillMethod,
setIterations,
setScheduler,
setMaskBlur,
setCanvasCoherenceMode,
setCanvasCoherenceEdgeSize,
setCanvasCoherenceMinDenoise,
setSeed,
setShouldRandomizeSeed,
setSteps,
modelChanged,
vaeSelected,
setSeamlessXAxis,
setSeamlessYAxis,
setClipSkip,
shouldUseCpuNoiseChanged,
vaePrecisionChanged,
setInfillTileSize,
setInfillPatchmatchDownscaleSize,
setInfillMosaicTileWidth,
setInfillMosaicTileHeight,
setInfillMosaicMinColor,
setInfillMosaicMaxColor,
setInfillColorValue,
positivePromptChanged,
negativePromptChanged,
positivePrompt2Changed,
negativePrompt2Changed,
shouldConcatPromptsChanged,
} = generationSlice.actions;
export const { selectOptimalDimension } = generationSlice.selectors;
export const selectGenerationSlice = (state: RootState) => state.generation;
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
const migrateGenerationState = (state: any): GenerationState => {
if (!('_version' in state)) {
state._version = 1;
state.aspectRatio = initialAspectRatioState;
}
if (state._version === 1) {
// The signature of the model has changed, so we need to reset it
state._version = 2;
state.model = null;
state.canvasCoherenceMode = initialGenerationState.canvasCoherenceMode;
}
return state;
};
export const generationPersistConfig: PersistConfig<GenerationState> = {
name: generationSlice.name,
initialState: initialGenerationState,
migrate: migrateGenerationState,
persistDenylist: [],
};

View File

@ -1,59 +0,0 @@
import type { PayloadAction } from '@reduxjs/toolkit';
import type {
ParameterCanvasCoherenceMode,
ParameterCFGRescaleMultiplier,
ParameterCFGScale,
ParameterMaskBlurMethod,
ParameterModel,
ParameterNegativePrompt,
ParameterNegativeStylePromptSDXL,
ParameterPositivePrompt,
ParameterPositiveStylePromptSDXL,
ParameterPrecision,
ParameterScheduler,
ParameterSeed,
ParameterSteps,
ParameterStrength,
ParameterVAEModel,
} from 'features/parameters/types/parameterSchemas';
import type { RgbaColor } from 'react-colorful';
export interface GenerationState {
_version: 2;
cfgScale: ParameterCFGScale;
cfgRescaleMultiplier: ParameterCFGRescaleMultiplier;
img2imgStrength: ParameterStrength;
infillMethod: string;
iterations: number;
scheduler: ParameterScheduler;
maskBlur: number;
maskBlurMethod: ParameterMaskBlurMethod;
canvasCoherenceMode: ParameterCanvasCoherenceMode;
canvasCoherenceMinDenoise: ParameterStrength;
canvasCoherenceEdgeSize: number;
seed: ParameterSeed;
shouldRandomizeSeed: boolean;
steps: ParameterSteps;
model: ParameterModel | null;
vae: ParameterVAEModel | null;
vaePrecision: ParameterPrecision;
seamlessXAxis: boolean;
seamlessYAxis: boolean;
clipSkip: number;
shouldUseCpuNoise: boolean;
shouldShowAdvancedOptions: boolean;
infillTileSize: number;
infillPatchmatchDownscaleSize: number;
infillMosaicTileWidth: number;
infillMosaicTileHeight: number;
infillMosaicMinColor: RgbaColor;
infillMosaicMaxColor: RgbaColor;
infillColorValue: RgbaColor;
positivePrompt: ParameterPositivePrompt;
negativePrompt: ParameterNegativePrompt;
positivePrompt2: ParameterPositiveStylePromptSDXL;
negativePrompt2: ParameterNegativeStylePromptSDXL;
shouldConcatPrompts: boolean;
}
export type PayloadActionWithOptimalDimension<T = void> = PayloadAction<T, string, { optimalDimension: number }>;

View File

@ -1,11 +1,8 @@
import type { ChakraProps, ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui-library';
import { Combobox, FormControl } from '@invoke-ai/ui-library';
import { skipToken } from '@reduxjs/toolkit/query';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppSelector } from 'app/store/storeHooks';
import type { GroupBase } from 'chakra-react-select';
import { selectLoraSlice } from 'features/lora/store/loraSlice';
import { selectGenerationSlice } from 'features/parameters/store/generationSlice';
import type { PromptTriggerSelectProps } from 'features/prompt/types';
import { t } from 'i18next';
import { flatten, map } from 'lodash-es';
@ -17,14 +14,11 @@ import { isNonRefinerMainModelConfig } from 'services/api/types';
const noOptionsMessage = () => t('prompt.noMatchingTriggers');
const selectLoRAs = createMemoizedSelector(selectLoraSlice, (loras) => loras.loras);
const selectMainModel = createMemoizedSelector(selectGenerationSlice, (generation) => generation.model);
export const PromptTriggerSelect = memo(({ onSelect, onClose }: PromptTriggerSelectProps) => {
const { t } = useTranslation();
const mainModel = useAppSelector(selectMainModel);
const addedLoRAs = useAppSelector(selectLoRAs);
const mainModel = useAppSelector((s) => s.canvasV2.params.model);
const addedLoRAs = useAppSelector((s) => s.lora.loras);
const { data: mainModelConfig, isLoading: isLoadingMainModelConfig } = useGetModelConfigQuery(
mainModel?.key ?? skipToken
);

View File

@ -35,7 +35,7 @@ const TooltipContent = memo(({ prepend = false }: Props) => {
const { isReady, reasons } = useIsReadyToEnqueue();
const isLoadingDynamicPrompts = useAppSelector((s) => s.dynamicPrompts.isLoading);
const promptsCount = useAppSelector(selectPromptsCount);
const iterationsCount = useAppSelector((s) => s.generation.iterations);
const iterationsCount = useAppSelector((s) => s.canvasV2.params.iterations);
const autoAddBoardId = useAppSelector((s) => s.gallery.autoAddBoardId);
const autoAddBoardName = useBoardName(autoAddBoardId);
const [_, { isLoading }] = useEnqueueBatchMutation({

View File

@ -1,11 +1,11 @@
import { CompositeNumberInput } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { setIterations } from 'features/parameters/store/generationSlice';
import { setIterations } from 'features/canvas/store/canvasSlice';
import { memo, useCallback } from 'react';
export const QueueIterationsNumberInput = memo(() => {
const iterations = useAppSelector((s) => s.generation.iterations);
const iterations = useAppSelector((s) => s.canvasV2.params.iterations);
const coarseStep = useAppSelector((s) => s.config.sd.iterations.coarseStep);
const fineStep = useAppSelector((s) => s.config.sd.iterations.fineStep);
const dispatch = useAppDispatch();

View File

@ -1,8 +1,8 @@
import { Box, Textarea } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { negativePrompt2Changed } from 'features/controlLayers/store/canvasV2Slice';
import { PromptLabel } from 'features/parameters/components/Prompts/PromptLabel';
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
import { negativePrompt2Changed } from 'features/parameters/store/generationSlice';
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
import { PromptPopover } from 'features/prompt/PromptPopover';
import { usePrompt } from 'features/prompt/usePrompt';
@ -12,7 +12,7 @@ import { useTranslation } from 'react-i18next';
export const ParamSDXLNegativeStylePrompt = memo(() => {
const dispatch = useAppDispatch();
const prompt = useAppSelector((s) => s.generation.negativePrompt2);
const prompt = useAppSelector((s) => s.canvasV2.params.negativePrompt2);
const textareaRef = useRef<HTMLTextAreaElement>(null);
const { t } = useTranslation();
const handleChange = useCallback(

View File

@ -1,8 +1,8 @@
import { Box, Textarea } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { positivePrompt2Changed } from 'features/controlLayers/store/canvasV2Slice';
import { PromptLabel } from 'features/parameters/components/Prompts/PromptLabel';
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
import { positivePrompt2Changed } from 'features/parameters/store/generationSlice';
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
import { PromptPopover } from 'features/prompt/PromptPopover';
import { usePrompt } from 'features/prompt/usePrompt';
@ -11,7 +11,7 @@ import { useTranslation } from 'react-i18next';
export const ParamSDXLPositiveStylePrompt = memo(() => {
const dispatch = useAppDispatch();
const prompt = useAppSelector((s) => s.generation.positivePrompt2);
const prompt = useAppSelector((s) => s.canvasV2.params.positivePrompt2);
const textareaRef = useRef<HTMLTextAreaElement>(null);
const { t } = useTranslation();
const handleChange = useCallback(

View File

@ -1,12 +1,12 @@
import { IconButton, Tooltip } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { shouldConcatPromptsChanged } from 'features/parameters/store/generationSlice';
import { shouldConcatPromptsChanged } from 'features/controlLayers/store/canvasV2Slice';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiLinkSimpleBold, PiLinkSimpleBreakBold } from 'react-icons/pi';
export const SDXLConcatButton = memo(() => {
const shouldConcatPrompts = useAppSelector((s) => s.generation.shouldConcatPrompts);
const shouldConcatPrompts = useAppSelector((s) => s.canvasV2.params.shouldConcatPrompts);
const dispatch = useAppDispatch();
const { t } = useTranslation();

View File

@ -8,7 +8,7 @@ import { useTranslation } from 'react-i18next';
const ParamSDXLRefinerCFGScale = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const refinerCFGScale = useAppSelector((s) => s.sdxl.refinerCFGScale);
const refinerCFGScale = useAppSelector((s) => s.canvasV2.params.refinerCFGScale);
const sliderMin = useAppSelector((s) => s.config.sd.guidance.sliderMin);
const sliderMax = useAppSelector((s) => s.config.sd.guidance.sliderMax);
const numberInputMin = useAppSelector((s) => s.config.sd.guidance.numberInputMin);

View File

@ -6,7 +6,7 @@ import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
const ParamSDXLRefinerNegativeAestheticScore = () => {
const refinerNegativeAestheticScore = useAppSelector((s) => s.sdxl.refinerNegativeAestheticScore);
const refinerNegativeAestheticScore = useAppSelector((s) => s.canvasV2.params.refinerNegativeAestheticScore);
const dispatch = useAppDispatch();
const { t } = useTranslation();

View File

@ -6,7 +6,7 @@ import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
const ParamSDXLRefinerPositiveAestheticScore = () => {
const refinerPositiveAestheticScore = useAppSelector((s) => s.sdxl.refinerPositiveAestheticScore);
const refinerPositiveAestheticScore = useAppSelector((s) => s.canvasV2.params.refinerPositiveAestheticScore);
const dispatch = useAppDispatch();
const { t } = useTranslation();

View File

@ -11,7 +11,7 @@ import { useTranslation } from 'react-i18next';
const ParamSDXLRefinerScheduler = () => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const refinerScheduler = useAppSelector((s) => s.sdxl.refinerScheduler);
const refinerScheduler = useAppSelector((s) => s.canvasV2.params.refinerScheduler);
const onChange = useCallback<ComboboxOnChange>(
(v) => {

View File

@ -6,7 +6,7 @@ import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
const ParamSDXLRefinerStart = () => {
const refinerStart = useAppSelector((s) => s.sdxl.refinerStart);
const refinerStart = useAppSelector((s) => s.canvasV2.params.refinerStart);
const dispatch = useAppDispatch();
const handleChange = useCallback((v: number) => dispatch(setRefinerStart(v)), [dispatch]);
const { t } = useTranslation();

View File

@ -8,7 +8,7 @@ import { useTranslation } from 'react-i18next';
const ParamSDXLRefinerSteps = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const refinerSteps = useAppSelector((s) => s.sdxl.refinerSteps);
const refinerSteps = useAppSelector((s) => s.canvasV2.params.refinerSteps);
const initial = useAppSelector((s) => s.config.sd.steps.initial);
const sliderMin = useAppSelector((s) => s.config.sd.steps.sliderMin);
const sliderMax = useAppSelector((s) => s.config.sd.steps.sliderMax);

View File

@ -1,86 +0,0 @@
import type { PayloadAction } from '@reduxjs/toolkit';
import { createSlice } from '@reduxjs/toolkit';
import type { PersistConfig, RootState } from 'app/store/store';
import type { ParameterScheduler, ParameterSDXLRefinerModel } from 'features/parameters/types/parameterSchemas';
type SDXLState = {
_version: 2;
refinerModel: ParameterSDXLRefinerModel | null;
refinerSteps: number;
refinerCFGScale: number;
refinerScheduler: ParameterScheduler;
refinerPositiveAestheticScore: number;
refinerNegativeAestheticScore: number;
refinerStart: number;
};
const initialSDXLState: SDXLState = {
_version: 2,
refinerModel: null,
refinerSteps: 20,
refinerCFGScale: 7.5,
refinerScheduler: 'euler',
refinerPositiveAestheticScore: 6,
refinerNegativeAestheticScore: 2.5,
refinerStart: 0.8,
};
export const sdxlSlice = createSlice({
name: 'sdxl',
initialState: initialSDXLState,
reducers: {
refinerModelChanged: (state, action: PayloadAction<ParameterSDXLRefinerModel | null>) => {
state.refinerModel = action.payload;
},
setRefinerSteps: (state, action: PayloadAction<number>) => {
state.refinerSteps = action.payload;
},
setRefinerCFGScale: (state, action: PayloadAction<number>) => {
state.refinerCFGScale = action.payload;
},
setRefinerScheduler: (state, action: PayloadAction<ParameterScheduler>) => {
state.refinerScheduler = action.payload;
},
setRefinerPositiveAestheticScore: (state, action: PayloadAction<number>) => {
state.refinerPositiveAestheticScore = action.payload;
},
setRefinerNegativeAestheticScore: (state, action: PayloadAction<number>) => {
state.refinerNegativeAestheticScore = action.payload;
},
setRefinerStart: (state, action: PayloadAction<number>) => {
state.refinerStart = action.payload;
},
},
});
export const {
refinerModelChanged,
setRefinerSteps,
setRefinerCFGScale,
setRefinerScheduler,
setRefinerPositiveAestheticScore,
setRefinerNegativeAestheticScore,
setRefinerStart,
} = sdxlSlice.actions;
export const selectSdxlSlice = (state: RootState) => state.sdxl;
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
const migrateSDXLState = (state: any): any => {
if (!('_version' in state)) {
state._version = 1;
}
if (state._version === 1) {
// Model type has changed, so we need to reset the state - too risky to migrate
state._version = 2;
state.refinerModel = null;
}
return state;
};
export const sdxlPersistConfig: PersistConfig<SDXLState> = {
name: sdxlSlice.name,
initialState: initialSDXLState,
migrate: migrateSDXLState,
persistDenylist: [],
};

View File

@ -12,7 +12,7 @@ import { ParamSeedRandomize } from 'features/parameters/components/Seed/ParamSee
import { ParamSeedShuffle } from 'features/parameters/components/Seed/ParamSeedShuffle';
import ParamVAEModelSelect from 'features/parameters/components/VAEModel/ParamVAEModelSelect';
import ParamVAEPrecision from 'features/parameters/components/VAEModel/ParamVAEPrecision';
import { selectGenerationSlice } from 'features/parameters/store/generationSlice';
import { selectGenerationSlice } from 'features/canvas/store/canvasSlice';
import { useStandaloneAccordionToggle } from 'features/settingsAccordions/hooks/useStandaloneAccordionToggle';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { memo, useMemo } from 'react';
@ -28,7 +28,7 @@ const formLabelProps2: FormLabelProps = {
};
export const AdvancedSettingsAccordion = memo(() => {
const vaeKey = useAppSelector((state) => state.generation.vae?.key);
const vaeKey = useAppSelector((state) => state.canvasV2.params.vae?.key);
const { currentData: vaeConfig } = useGetModelConfigQuery(vaeKey ?? skipToken);
const activeTabName = useAppSelector(activeTabNameSelector);

View File

@ -12,7 +12,7 @@ import ParamImageToImageStrength from 'features/parameters/components/Canvas/Par
import { ParamSeedNumberInput } from 'features/parameters/components/Seed/ParamSeedNumberInput';
import { ParamSeedRandomize } from 'features/parameters/components/Seed/ParamSeedRandomize';
import { ParamSeedShuffle } from 'features/parameters/components/Seed/ParamSeedShuffle';
import { selectGenerationSlice } from 'features/parameters/store/generationSlice';
import { selectGenerationSlice } from 'features/canvas/store/canvasSlice';
import { useExpanderToggle } from 'features/settingsAccordions/hooks/useExpanderToggle';
import { useStandaloneAccordionToggle } from 'features/settingsAccordions/hooks/useStandaloneAccordionToggle';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';

View File

@ -58,7 +58,7 @@ const RefinerSettingsAccordionNoRefiner: React.FC = memo(() => {
RefinerSettingsAccordionNoRefiner.displayName = 'RefinerSettingsAccordionNoRefiner';
const RefinerSettingsAccordionContent: React.FC = memo(() => {
const isRefinerModelSelected = useAppSelector((state) => !isNil(state.sdxl.refinerModel));
const isRefinerModelSelected = useAppSelector((state) => !isNil(state.canvasV2.params.refinerModel));
return (
<FormControlGroup isDisabled={!isRefinerModelSelected}>

View File

@ -19,7 +19,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
import { useClearStorage } from 'common/hooks/useClearStorage';
import { shouldUseCpuNoiseChanged } from 'features/parameters/store/generationSlice';
import { shouldUseCpuNoiseChanged } from 'features/canvas/store/canvasSlice';
import { useClearIntermediates } from 'features/system/components/SettingsModal/useClearIntermediates';
import { StickyScrollable } from 'features/system/components/StickyScrollable';
import {
@ -88,7 +88,7 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => {
const { isOpen: isRefreshModalOpen, onOpen: onRefreshModalOpen, onClose: onRefreshModalClose } = useDisclosure();
const shouldUseCpuNoise = useAppSelector((s) => s.generation.shouldUseCpuNoise);
const shouldUseCpuNoise = useAppSelector((s) => s.canvasV2.params.shouldUseCpuNoise);
const shouldConfirmOnDelete = useAppSelector((s) => s.system.shouldConfirmOnDelete);
const enableImageDebugging = useAppSelector((s) => s.system.enableImageDebugging);
const shouldShowProgressInViewer = useAppSelector((s) => s.ui.shouldShowProgressInViewer);

View File

@ -16,7 +16,6 @@ import { RefinerSettingsAccordion } from 'features/settingsAccordions/components
import { StylePresetMenu } from 'features/stylePresets/components/StylePresetMenu';
import { StylePresetMenuTrigger } from 'features/stylePresets/components/StylePresetMenuTrigger';
import { $isMenuOpen } from 'features/stylePresets/store/isMenuOpen';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
import type { CSSProperties } from 'react';
import { memo, useCallback, useMemo, useRef } from 'react';
@ -42,15 +41,14 @@ const selectedStyles: ChakraProps['sx'] = {
const ParametersPanelTextToImage = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const activeTabName = useAppSelector(activeTabNameSelector);
const controlLayersCount = useAppSelector((s) => s.layers.layers.length);
const controlLayersCount = useAppSelector((s) => s.canvasV2.layers.length);
const controlLayersTitle = useMemo(() => {
if (controlLayersCount === 0) {
return t('controlLayers.controlLayers');
}
return `${t('controlLayers.controlLayers')} (${controlLayersCount})`;
}, [controlLayersCount, t]);
const isSDXL = useAppSelector((s) => s.generation.model?.base === 'sdxl');
const isSDXL = useAppSelector((s) => s.canvasV2.params.model?.base === 'sdxl');
const onChangeTabs = useCallback(
(i: number) => {
if (i === 1) {

View File

@ -1,7 +1,7 @@
import { createSelector } from '@reduxjs/toolkit';
import { skipToken } from '@reduxjs/toolkit/query';
import { useAppSelector } from 'app/store/storeHooks';
import { selectGenerationSlice } from 'features/parameters/store/generationSlice';
import { selectGenerationSlice } from 'features/canvas/store/canvasSlice';
import { useGetModelConfigQuery } from 'services/api/endpoints/models';
const selectModelKey = createSelector(selectGenerationSlice, (generation) => generation.model?.key);