feat(ui): split out params/compositing state from canvas rendering state

First step to restoring undo/redo - the undoable state must be in its own slice. So params and settings must be isolated.
This commit is contained in:
psychedelicious 2024-08-26 21:41:47 +10:00
parent 301da97670
commit 1303e18e93
83 changed files with 530 additions and 523 deletions

View File

@ -1,5 +1,5 @@
import { PropsWithChildren, memo, useEffect } from 'react';
import { modelChanged } from '../src/features/controlLayers/store/canvasV2Slice';
import { modelChanged } from '../src/features/controlLayers/store/paramsSlice';
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/controlLayers/store/canvasV2Slice';
import { setInfillMethod } from 'features/controlLayers/store/paramsSlice';
import { shouldUseNSFWCheckerChanged, shouldUseWatermarkerChanged } from 'features/system/store/systemSlice';
import { appInfoApi } from 'services/api/endpoints/appInfo';
@ -8,7 +8,7 @@ export const addAppConfigReceivedListener = (startAppListening: AppStartListenin
matcher: appInfoApi.endpoints.getAppConfig.matchFulfilled,
effect: (action, { getState, dispatch }) => {
const { infill_methods = [], nsfw_methods = [], watermarking_methods = [] } = action.payload;
const infillMethod = getState().canvasV2.compositing.infillMethod;
const infillMethod = getState().params.infillMethod;
if (!infill_methods.includes(infillMethod)) {
// if there is no infill method, set it to the first one

View File

@ -23,7 +23,7 @@ export const addEnqueueRequestedLinear = (startAppListening: AppStartListening)
enqueueRequested.match(action) && action.payload.tabName === 'generation',
effect: async (action, { getState, dispatch }) => {
const state = getState();
const model = state.canvasV2.params.model;
const model = state.params.model;
const { prepend } = action.payload;
const manager = $canvasManager.get();

View File

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

View File

@ -1,6 +1,7 @@
import { logger } from 'app/logging/logger';
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
import { loraDeleted, modelChanged, vaeSelected } from 'features/controlLayers/store/canvasV2Slice';
import { loraDeleted } from 'features/controlLayers/store/canvasV2Slice';
import { modelChanged, vaeSelected } from 'features/controlLayers/store/paramsSlice';
import { modelSelected } from 'features/parameters/store/actions';
import { zParameterModel } from 'features/parameters/types/parameterSchemas';
import { toast } from 'features/toast/toast';
@ -23,7 +24,7 @@ export const addModelSelectedListener = (startAppListening: AppStartListening) =
const newModel = result.data;
const newBaseModel = newModel.base;
const didBaseModelChange = state.canvasV2.params.model?.base !== newBaseModel;
const didBaseModelChange = state.params.model?.base !== newBaseModel;
if (didBaseModelChange) {
// we may need to reset some incompatible submodels
@ -38,7 +39,7 @@ export const addModelSelectedListener = (startAppListening: AppStartListening) =
});
// handle incompatible vae
const { vae } = state.canvasV2.params;
const { vae } = state.params;
if (vae && vae.base !== newBaseModel) {
dispatch(vaeSelected(null));
modelsCleared += 1;
@ -66,7 +67,7 @@ export const addModelSelectedListener = (startAppListening: AppStartListening) =
}
}
dispatch(modelChanged({ model: newModel, previousModel: state.canvasV2.params.model }));
dispatch(modelChanged({ model: newModel, previousModel: state.params.model }));
},
});
};

View File

@ -8,11 +8,9 @@ import {
controlLayerModelChanged,
ipaModelChanged,
loraDeleted,
modelChanged,
refinerModelChanged,
rgIPAdapterModelChanged,
vaeSelected,
} from 'features/controlLayers/store/canvasV2Slice';
import { modelChanged, refinerModelChanged, vaeSelected } from 'features/controlLayers/store/paramsSlice';
import { getEntityIdentifier } from 'features/controlLayers/store/types';
import { calculateNewSize } from 'features/parameters/components/DocumentSize/calculateNewSize';
import { postProcessingModelChanged, upscaleModelChanged } from 'features/parameters/store/upscaleSlice';
@ -63,7 +61,7 @@ type ModelHandler = (
) => undefined;
const handleMainModels: ModelHandler = (models, state, dispatch, log) => {
const currentModel = state.canvasV2.params.model;
const currentModel = state.params.model;
const mainModels = models.filter(isNonRefinerMainModelConfig);
if (mainModels.length === 0) {
// No models loaded at all
@ -110,7 +108,7 @@ const handleMainModels: ModelHandler = (models, state, dispatch, log) => {
};
const handleRefinerModels: ModelHandler = (models, state, dispatch, _log) => {
const currentRefinerModel = state.canvasV2.params.refinerModel;
const currentRefinerModel = state.params.refinerModel;
const refinerModels = models.filter(isRefinerMainModelModelConfig);
if (models.length === 0) {
// No models loaded at all
@ -129,7 +127,7 @@ const handleRefinerModels: ModelHandler = (models, state, dispatch, _log) => {
};
const handleVAEModels: ModelHandler = (models, state, dispatch, log) => {
const currentVae = state.canvasV2.params.vae;
const currentVae = state.params.vae;
if (currentVae === null) {
// null is a valid VAE! it means "use the default with the main model"

View File

@ -1,6 +1,6 @@
import { isAnyOf } from '@reduxjs/toolkit';
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
import { positivePromptChanged } from 'features/controlLayers/store/canvasV2Slice';
import { positivePromptChanged } from 'features/controlLayers/store/paramsSlice';
import {
combinatorialToggled,
isErrorChanged,

View File

@ -1,14 +1,13 @@
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
import { bboxHeightChanged, bboxWidthChanged } from 'features/controlLayers/store/canvasV2Slice';
import {
bboxHeightChanged,
bboxWidthChanged,
setCfgRescaleMultiplier,
setCfgScale,
setScheduler,
setSteps,
vaePrecisionChanged,
vaeSelected,
} from 'features/controlLayers/store/canvasV2Slice';
} from 'features/controlLayers/store/paramsSlice';
import { setDefaultSettings } from 'features/parameters/store/actions';
import {
isParameterCFGRescaleMultiplier,
@ -31,7 +30,7 @@ export const addSetDefaultSettingsListener = (startAppListening: AppStartListeni
effect: async (action, { dispatch, getState }) => {
const state = getState();
const currentModel = state.canvasV2.params.model;
const currentModel = state.params.model;
if (!currentModel) {
return;

View File

@ -7,6 +7,7 @@ import type { SerializableObject } from 'common/types';
import { deepClone } from 'common/util/deepClone';
import { changeBoardModalSlice } from 'features/changeBoardModal/store/slice';
import { canvasV2PersistConfig, canvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
import { paramsPersistConfig, paramsSlice } from 'features/controlLayers/store/paramsSlice';
import { deleteImageModalSlice } from 'features/deleteImageModal/store/slice';
import { dynamicPromptsPersistConfig, dynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
import { galleryPersistConfig, gallerySlice } from 'features/gallery/store/gallerySlice';
@ -57,6 +58,7 @@ const allReducers = {
[workflowSettingsSlice.name]: workflowSettingsSlice.reducer,
[upscaleSlice.name]: upscaleSlice.reducer,
[stylePresetSlice.name]: stylePresetSlice.reducer,
[paramsSlice.name]: paramsSlice.reducer,
};
const rootReducer = combineReducers(allReducers);
@ -98,6 +100,7 @@ const persistConfigs: { [key in keyof typeof allReducers]?: PersistConfig } = {
[workflowSettingsPersistConfig.name]: workflowSettingsPersistConfig,
[upscalePersistConfig.name]: upscalePersistConfig,
[stylePresetPersistConfig.name]: stylePresetPersistConfig,
[paramsPersistConfig.name]: paramsPersistConfig,
};
const unserialize: UnserializeFunction = (data, key) => {

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.canvasV2.params.model?.base ?? 'sdxl');
const base_model = useAppSelector((s) => s.params.model?.base ?? 'sdxl');
const { modelConfigs, selectedModel, getIsDisabled, onChange, isLoading, groupByType = false } = arg;
const options = useMemo<GroupBase<ComboboxOption>[]>(() => {
if (!modelConfigs) {

View File

@ -2,6 +2,7 @@ import { useStore } from '@nanostores/react';
import { $isConnected } from 'app/hooks/useSocketIO';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppSelector } from 'app/store/storeHooks';
import { selectParamsSlice } from 'features/controlLayers/store/paramsSlice';
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
import { getShouldProcessPrompt } from 'features/dynamicPrompts/util/getShouldProcessPrompt';
@ -34,13 +35,14 @@ const createSelector = (templates: Templates, isConnected: boolean) =>
selectWorkflowSettingsSlice,
selectDynamicPromptsSlice,
selectCanvasV2Slice,
selectParamsSlice,
selectUpscalelice,
selectConfigSlice,
selectActiveTab,
],
(system, nodes, workflowSettings, dynamicPrompts, canvasV2, upscale, config, activeTabName) => {
(system, nodes, workflowSettings, dynamicPrompts, canvasV2, params, upscale, config, activeTabName) => {
const { bbox } = canvasV2;
const { model, positivePrompt } = canvasV2.params;
const { model, positivePrompt } = params;
const reasons: { prefix?: string; content: string }[] = [];

View File

@ -18,7 +18,7 @@ export const ControlLayerControlAdapterModel = memo(({ modelKey, onChange: onCha
const { t } = useTranslation();
const entityIdentifier = useEntityIdentifierContext();
const canvasManager = useCanvasManager();
const currentBaseModel = useAppSelector((s) => s.canvasV2.params.model?.base);
const currentBaseModel = useAppSelector((s) => s.params.model?.base);
const [modelConfigs, { isLoading }] = useControlNetAndT2IAdapterModels();
const selectedModel = useMemo(() => modelConfigs.find((m) => m.key === modelKey), [modelConfigs, modelKey]);

View File

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

View File

@ -30,7 +30,7 @@ export const useControlLayerControlAdapter = (entityIdentifier: CanvasEntityIden
export const useDefaultControlAdapter = (): ControlNetConfig | T2IAdapterConfig => {
const [modelConfigs] = useControlNetAndT2IAdapterModels();
const baseModel = useAppSelector((s) => s.canvasV2.params.model?.base);
const baseModel = useAppSelector((s) => s.params.model?.base);
const defaultControlAdapter = useMemo(() => {
const compatibleModels = modelConfigs.filter((m) => (baseModel ? m.base === baseModel : true));
@ -51,7 +51,7 @@ export const useDefaultControlAdapter = (): ControlNetConfig | T2IAdapterConfig
export const useDefaultIPAdapter = (): IPAdapterConfig => {
const [modelConfigs] = useIPAdapterModels();
const baseModel = useAppSelector((s) => s.canvasV2.params.model?.base);
const baseModel = useAppSelector((s) => s.params.model?.base);
const defaultControlAdapter = useMemo(() => {
const compatibleModels = modelConfigs.filter((m) => (baseModel ? m.base === baseModel : true));

View File

@ -6,14 +6,12 @@ import { getScaledBoundingBoxDimensions } from 'features/controlLayers/util/getS
import { calculateNewSize } from 'features/parameters/components/DocumentSize/calculateNewSize';
import { ASPECT_RATIO_MAP, initialAspectRatioState } from 'features/parameters/components/DocumentSize/constants';
import type { AspectRatioID } from 'features/parameters/components/DocumentSize/types';
import { getOptimalDimension } from 'features/parameters/util/optimalDimension';
import type { IRect } from 'konva/lib/types';
const syncScaledSize = (state: CanvasV2State) => {
if (state.bbox.scaleMethod === 'auto') {
const optimalDimension = getOptimalDimension(state.params.model);
const { width, height } = state.bbox.rect;
state.bbox.scaledSize = getScaledBoundingBoxDimensions({ width, height }, optimalDimension);
state.bbox.scaledSize = getScaledBoundingBoxDimensions({ width, height }, state.bbox.optimalDimension);
}
};
@ -106,15 +104,14 @@ export const bboxReducers = {
syncScaledSize(state);
},
bboxSizeOptimized: (state) => {
const optimalDimension = getOptimalDimension(state.params.model);
if (state.bbox.aspectRatio.isLocked) {
const { width, height } = calculateNewSize(state.bbox.aspectRatio.value, optimalDimension ** 2);
const { width, height } = calculateNewSize(state.bbox.aspectRatio.value, state.bbox.optimalDimension ** 2);
state.bbox.rect.width = width;
state.bbox.rect.height = height;
} else {
state.bbox.aspectRatio = deepClone(initialAspectRatioState);
state.bbox.rect.width = optimalDimension;
state.bbox.rect.height = optimalDimension;
state.bbox.rect.width = state.bbox.optimalDimension;
state.bbox.rect.height = state.bbox.optimalDimension;
}
syncScaledSize(state);

View File

@ -5,12 +5,11 @@ import { moveOneToEnd, moveOneToStart, moveToEnd, moveToStart } from 'common/uti
import { deepClone } from 'common/util/deepClone';
import { getPrefixedId } from 'features/controlLayers/konva/util';
import { bboxReducers } from 'features/controlLayers/store/bboxReducers';
import { compositingReducers } from 'features/controlLayers/store/compositingReducers';
import { controlLayersReducers } from 'features/controlLayers/store/controlLayersReducers';
import { inpaintMaskReducers } from 'features/controlLayers/store/inpaintMaskReducers';
import { ipAdaptersReducers } from 'features/controlLayers/store/ipAdaptersReducers';
import { lorasReducers } from 'features/controlLayers/store/lorasReducers';
import { paramsReducers } from 'features/controlLayers/store/paramsReducers';
import { modelChanged } from 'features/controlLayers/store/paramsSlice';
import { rasterLayersReducers } from 'features/controlLayers/store/rasterLayersReducers';
import { regionsReducers } from 'features/controlLayers/store/regionsReducers';
import { selectAllEntities, selectAllEntitiesOfType, selectEntity } from 'features/controlLayers/store/selectors';
@ -19,8 +18,9 @@ import { settingsReducers } from 'features/controlLayers/store/settingsReducers'
import { toolReducers } from 'features/controlLayers/store/toolReducers';
import { getScaledBoundingBoxDimensions } from 'features/controlLayers/util/getScaledBoundingBoxDimensions';
import { simplifyFlatNumbersArray } from 'features/controlLayers/util/simplify';
import { calculateNewSize } from 'features/parameters/components/DocumentSize/calculateNewSize';
import { initialAspectRatioState } from 'features/parameters/components/DocumentSize/constants';
import { getOptimalDimension } from 'features/parameters/util/optimalDimension';
import { getIsSizeOptimal, getOptimalDimension } from 'features/parameters/util/optimalDimension';
import { pick } from 'lodash-es';
import { assert } from 'tsafe';
@ -69,6 +69,7 @@ const initialState: CanvasV2State = {
},
bbox: {
rect: { x: 0, y: 0, width: 512, height: 512 },
optimalDimension: 512,
aspectRatio: deepClone(initialAspectRatioState),
scaleMethod: 'auto',
scaledSize: {
@ -86,46 +87,6 @@ const initialState: CanvasV2State = {
cropToBboxOnSave: false,
dynamicGrid: false,
},
compositing: {
maskBlur: 16,
maskBlurMethod: 'box',
canvasCoherenceMode: 'Gaussian Blur',
canvasCoherenceMinDenoise: 0,
canvasCoherenceEdgeSize: 16,
infillMethod: 'patchmatch',
infillTileSize: 32,
infillPatchmatchDownscaleSize: 1,
infillColorValue: { r: 0, g: 0, b: 0, a: 1 },
},
params: {
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,
positivePrompt: '',
negativePrompt: '',
positivePrompt2: '',
negativePrompt2: '',
shouldConcatPrompts: true,
refinerModel: null,
refinerSteps: 20,
refinerCFGScale: 7.5,
refinerScheduler: 'euler',
refinerPositiveAestheticScore: 6,
refinerNegativeAestheticScore: 2.5,
refinerStart: 0.8,
},
session: {
mode: 'generate',
isStaging: false,
@ -147,8 +108,6 @@ export const canvasV2Slice = createSlice({
...bboxReducers,
// move out
...lorasReducers,
...paramsReducers,
...compositingReducers,
...settingsReducers,
...toolReducers,
...sessionReducers,
@ -400,11 +359,10 @@ export const canvasV2Slice = createSlice({
},
canvasReset: (state) => {
state.bbox = deepClone(initialState.bbox);
const optimalDimension = getOptimalDimension(state.params.model);
state.bbox.rect.width = optimalDimension;
state.bbox.rect.height = optimalDimension;
state.bbox.rect.width = state.bbox.optimalDimension;
state.bbox.rect.height = state.bbox.optimalDimension;
const size = pick(state.bbox.rect, 'width', 'height');
state.bbox.scaledSize = getScaledBoundingBoxDimensions(size, optimalDimension);
state.bbox.scaledSize = getScaledBoundingBoxDimensions(size, state.bbox.optimalDimension);
state.session = deepClone(initialState.session);
state.tool = deepClone(initialState.tool);
@ -417,6 +375,31 @@ export const canvasV2Slice = createSlice({
state.selectedEntityIdentifier = deepClone(initialState.selectedEntityIdentifier);
},
},
extraReducers(builder) {
builder.addCase(modelChanged, (state, action) => {
const { model, previousModel } = action.payload;
// If the model base changes (e.g. SD1.5 -> SDXL), we need to change a few things
if (model === null || previousModel?.base === model.base) {
return;
}
// Update the bbox size to match the new model's optimal size
const optimalDimension = getOptimalDimension(model);
state.bbox.optimalDimension = optimalDimension;
if (!getIsSizeOptimal(state.bbox.rect.width, state.bbox.rect.height, optimalDimension)) {
const bboxDims = calculateNewSize(state.bbox.aspectRatio.value, optimalDimension * optimalDimension);
state.bbox.rect.width = bboxDims.width;
state.bbox.rect.height = bboxDims.height;
if (state.bbox.scaleMethod === 'auto') {
state.bbox.scaledSize = getScaledBoundingBoxDimensions(bboxDims, optimalDimension);
}
}
});
},
});
export const {
@ -495,43 +478,6 @@ export const {
rgIPAdapterMethodChanged,
rgIPAdapterModelChanged,
rgIPAdapterCLIPVisionModelChanged,
// Compositing
setInfillMethod,
setInfillTileSize,
setInfillPatchmatchDownscaleSize,
setInfillColorValue,
setMaskBlur,
setCanvasCoherenceMode,
setCanvasCoherenceEdgeSize,
setCanvasCoherenceMinDenoise,
// Parameters
setIterations,
setSteps,
setCfgScale,
setCfgRescaleMultiplier,
setScheduler,
setSeed,
setImg2imgStrength,
setSeamlessXAxis,
setSeamlessYAxis,
setShouldRandomizeSeed,
vaeSelected,
vaePrecisionChanged,
setClipSkip,
shouldUseCpuNoiseChanged,
positivePromptChanged,
negativePromptChanged,
positivePrompt2Changed,
negativePrompt2Changed,
shouldConcatPromptsChanged,
refinerModelChanged,
setRefinerSteps,
setRefinerCFGScale,
setRefinerScheduler,
setRefinerPositiveAestheticScore,
setRefinerNegativeAestheticScore,
setRefinerStart,
modelChanged,
// LoRAs
loraAdded,
loraRecalled,

View File

@ -1,30 +0,0 @@
import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit';
import type { CanvasV2State, RgbaColor } from 'features/controlLayers/store/types';
import type { ParameterCanvasCoherenceMode } from 'features/parameters/types/parameterSchemas';
export const compositingReducers = {
setInfillMethod: (state, action: PayloadAction<string>) => {
state.compositing.infillMethod = action.payload;
},
setInfillTileSize: (state, action: PayloadAction<number>) => {
state.compositing.infillTileSize = action.payload;
},
setInfillPatchmatchDownscaleSize: (state, action: PayloadAction<number>) => {
state.compositing.infillPatchmatchDownscaleSize = action.payload;
},
setInfillColorValue: (state, action: PayloadAction<RgbaColor>) => {
state.compositing.infillColorValue = action.payload;
},
setMaskBlur: (state, action: PayloadAction<number>) => {
state.compositing.maskBlur = action.payload;
},
setCanvasCoherenceMode: (state, action: PayloadAction<ParameterCanvasCoherenceMode>) => {
state.compositing.canvasCoherenceMode = action.payload;
},
setCanvasCoherenceEdgeSize: (state, action: PayloadAction<number>) => {
state.compositing.canvasCoherenceEdgeSize = action.payload;
},
setCanvasCoherenceMinDenoise: (state, action: PayloadAction<number>) => {
state.compositing.canvasCoherenceMinDenoise = action.payload;
},
} satisfies SliceCaseReducers<CanvasV2State>;

View File

@ -1,132 +0,0 @@
import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit';
import type { CanvasV2State } from 'features/controlLayers/store/types';
import { getScaledBoundingBoxDimensions } from 'features/controlLayers/util/getScaledBoundingBoxDimensions';
import { calculateNewSize } from 'features/parameters/components/DocumentSize/calculateNewSize';
import { CLIP_SKIP_MAP } from 'features/parameters/types/constants';
import type {
ParameterCFGRescaleMultiplier,
ParameterCFGScale,
ParameterModel,
ParameterPrecision,
ParameterScheduler,
ParameterSDXLRefinerModel,
ParameterVAEModel,
} from 'features/parameters/types/parameterSchemas';
import { getIsSizeOptimal, getOptimalDimension } from 'features/parameters/util/optimalDimension';
import { clamp } from 'lodash-es';
export const paramsReducers = {
setIterations: (state, action: PayloadAction<number>) => {
state.params.iterations = action.payload;
},
setSteps: (state, action: PayloadAction<number>) => {
state.params.steps = action.payload;
},
setCfgScale: (state, action: PayloadAction<ParameterCFGScale>) => {
state.params.cfgScale = action.payload;
},
setCfgRescaleMultiplier: (state, action: PayloadAction<ParameterCFGRescaleMultiplier>) => {
state.params.cfgRescaleMultiplier = action.payload;
},
setScheduler: (state, action: PayloadAction<ParameterScheduler>) => {
state.params.scheduler = action.payload;
},
setSeed: (state, action: PayloadAction<number>) => {
state.params.seed = action.payload;
state.params.shouldRandomizeSeed = false;
},
setImg2imgStrength: (state, action: PayloadAction<number>) => {
state.params.img2imgStrength = action.payload;
},
setSeamlessXAxis: (state, action: PayloadAction<boolean>) => {
state.params.seamlessXAxis = action.payload;
},
setSeamlessYAxis: (state, action: PayloadAction<boolean>) => {
state.params.seamlessYAxis = action.payload;
},
setShouldRandomizeSeed: (state, action: PayloadAction<boolean>) => {
state.params.shouldRandomizeSeed = action.payload;
},
modelChanged: (
state,
action: PayloadAction<{ model: ParameterModel | null; previousModel?: ParameterModel | null }>
) => {
const { model, previousModel } = action.payload;
state.params.model = model;
// If the model base changes (e.g. SD1.5 -> SDXL), we need to change a few things
if (model === null || previousModel?.base === model.base) {
return;
}
// Update the bbox size to match the new model's optimal size
const optimalDimension = getOptimalDimension(model);
if (!getIsSizeOptimal(state.bbox.rect.width, state.bbox.rect.height, optimalDimension)) {
const bboxDims = calculateNewSize(state.bbox.aspectRatio.value, optimalDimension * optimalDimension);
state.bbox.rect.width = bboxDims.width;
state.bbox.rect.height = bboxDims.height;
if (state.bbox.scaleMethod === 'auto') {
state.bbox.scaledSize = getScaledBoundingBoxDimensions(bboxDims, optimalDimension);
}
}
// Clamp CLIP skip layer count to the bounds of the new model
if (model.base === 'sdxl') {
// We don't support user-defined CLIP skip for SDXL because it doesn't do anything useful
state.params.clipSkip = 0;
} else {
const { maxClip } = CLIP_SKIP_MAP[model.base];
state.params.clipSkip = clamp(state.params.clipSkip, 0, maxClip);
}
},
vaeSelected: (state, action: PayloadAction<ParameterVAEModel | null>) => {
// null is a valid VAE!
state.params.vae = action.payload;
},
vaePrecisionChanged: (state, action: PayloadAction<ParameterPrecision>) => {
state.params.vaePrecision = action.payload;
},
setClipSkip: (state, action: PayloadAction<number>) => {
state.params.clipSkip = action.payload;
},
shouldUseCpuNoiseChanged: (state, action: PayloadAction<boolean>) => {
state.params.shouldUseCpuNoise = action.payload;
},
positivePromptChanged: (state, action: PayloadAction<string>) => {
state.params.positivePrompt = action.payload;
},
negativePromptChanged: (state, action: PayloadAction<string>) => {
state.params.negativePrompt = action.payload;
},
positivePrompt2Changed: (state, action: PayloadAction<string>) => {
state.params.positivePrompt2 = action.payload;
},
negativePrompt2Changed: (state, action: PayloadAction<string>) => {
state.params.negativePrompt2 = action.payload;
},
shouldConcatPromptsChanged: (state, action: PayloadAction<boolean>) => {
state.params.shouldConcatPrompts = action.payload;
},
refinerModelChanged: (state, action: PayloadAction<ParameterSDXLRefinerModel | null>) => {
state.params.refinerModel = action.payload;
},
setRefinerSteps: (state, action: PayloadAction<number>) => {
state.params.refinerSteps = action.payload;
},
setRefinerCFGScale: (state, action: PayloadAction<number>) => {
state.params.refinerCFGScale = action.payload;
},
setRefinerScheduler: (state, action: PayloadAction<ParameterScheduler>) => {
state.params.refinerScheduler = action.payload;
},
setRefinerPositiveAestheticScore: (state, action: PayloadAction<number>) => {
state.params.refinerPositiveAestheticScore = action.payload;
},
setRefinerNegativeAestheticScore: (state, action: PayloadAction<number>) => {
state.params.refinerNegativeAestheticScore = action.payload;
},
setRefinerStart: (state, action: PayloadAction<number>) => {
state.params.refinerStart = action.payload;
},
} satisfies SliceCaseReducers<CanvasV2State>;

View File

@ -0,0 +1,285 @@
import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
import type { PersistConfig, RootState } from 'app/store/store';
import type { RgbaColor } from 'features/controlLayers/store/types';
import { CLIP_SKIP_MAP } from 'features/parameters/types/constants';
import type {
ParameterCanvasCoherenceMode,
ParameterCFGRescaleMultiplier,
ParameterCFGScale,
ParameterMaskBlurMethod,
ParameterModel,
ParameterNegativePrompt,
ParameterNegativeStylePromptSDXL,
ParameterPositivePrompt,
ParameterPositiveStylePromptSDXL,
ParameterPrecision,
ParameterScheduler,
ParameterSDXLRefinerModel,
ParameterSeed,
ParameterSteps,
ParameterStrength,
ParameterVAEModel,
} from 'features/parameters/types/parameterSchemas';
import { clamp } from 'lodash-es';
export type ParamsState = {
maskBlur: number;
maskBlurMethod: ParameterMaskBlurMethod;
canvasCoherenceMode: ParameterCanvasCoherenceMode;
canvasCoherenceMinDenoise: ParameterStrength;
canvasCoherenceEdgeSize: number;
infillMethod: string;
infillTileSize: number;
infillPatchmatchDownscaleSize: number;
infillColorValue: RgbaColor;
cfgScale: ParameterCFGScale;
cfgRescaleMultiplier: ParameterCFGRescaleMultiplier;
img2imgStrength: ParameterStrength;
iterations: number;
scheduler: ParameterScheduler;
seed: ParameterSeed;
shouldRandomizeSeed: boolean;
steps: ParameterSteps;
model: ParameterModel | null;
vae: ParameterVAEModel | null;
vaePrecision: ParameterPrecision;
seamlessXAxis: boolean;
seamlessYAxis: boolean;
clipSkip: number;
shouldUseCpuNoise: boolean;
positivePrompt: ParameterPositivePrompt;
negativePrompt: ParameterNegativePrompt;
positivePrompt2: ParameterPositiveStylePromptSDXL;
negativePrompt2: ParameterNegativeStylePromptSDXL;
shouldConcatPrompts: boolean;
refinerModel: ParameterSDXLRefinerModel | null;
refinerSteps: number;
refinerCFGScale: number;
refinerScheduler: ParameterScheduler;
refinerPositiveAestheticScore: number;
refinerNegativeAestheticScore: number;
refinerStart: number;
};
const initialState: ParamsState = {
maskBlur: 16,
maskBlurMethod: 'box',
canvasCoherenceMode: 'Gaussian Blur',
canvasCoherenceMinDenoise: 0,
canvasCoherenceEdgeSize: 16,
infillMethod: 'patchmatch',
infillTileSize: 32,
infillPatchmatchDownscaleSize: 1,
infillColorValue: { r: 0, g: 0, b: 0, a: 1 },
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,
positivePrompt: '',
negativePrompt: '',
positivePrompt2: '',
negativePrompt2: '',
shouldConcatPrompts: true,
refinerModel: null,
refinerSteps: 20,
refinerCFGScale: 7.5,
refinerScheduler: 'euler',
refinerPositiveAestheticScore: 6,
refinerNegativeAestheticScore: 2.5,
refinerStart: 0.8,
};
export const paramsSlice = createSlice({
name: 'params',
initialState,
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;
},
modelChanged: (
state,
action: PayloadAction<{ model: ParameterModel | null; previousModel?: ParameterModel | null }>
) => {
const { model, previousModel } = action.payload;
state.model = model;
// If the model base changes (e.g. SD1.5 -> SDXL), we need to change a few things
if (model === null || previousModel?.base === model.base) {
return;
}
// Clamp CLIP skip layer count to the bounds of the new model
if (model.base === 'sdxl') {
// We don't support user-defined CLIP skip for SDXL because it doesn't do anything useful
state.clipSkip = 0;
} else {
const { maxClip } = CLIP_SKIP_MAP[model.base];
state.clipSkip = clamp(state.clipSkip, 0, maxClip);
}
},
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;
},
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;
},
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;
},
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;
},
setInfillColorValue: (state, action: PayloadAction<RgbaColor>) => {
state.infillColorValue = 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;
},
},
});
export const {
setInfillMethod,
setInfillTileSize,
setInfillPatchmatchDownscaleSize,
setInfillColorValue,
setMaskBlur,
setCanvasCoherenceMode,
setCanvasCoherenceEdgeSize,
setCanvasCoherenceMinDenoise,
setIterations,
setSteps,
setCfgScale,
setCfgRescaleMultiplier,
setScheduler,
setSeed,
setImg2imgStrength,
setSeamlessXAxis,
setSeamlessYAxis,
setShouldRandomizeSeed,
vaeSelected,
vaePrecisionChanged,
setClipSkip,
shouldUseCpuNoiseChanged,
positivePromptChanged,
negativePromptChanged,
positivePrompt2Changed,
negativePrompt2Changed,
shouldConcatPromptsChanged,
refinerModelChanged,
setRefinerSteps,
setRefinerCFGScale,
setRefinerScheduler,
setRefinerPositiveAestheticScore,
setRefinerNegativeAestheticScore,
setRefinerStart,
modelChanged,
} = paramsSlice.actions;
export const selectParamsSlice = (state: RootState) => state.params;
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
const migrate = (state: any): any => {
return state;
};
export const paramsPersistConfig: PersistConfig<ParamsState> = {
name: paramsSlice.name,
initialState,
migrate,
persistDenylist: [],
};

View File

@ -1,5 +1,6 @@
import { createSelector } from '@reduxjs/toolkit';
import type { RootState } from 'app/store/store';
import { selectParamsSlice } from 'features/controlLayers/store/paramsSlice';
import type {
CanvasControlLayerState,
CanvasEntityIdentifier,
@ -40,8 +41,8 @@ export const selectEntityCount = createSelector(selectCanvasV2Slice, (canvasV2)
/**
* Selects the optimal dimension for the canvas based on the currently-model
*/
export const selectOptimalDimension = createSelector(selectCanvasV2Slice, (canvasV2) => {
return getOptimalDimension(canvasV2.params.model);
export const selectOptimalDimension = createSelector(selectParamsSlice, (params) => {
return getOptimalDimension(params.model);
});
/**

View File

@ -2,27 +2,7 @@ import type { SerializableObject } from 'common/types';
import { getPrefixedId } from 'features/controlLayers/konva/util';
import { zModelIdentifierField } from 'features/nodes/types/common';
import type { AspectRatioState } from 'features/parameters/components/DocumentSize/types';
import type {
ParameterCanvasCoherenceMode,
ParameterCFGRescaleMultiplier,
ParameterCFGScale,
ParameterHeight,
ParameterLoRAModel,
ParameterMaskBlurMethod,
ParameterModel,
ParameterNegativePrompt,
ParameterNegativeStylePromptSDXL,
ParameterPositivePrompt,
ParameterPositiveStylePromptSDXL,
ParameterPrecision,
ParameterScheduler,
ParameterSDXLRefinerModel,
ParameterSeed,
ParameterSteps,
ParameterStrength,
ParameterVAEModel,
ParameterWidth,
} from 'features/parameters/types/parameterSchemas';
import type { ParameterHeight, ParameterLoRAModel, ParameterWidth } from 'features/parameters/types/parameterSchemas';
import { zParameterNegativePrompt, zParameterPositivePrompt } from 'features/parameters/types/parameterSchemas';
import type { AnyInvocation, BaseModelType, ImageDTO, S } from 'services/api/types';
import { z } from 'zod';
@ -763,46 +743,7 @@ export type CanvasV2State = {
height: ParameterHeight;
};
scaleMethod: BoundingBoxScaleMethod;
};
compositing: {
maskBlur: number;
maskBlurMethod: ParameterMaskBlurMethod;
canvasCoherenceMode: ParameterCanvasCoherenceMode;
canvasCoherenceMinDenoise: ParameterStrength;
canvasCoherenceEdgeSize: number;
infillMethod: string;
infillTileSize: number;
infillPatchmatchDownscaleSize: number;
infillColorValue: RgbaColor;
};
params: {
cfgScale: ParameterCFGScale;
cfgRescaleMultiplier: ParameterCFGRescaleMultiplier;
img2imgStrength: ParameterStrength;
iterations: number;
scheduler: ParameterScheduler;
seed: ParameterSeed;
shouldRandomizeSeed: boolean;
steps: ParameterSteps;
model: ParameterModel | null;
vae: ParameterVAEModel | null;
vaePrecision: ParameterPrecision;
seamlessXAxis: boolean;
seamlessYAxis: boolean;
clipSkip: number;
shouldUseCpuNoise: boolean;
positivePrompt: ParameterPositivePrompt;
negativePrompt: ParameterNegativePrompt;
positivePrompt2: ParameterPositiveStylePromptSDXL;
negativePrompt2: ParameterNegativeStylePromptSDXL;
shouldConcatPrompts: boolean;
refinerModel: ParameterSDXLRefinerModel | null;
refinerSteps: number;
refinerCFGScale: number;
refinerScheduler: ParameterScheduler;
refinerPositiveAestheticScore: number;
refinerNegativeAestheticScore: number;
refinerStart: number;
optimalDimension: number;
};
session: {
mode: SessionMode;

View File

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

View File

@ -1,7 +1,7 @@
import { getStore } from 'app/store/nanostores/store';
import { deepClone } from 'common/util/deepClone';
import { objectKeys } from 'common/util/objectKeys';
import { shouldConcatPromptsChanged } from 'features/controlLayers/store/canvasV2Slice';
import { shouldConcatPromptsChanged } from 'features/controlLayers/store/paramsSlice';
import type { LoRA } from 'features/controlLayers/store/types';
import type {
BuildMetadataHandlers,

View File

@ -4,6 +4,8 @@ import {
bboxWidthChanged,
loraAllDeleted,
loraRecalled,
} from 'features/controlLayers/store/canvasV2Slice';
import {
negativePrompt2Changed,
negativePromptChanged,
positivePrompt2Changed,
@ -22,7 +24,7 @@ import {
setSeed,
setSteps,
vaeSelected,
} from 'features/controlLayers/store/canvasV2Slice';
} from 'features/controlLayers/store/paramsSlice';
import type { LoRA } from 'features/controlLayers/store/types';
import { setHrfEnabled, setHrfMethod, setHrfStrength } from 'features/hrf/store/hrfSlice';
import type { MetadataRecallFunc } from 'features/metadata/types';

View File

@ -20,7 +20,7 @@ const validateBaseCompatibility = (base?: BaseModelType, message?: string) => {
if (!base) {
throw new InvalidModelConfigError(message || 'Missing base');
}
const currentBase = getStore().getState().canvasV2.params.model?.base;
const currentBase = getStore().getState().params.model?.base;
if (currentBase && base !== currentBase) {
throw new InvalidModelConfigError(message || `Incompatible base models: ${base} and ${currentBase}`);
}

View File

@ -12,7 +12,7 @@ export const prepareLinearUIBatch = (
noise: Invocation<'noise'>,
posCond: Invocation<'compel' | 'sdxl_compel_prompt'>
): BatchConfig => {
const { iterations, model, shouldRandomizeSeed, seed, shouldConcatPrompts } = state.canvasV2.params;
const { iterations, model, shouldRandomizeSeed, seed, shouldConcatPrompts } = state.params;
const { prompts, seedBehaviour } = state.dynamicPrompts;
const data: Batch['data'] = [];

View File

@ -13,7 +13,7 @@ import { getBoardField, getPresetModifiedPrompts } from './graphBuilderUtils';
export const buildMultidiffusionUpscaleGraph = async (
state: RootState
): Promise<{ g: Graph; noise: Invocation<'noise'>; posCond: Invocation<'compel' | 'sdxl_compel_prompt'> }> => {
const { model, cfgScale: cfg_scale, scheduler, steps, vaePrecision, seed, vae } = state.canvasV2.params;
const { model, cfgScale: cfg_scale, scheduler, steps, vaePrecision, seed, vae } = state.params;
const { upscaleModel, upscaleInitialImage, structure, creativity, tileControlnetModel, scale } = state.upscale;
assert(model, 'No model found in state');

View File

@ -1,7 +1,7 @@
import type { RootState } from 'app/store/store';
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
import { getPrefixedId } from 'features/controlLayers/konva/util';
import type { CanvasV2State, Dimensions } from 'features/controlLayers/store/types';
import type { Dimensions } from 'features/controlLayers/store/types';
import type { Graph } from 'features/nodes/util/graph/generation/Graph';
import { isEqual } from 'lodash-es';
import type { Invocation } from 'services/api/types';
@ -16,14 +16,15 @@ export const addInpaint = async (
modelLoader: Invocation<'main_model_loader' | 'sdxl_model_loader'>,
originalSize: Dimensions,
scaledSize: Dimensions,
bbox: CanvasV2State['bbox'],
compositing: CanvasV2State['compositing'],
denoising_start: number,
fp32: boolean
): Promise<Invocation<'canvas_v2_mask_and_crop'>> => {
denoise.denoising_start = denoising_start;
const mode = state.canvasV2.session.mode;
const { params, canvasV2 } = state;
const { bbox, session } = canvasV2;
const { mode } = session;
const initialImage = await manager.compositor.getCompositeRasterLayerImageDTO(bbox.rect);
const maskImage = await manager.compositor.getCompositeInpaintMaskImageDTO(bbox.rect);
@ -60,15 +61,15 @@ export const addInpaint = async (
const createGradientMask = g.addNode({
id: getPrefixedId('create_gradient_mask'),
type: 'create_gradient_mask',
coherence_mode: compositing.canvasCoherenceMode,
minimum_denoise: compositing.canvasCoherenceMinDenoise,
edge_radius: compositing.canvasCoherenceEdgeSize,
coherence_mode: params.canvasCoherenceMode,
minimum_denoise: params.canvasCoherenceMinDenoise,
edge_radius: params.canvasCoherenceEdgeSize,
fp32,
});
const canvasPasteBack = g.addNode({
id: getPrefixedId('canvas_v2_mask_and_crop'),
type: 'canvas_v2_mask_and_crop',
mask_blur: compositing.maskBlur,
mask_blur: params.maskBlur,
});
// Resize initial image and mask to scaled size, feed into to gradient mask
@ -114,16 +115,16 @@ export const addInpaint = async (
const createGradientMask = g.addNode({
id: getPrefixedId('create_gradient_mask'),
type: 'create_gradient_mask',
coherence_mode: compositing.canvasCoherenceMode,
minimum_denoise: compositing.canvasCoherenceMinDenoise,
edge_radius: compositing.canvasCoherenceEdgeSize,
coherence_mode: params.canvasCoherenceMode,
minimum_denoise: params.canvasCoherenceMinDenoise,
edge_radius: params.canvasCoherenceEdgeSize,
fp32,
image: { image_name: initialImage.image_name },
});
const canvasPasteBack = g.addNode({
id: getPrefixedId('canvas_v2_mask_and_crop'),
type: 'canvas_v2_mask_and_crop',
mask_blur: compositing.maskBlur,
mask_blur: params.maskBlur,
});
g.addEdge(alphaToMask, 'image', createGradientMask, 'mask');

View File

@ -1,7 +1,7 @@
import type { RootState } from 'app/store/store';
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
import { getPrefixedId } from 'features/controlLayers/konva/util';
import type { CanvasV2State, Dimensions } from 'features/controlLayers/store/types';
import type { Dimensions } from 'features/controlLayers/store/types';
import type { Graph } from 'features/nodes/util/graph/generation/Graph';
import { getInfill } from 'features/nodes/util/graph/graphBuilderUtils';
import { isEqual } from 'lodash-es';
@ -17,17 +17,18 @@ export const addOutpaint = async (
modelLoader: Invocation<'main_model_loader' | 'sdxl_model_loader'>,
originalSize: Dimensions,
scaledSize: Dimensions,
bbox: CanvasV2State['bbox'],
compositing: CanvasV2State['compositing'],
denoising_start: number,
fp32: boolean
): Promise<Invocation<'canvas_v2_mask_and_crop'>> => {
denoise.denoising_start = denoising_start;
const mode = state.canvasV2.session.mode;
const { params, canvasV2 } = state;
const { bbox, session } = canvasV2;
const { mode } = session;
const initialImage = await manager.compositor.getCompositeRasterLayerImageDTO(bbox.rect);
const maskImage = await manager.compositor.getCompositeInpaintMaskImageDTO(bbox.rect);
const infill = getInfill(g, compositing);
const infill = getInfill(g, params);
if (!isEqual(scaledSize, originalSize)) {
// Scale before processing requires some resizing
@ -72,9 +73,9 @@ export const addOutpaint = async (
const createGradientMask = g.addNode({
id: getPrefixedId('create_gradient_mask'),
type: 'create_gradient_mask',
coherence_mode: compositing.canvasCoherenceMode,
minimum_denoise: compositing.canvasCoherenceMinDenoise,
edge_radius: compositing.canvasCoherenceEdgeSize,
coherence_mode: params.canvasCoherenceMode,
minimum_denoise: params.canvasCoherenceMinDenoise,
edge_radius: params.canvasCoherenceEdgeSize,
fp32,
});
g.addEdge(infill, 'image', createGradientMask, 'image');
@ -103,7 +104,7 @@ export const addOutpaint = async (
const canvasPasteBack = g.addNode({
id: getPrefixedId('canvas_v2_mask_and_crop'),
type: 'canvas_v2_mask_and_crop',
mask_blur: compositing.maskBlur,
mask_blur: params.maskBlur,
});
// Resize initial image and mask to scaled size, feed into to gradient mask
@ -143,16 +144,16 @@ export const addOutpaint = async (
const createGradientMask = g.addNode({
id: getPrefixedId('create_gradient_mask'),
type: 'create_gradient_mask',
coherence_mode: compositing.canvasCoherenceMode,
minimum_denoise: compositing.canvasCoherenceMinDenoise,
edge_radius: compositing.canvasCoherenceEdgeSize,
coherence_mode: params.canvasCoherenceMode,
minimum_denoise: params.canvasCoherenceMinDenoise,
edge_radius: params.canvasCoherenceEdgeSize,
fp32,
image: { image_name: initialImage.image_name },
});
const canvasPasteBack = g.addNode({
id: getPrefixedId('canvas_v2_mask_and_crop'),
type: 'canvas_v2_mask_and_crop',
mask_blur: compositing.maskBlur,
mask_blur: params.maskBlur,
});
g.addEdge(maskAlphaToMask, 'image', maskCombine, 'mask1');
g.addEdge(initialImageAlphaToMask, 'image', maskCombine, 'mask2');

View File

@ -23,7 +23,7 @@ export const addSDXLRefiner = async (
refinerScheduler,
refinerCFGScale,
refinerStart,
} = state.canvasV2.params;
} = state.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.canvasV2.params;
const { seamlessXAxis: seamless_x, seamlessYAxis: seamless_y } = state.params;
if (!seamless_x && !seamless_y) {
return null;

View File

@ -31,7 +31,8 @@ export const buildSD1Graph = async (
const generationMode = manager.compositor.getGenerationMode();
log.debug({ generationMode }, 'Building SD1/SD2 graph');
const { bbox, params, session, settings } = state.canvasV2;
const { canvasV2, params } = state;
const { bbox, session, settings } = canvasV2;
const {
model,
@ -172,7 +173,6 @@ export const buildSD1Graph = async (
vaePrecision === 'fp32'
);
} else if (generationMode === 'inpaint') {
const { compositing } = state.canvasV2;
canvasOutput = await addInpaint(
state,
g,
@ -183,13 +183,10 @@ export const buildSD1Graph = async (
modelLoader,
originalSize,
scaledSize,
bbox,
compositing,
1 - params.img2imgStrength,
vaePrecision === 'fp32'
);
} else if (generationMode === 'outpaint') {
const { compositing } = state.canvasV2;
canvasOutput = await addOutpaint(
state,
g,
@ -200,8 +197,6 @@ export const buildSD1Graph = async (
modelLoader,
originalSize,
scaledSize,
bbox,
compositing,
1 - params.img2imgStrength,
fp32
);

View File

@ -31,7 +31,8 @@ export const buildSDXLGraph = async (
const generationMode = manager.compositor.getGenerationMode();
log.debug({ generationMode }, 'Building SDXL graph');
const { bbox, params, session, settings } = state.canvasV2;
const { params, canvasV2 } = state;
const { bbox, session, settings } = canvasV2;
const {
model,
@ -175,7 +176,6 @@ export const buildSDXLGraph = async (
fp32
);
} else if (generationMode === 'inpaint') {
const { compositing } = state.canvasV2;
canvasOutput = await addInpaint(
state,
g,
@ -186,13 +186,10 @@ export const buildSDXLGraph = async (
modelLoader,
originalSize,
scaledSize,
bbox,
compositing,
refinerModel ? Math.min(refinerStart, 1 - params.img2imgStrength) : 1 - params.img2imgStrength,
fp32
);
} else if (generationMode === 'outpaint') {
const { compositing } = state.canvasV2;
canvasOutput = await addOutpaint(
state,
g,
@ -203,8 +200,6 @@ export const buildSDXLGraph = async (
modelLoader,
originalSize,
scaledSize,
bbox,
compositing,
refinerModel ? Math.min(refinerStart, 1 - params.img2imgStrength) : 1 - params.img2imgStrength,
fp32
);

View File

@ -1,4 +1,5 @@
import type { RootState } from 'app/store/store';
import type { ParamsState } from 'features/controlLayers/store/paramsSlice';
import type { CanvasV2State } from 'features/controlLayers/store/types';
import type { BoardField } from 'features/nodes/types/common';
import type { Graph } from 'features/nodes/util/graph/generation/Graph';
@ -25,8 +26,7 @@ export const getBoardField = (state: RootState): BoardField | undefined => {
export const getPresetModifiedPrompts = (
state: RootState
): { positivePrompt: string; negativePrompt: string; positiveStylePrompt?: string; negativeStylePrompt?: string } => {
const { positivePrompt, negativePrompt, positivePrompt2, negativePrompt2, shouldConcatPrompts } =
state.canvasV2.params;
const { positivePrompt, negativePrompt, positivePrompt2, negativePrompt2, shouldConcatPrompts } = state.params;
const { activeStylePresetId } = state.stylePreset;
if (activeStylePresetId) {
@ -70,9 +70,9 @@ export const getSizes = (bboxState: CanvasV2State['bbox']) => {
export const getInfill = (
g: Graph,
compositing: CanvasV2State['compositing']
params: ParamsState
): Invocation<'infill_patchmatch' | 'infill_cv2' | 'infill_lama' | 'infill_rgba' | 'infill_tile'> => {
const { infillMethod, infillColorValue, infillPatchmatchDownscaleSize, infillTileSize } = compositing;
const { infillMethod, infillColorValue, infillPatchmatchDownscaleSize, infillTileSize } = params;
// Add Infill Nodes
if (infillMethod === 'patchmatch') {

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/controlLayers/store/canvasV2Slice';
import { setCfgRescaleMultiplier } from 'features/controlLayers/store/paramsSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
const ParamCFGRescaleMultiplier = () => {
const cfgRescaleMultiplier = useAppSelector((s) => s.canvasV2.params.cfgRescaleMultiplier);
const cfgRescaleMultiplier = useAppSelector((s) => s.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/controlLayers/store/canvasV2Slice';
import { setClipSkip } from 'features/controlLayers/store/paramsSlice';
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.canvasV2.params.clipSkip);
const clipSkip = useAppSelector((s) => s.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.canvasV2.params.model);
const model = useAppSelector((s) => s.params.model);
const dispatch = useAppDispatch();
const { t } = useTranslation();

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/controlLayers/store/canvasV2Slice';
import { setCanvasCoherenceEdgeSize } from 'features/controlLayers/store/paramsSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
const ParamCanvasCoherenceEdgeSize = () => {
const dispatch = useAppDispatch();
const canvasCoherenceEdgeSize = useAppSelector((s) => s.canvasV2.compositing.canvasCoherenceEdgeSize);
const canvasCoherenceEdgeSize = useAppSelector((s) => s.params.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/controlLayers/store/canvasV2Slice';
import { setCanvasCoherenceMinDenoise } from 'features/controlLayers/store/paramsSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
const ParamCanvasCoherenceMinDenoise = () => {
const dispatch = useAppDispatch();
const canvasCoherenceMinDenoise = useAppSelector((s) => s.canvasV2.compositing.canvasCoherenceMinDenoise);
const canvasCoherenceMinDenoise = useAppSelector((s) => s.params.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/controlLayers/store/canvasV2Slice';
import { setCanvasCoherenceMode } from 'features/controlLayers/store/paramsSlice';
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.canvasV2.compositing.canvasCoherenceMode);
const canvasCoherenceMode = useAppSelector((s) => s.params.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/controlLayers/store/canvasV2Slice';
import { setMaskBlur } from 'features/controlLayers/store/paramsSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
const ParamMaskBlur = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const maskBlur = useAppSelector((s) => s.canvasV2.compositing.maskBlur);
const maskBlur = useAppSelector((s) => s.params.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,7 +1,7 @@
import { Box, Flex, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIColorPicker from 'common/components/IAIColorPicker';
import { setInfillColorValue } from 'features/controlLayers/store/canvasV2Slice';
import { setInfillColorValue } from 'features/controlLayers/store/paramsSlice';
import { memo, useCallback } from 'react';
import type { RgbaColor } from 'react-colorful';
import { useTranslation } from 'react-i18next';
@ -9,8 +9,8 @@ import { useTranslation } from 'react-i18next';
const ParamInfillColorOptions = () => {
const dispatch = useAppDispatch();
const infillColor = useAppSelector((s) => s.canvasV2.compositing.infillColorValue);
const infillMethod = useAppSelector((s) => s.canvasV2.compositing.infillMethod);
const infillColor = useAppSelector((s) => s.params.infillColorValue);
const infillMethod = useAppSelector((s) => s.params.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/controlLayers/store/canvasV2Slice';
import { setInfillMethod } from 'features/controlLayers/store/paramsSlice';
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.canvasV2.compositing.infillMethod);
const infillMethod = useAppSelector((s) => s.params.infillMethod);
const { data: appConfigData } = useGetAppConfigQuery();
const options = useMemo<ComboboxOption[]>(
() =>

View File

@ -6,7 +6,7 @@ import ParamInfillPatchmatchDownscaleSize from './ParamInfillPatchmatchDownscale
import ParamInfillTilesize from './ParamInfillTilesize';
const ParamInfillOptions = () => {
const infillMethod = useAppSelector((s) => s.canvasV2.compositing.infillMethod);
const infillMethod = useAppSelector((s) => s.params.infillMethod);
if (infillMethod === 'tile') {
return <ParamInfillTilesize />;
}

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/controlLayers/store/canvasV2Slice';
import { setInfillPatchmatchDownscaleSize } from 'features/controlLayers/store/paramsSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
const ParamInfillPatchmatchDownscaleSize = () => {
const dispatch = useAppDispatch();
const infillMethod = useAppSelector((s) => s.canvasV2.compositing.infillMethod);
const infillPatchmatchDownscaleSize = useAppSelector((s) => s.canvasV2.compositing.infillPatchmatchDownscaleSize);
const infillMethod = useAppSelector((s) => s.params.infillMethod);
const infillPatchmatchDownscaleSize = useAppSelector((s) => s.params.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/controlLayers/store/canvasV2Slice';
import { setInfillTileSize } from 'features/controlLayers/store/paramsSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
const ParamInfillTileSize = () => {
const dispatch = useAppDispatch();
const infillTileSize = useAppSelector((s) => s.canvasV2.compositing.infillTileSize);
const infillTileSize = useAppSelector((s) => s.params.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.canvasV2.compositing.infillMethod);
const infillMethod = useAppSelector((s) => s.params.infillMethod);
const { t } = useTranslation();

View File

@ -1,10 +1,10 @@
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { setImg2imgStrength } from 'features/controlLayers/store/canvasV2Slice';
import { setImg2imgStrength } from 'features/controlLayers/store/paramsSlice';
import ImageToImageStrength from 'features/parameters/components/ImageToImage/ImageToImageStrength';
import { memo, useCallback } from 'react';
const ParamImageToImageStrength = () => {
const img2imgStrength = useAppSelector((s) => s.canvasV2.params.img2imgStrength);
const img2imgStrength = useAppSelector((s) => s.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/controlLayers/store/canvasV2Slice';
import { setCfgScale } from 'features/controlLayers/store/paramsSlice';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
const ParamCFGScale = () => {
const cfgScale = useAppSelector((s) => s.canvasV2.params.cfgScale);
const cfgScale = useAppSelector((s) => s.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

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

View File

@ -1,6 +1,6 @@
import { Box, Textarea } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { positivePromptChanged } from 'features/controlLayers/store/canvasV2Slice';
import { positivePromptChanged } from 'features/controlLayers/store/paramsSlice';
import { ShowDynamicPromptsPreviewButton } from 'features/dynamicPrompts/components/ShowDynamicPromptsPreviewButton';
import { PromptLabel } from 'features/parameters/components/Prompts/PromptLabel';
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
@ -17,8 +17,8 @@ import { useListStylePresetsQuery } from 'services/api/endpoints/stylePresets';
export const ParamPositivePrompt = memo(() => {
const dispatch = useAppDispatch();
const prompt = useAppSelector((s) => s.canvasV2.params.positivePrompt);
const baseModel = useAppSelector((s) => s.canvasV2.params.model)?.base;
const prompt = useAppSelector((s) => s.params.positivePrompt);
const baseModel = useAppSelector((s) => s.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/controlLayers/store/canvasV2Slice';
import { setScheduler } from 'features/controlLayers/store/paramsSlice';
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.canvasV2.params.scheduler);
const scheduler = useAppSelector((s) => s.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/controlLayers/store/canvasV2Slice';
import { setSteps } from 'features/controlLayers/store/paramsSlice';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
const ParamSteps = () => {
const steps = useAppSelector((s) => s.canvasV2.params.steps);
const steps = useAppSelector((s) => s.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

@ -12,7 +12,7 @@ import type { MainModelConfig } from 'services/api/types';
const ParamMainModelSelect = () => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const selectedModel = useAppSelector((s) => s.canvasV2.params.model);
const selectedModel = useAppSelector((s) => s.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.canvasV2.params.model);
const model = useAppSelector((s) => s.params.model);
const { t } = useTranslation();
const dispatch = useAppDispatch();

View File

@ -1,25 +1,23 @@
import { Flex } from '@invoke-ai/ui-library';
import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks';
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
import { ParamNegativePrompt } from 'features/parameters/components/Core/ParamNegativePrompt';
import { ParamPositivePrompt } from 'features/parameters/components/Core/ParamPositivePrompt';
import { ParamSDXLNegativeStylePrompt } from 'features/sdxl/components/SDXLPrompts/ParamSDXLNegativeStylePrompt';
import { ParamSDXLPositiveStylePrompt } from 'features/sdxl/components/SDXLPrompts/ParamSDXLPositiveStylePrompt';
import { memo } from 'react';
const concatPromptsSelector = createSelector([selectCanvasV2Slice], (canvasV2) => {
return canvasV2.params.model?.base !== 'sdxl' || canvasV2.params.shouldConcatPrompts;
});
export const Prompts = memo(() => {
const shouldConcatPrompts = useAppSelector(concatPromptsSelector);
const withStylePrompts = useAppSelector((s) => {
const isSDXL = s.params.model?.base === 'sdxl';
const shouldConcatPrompts = s.params.shouldConcatPrompts;
return isSDXL && !shouldConcatPrompts;
});
return (
<Flex flexDir="column" gap={2}>
<ParamPositivePrompt />
{!shouldConcatPrompts && <ParamSDXLPositiveStylePrompt />}
{withStylePrompts && <ParamSDXLPositiveStylePrompt />}
<ParamNegativePrompt />
{!shouldConcatPrompts && <ParamSDXLNegativeStylePrompt />}
{withStylePrompts && <ParamSDXLNegativeStylePrompt />}
</Flex>
);
});

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/controlLayers/store/canvasV2Slice';
import { setSeamlessXAxis } from 'features/controlLayers/store/paramsSlice';
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.canvasV2.params.seamlessXAxis);
const seamlessXAxis = useAppSelector((s) => s.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/controlLayers/store/canvasV2Slice';
import { setSeamlessYAxis } from 'features/controlLayers/store/paramsSlice';
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.canvasV2.params.seamlessYAxis);
const seamlessYAxis = useAppSelector((s) => s.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/controlLayers/store/canvasV2Slice';
import { setSeed } from 'features/controlLayers/store/paramsSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
export const ParamSeedNumberInput = memo(() => {
const seed = useAppSelector((s) => s.canvasV2.params.seed);
const shouldRandomizeSeed = useAppSelector((s) => s.canvasV2.params.shouldRandomizeSeed);
const seed = useAppSelector((s) => s.params.seed);
const shouldRandomizeSeed = useAppSelector((s) => s.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/controlLayers/store/canvasV2Slice';
import { setShouldRandomizeSeed } from 'features/controlLayers/store/paramsSlice';
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.canvasV2.params.shouldRandomizeSeed);
const shouldRandomizeSeed = useAppSelector((s) => s.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/controlLayers/store/canvasV2Slice';
import { setSeed } from 'features/controlLayers/store/paramsSlice';
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.canvasV2.params.shouldRandomizeSeed);
const shouldRandomizeSeed = useAppSelector((s) => s.params.shouldRandomizeSeed);
const { t } = useTranslation();
const handleClickRandomizeSeed = useCallback(

View File

@ -2,7 +2,7 @@ import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library';
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 { vaeSelected } from 'features/controlLayers/store/paramsSlice';
import { zModelIdentifierField } from 'features/nodes/types/common';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
@ -12,8 +12,8 @@ import type { VAEModelConfig } from 'services/api/types';
const ParamVAEModelSelect = () => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const model = useAppSelector((s) => s.canvasV2.params.model);
const vae = useAppSelector((s) => s.canvasV2.params.vae);
const model = useAppSelector((s) => s.params.model);
const vae = useAppSelector((s) => s.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/controlLayers/store/canvasV2Slice';
import { vaePrecisionChanged } from 'features/controlLayers/store/paramsSlice';
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.canvasV2.params.vaePrecision);
const vaePrecision = useAppSelector((s) => s.params.vaePrecision);
const onChange = useCallback<ComboboxOnChange>(
(v) => {

View File

@ -17,7 +17,7 @@ const noOptionsMessage = () => t('prompt.noMatchingTriggers');
export const PromptTriggerSelect = memo(({ onSelect, onClose }: PromptTriggerSelectProps) => {
const { t } = useTranslation();
const mainModel = useAppSelector((s) => s.canvasV2.params.model);
const mainModel = useAppSelector((s) => s.params.model);
const addedLoRAs = useAppSelector((s) => s.canvasV2.loras);
const { data: mainModelConfig, isLoading: isLoadingMainModelConfig } = useGetModelConfigQuery(
mainModel?.key ?? skipToken

View File

@ -2,7 +2,7 @@ import { Divider, Flex, ListItem, Text, Tooltip, UnorderedList } from '@invoke-a
import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks';
import { useIsReadyToEnqueue } from 'common/hooks/useIsReadyToEnqueue';
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
import { selectParamsSlice } from 'features/controlLayers/store/paramsSlice';
import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
import { getShouldProcessPrompt } from 'features/dynamicPrompts/util/getShouldProcessPrompt';
import type { PropsWithChildren } from 'react';
@ -11,8 +11,8 @@ import { useTranslation } from 'react-i18next';
import { useEnqueueBatchMutation } from 'services/api/endpoints/queue';
import { useBoardName } from 'services/api/hooks/useBoardName';
const selectPromptsCount = createSelector(selectCanvasV2Slice, selectDynamicPromptsSlice, (canvasV2, dynamicPrompts) =>
getShouldProcessPrompt(canvasV2.params.positivePrompt) ? dynamicPrompts.prompts.length : 1
const selectPromptsCount = createSelector(selectParamsSlice, selectDynamicPromptsSlice, (params, dynamicPrompts) =>
getShouldProcessPrompt(params.positivePrompt) ? dynamicPrompts.prompts.length : 1
);
type Props = {
@ -32,7 +32,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.canvasV2.params.iterations);
const iterationsCount = useAppSelector((s) => s.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/controlLayers/store/canvasV2Slice';
import { setIterations } from 'features/controlLayers/store/paramsSlice';
import { memo, useCallback } from 'react';
export const QueueIterationsNumberInput = memo(() => {
const iterations = useAppSelector((s) => s.canvasV2.params.iterations);
const iterations = useAppSelector((s) => s.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,6 +1,6 @@
import { Box, Textarea } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { negativePrompt2Changed } from 'features/controlLayers/store/canvasV2Slice';
import { negativePrompt2Changed } from 'features/controlLayers/store/paramsSlice';
import { PromptLabel } from 'features/parameters/components/Prompts/PromptLabel';
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
@ -12,7 +12,7 @@ import { useTranslation } from 'react-i18next';
export const ParamSDXLNegativeStylePrompt = memo(() => {
const dispatch = useAppDispatch();
const prompt = useAppSelector((s) => s.canvasV2.params.negativePrompt2);
const prompt = useAppSelector((s) => s.params.negativePrompt2);
const textareaRef = useRef<HTMLTextAreaElement>(null);
const { t } = useTranslation();
const handleChange = useCallback(

View File

@ -1,6 +1,6 @@
import { Box, Textarea } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { positivePrompt2Changed } from 'features/controlLayers/store/canvasV2Slice';
import { positivePrompt2Changed } from 'features/controlLayers/store/paramsSlice';
import { PromptLabel } from 'features/parameters/components/Prompts/PromptLabel';
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
@ -11,7 +11,7 @@ import { useTranslation } from 'react-i18next';
export const ParamSDXLPositiveStylePrompt = memo(() => {
const dispatch = useAppDispatch();
const prompt = useAppSelector((s) => s.canvasV2.params.positivePrompt2);
const prompt = useAppSelector((s) => s.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/controlLayers/store/canvasV2Slice';
import { shouldConcatPromptsChanged } from 'features/controlLayers/store/paramsSlice';
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.canvasV2.params.shouldConcatPrompts);
const shouldConcatPrompts = useAppSelector((s) => s.params.shouldConcatPrompts);
const dispatch = useAppDispatch();
const { t } = useTranslation();

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 { setRefinerCFGScale } from 'features/controlLayers/store/canvasV2Slice';
import { setRefinerCFGScale } from 'features/controlLayers/store/paramsSlice';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
const ParamSDXLRefinerCFGScale = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const refinerCFGScale = useAppSelector((s) => s.canvasV2.params.refinerCFGScale);
const refinerCFGScale = useAppSelector((s) => s.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

@ -2,7 +2,7 @@ import { Box, Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { useModelCombobox } from 'common/hooks/useModelCombobox';
import { refinerModelChanged } from 'features/controlLayers/store/canvasV2Slice';
import { refinerModelChanged } from 'features/controlLayers/store/paramsSlice';
import { zModelIdentifierField } from 'features/nodes/types/common';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
@ -13,7 +13,7 @@ const optionsFilter = (model: MainModelConfig) => model.base === 'sdxl-refiner';
const ParamSDXLRefinerModelSelect = () => {
const dispatch = useAppDispatch();
const model = useAppSelector((s) => s.canvasV2.params.refinerModel);
const model = useAppSelector((s) => s.params.refinerModel);
const { t } = useTranslation();
const [modelConfigs, { isLoading }] = useRefinerModels();
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 { setRefinerNegativeAestheticScore } from 'features/controlLayers/store/canvasV2Slice';
import { setRefinerNegativeAestheticScore } from 'features/controlLayers/store/paramsSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
const ParamSDXLRefinerNegativeAestheticScore = () => {
const refinerNegativeAestheticScore = useAppSelector((s) => s.canvasV2.params.refinerNegativeAestheticScore);
const refinerNegativeAestheticScore = useAppSelector((s) => s.params.refinerNegativeAestheticScore);
const dispatch = useAppDispatch();
const { t } = useTranslation();

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 { setRefinerPositiveAestheticScore } from 'features/controlLayers/store/canvasV2Slice';
import { setRefinerPositiveAestheticScore } from 'features/controlLayers/store/paramsSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
const ParamSDXLRefinerPositiveAestheticScore = () => {
const refinerPositiveAestheticScore = useAppSelector((s) => s.canvasV2.params.refinerPositiveAestheticScore);
const refinerPositiveAestheticScore = useAppSelector((s) => s.params.refinerPositiveAestheticScore);
const dispatch = useAppDispatch();
const { t } = useTranslation();

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 { setRefinerScheduler } from 'features/controlLayers/store/canvasV2Slice';
import { setRefinerScheduler } from 'features/controlLayers/store/paramsSlice';
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 ParamSDXLRefinerScheduler = () => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const refinerScheduler = useAppSelector((s) => s.canvasV2.params.refinerScheduler);
const refinerScheduler = useAppSelector((s) => s.params.refinerScheduler);
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 { setRefinerStart } from 'features/controlLayers/store/canvasV2Slice';
import { setRefinerStart } from 'features/controlLayers/store/paramsSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
const ParamSDXLRefinerStart = () => {
const refinerStart = useAppSelector((s) => s.canvasV2.params.refinerStart);
const refinerStart = useAppSelector((s) => s.params.refinerStart);
const dispatch = useAppDispatch();
const handleChange = useCallback((v: number) => dispatch(setRefinerStart(v)), [dispatch]);
const { t } = useTranslation();

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 { setRefinerSteps } from 'features/controlLayers/store/canvasV2Slice';
import { setRefinerSteps } from 'features/controlLayers/store/paramsSlice';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
const ParamSDXLRefinerSteps = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const refinerSteps = useAppSelector((s) => s.canvasV2.params.refinerSteps);
const refinerSteps = useAppSelector((s) => s.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

@ -3,7 +3,7 @@ import { Flex, FormControlGroup, StandaloneAccordion } from '@invoke-ai/ui-libra
import { skipToken } from '@reduxjs/toolkit/query';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppSelector } from 'app/store/storeHooks';
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
import { selectParamsSlice } from 'features/controlLayers/store/paramsSlice';
import ParamCFGRescaleMultiplier from 'features/parameters/components/Advanced/ParamCFGRescaleMultiplier';
import ParamClipSkip from 'features/parameters/components/Advanced/ParamClipSkip';
import ParamSeamlessXAxis from 'features/parameters/components/Seamless/ParamSeamlessXAxis';
@ -28,13 +28,13 @@ const formLabelProps2: FormLabelProps = {
};
export const AdvancedSettingsAccordion = memo(() => {
const vaeKey = useAppSelector((state) => state.canvasV2.params.vae?.key);
const vaeKey = useAppSelector((state) => state.params.vae?.key);
const { currentData: vaeConfig } = useGetModelConfigQuery(vaeKey ?? skipToken);
const activeTabName = useAppSelector(selectActiveTab);
const selectBadges = useMemo(
() =>
createMemoizedSelector(selectCanvasV2Slice, ({ params }) => {
createMemoizedSelector(selectParamsSlice, (params) => {
const badges: (string | number)[] = [];
if (vaeConfig) {
let vaeBadge = vaeConfig.name;

View File

@ -2,6 +2,7 @@ import type { FormLabelProps } from '@invoke-ai/ui-library';
import { Expander, Flex, FormControlGroup, StandaloneAccordion } from '@invoke-ai/ui-library';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppSelector } from 'app/store/storeHooks';
import { selectParamsSlice } from 'features/controlLayers/store/paramsSlice';
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
import { HrfSettings } from 'features/hrf/components/HrfSettings';
import { selectHrfSlice } from 'features/hrf/store/hrfSlice';
@ -18,31 +19,34 @@ import { useStandaloneAccordionToggle } from 'features/settingsAccordions/hooks/
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
const selector = createMemoizedSelector([selectHrfSlice, selectCanvasV2Slice], (hrf, canvasV2) => {
const { shouldRandomizeSeed, model } = canvasV2.params;
const { hrfEnabled } = hrf;
const badges: string[] = [];
const isSDXL = model?.base === 'sdxl';
const selector = createMemoizedSelector(
[selectHrfSlice, selectCanvasV2Slice, selectParamsSlice],
(hrf, canvasV2, params) => {
const { shouldRandomizeSeed, model } = params;
const { hrfEnabled } = hrf;
const badges: string[] = [];
const isSDXL = model?.base === 'sdxl';
const { aspectRatio } = canvasV2.bbox;
const { width, height } = canvasV2.bbox.rect;
const { aspectRatio } = canvasV2.bbox;
const { width, height } = canvasV2.bbox.rect;
badges.push(`${width}×${height}`);
badges.push(aspectRatio.id);
badges.push(`${width}×${height}`);
badges.push(aspectRatio.id);
if (aspectRatio.isLocked) {
badges.push('locked');
if (aspectRatio.isLocked) {
badges.push('locked');
}
if (!shouldRandomizeSeed) {
badges.push('Manual Seed');
}
if (hrfEnabled && !isSDXL) {
badges.push('HiRes Fix');
}
return { badges, isSDXL };
}
if (!shouldRandomizeSeed) {
badges.push('Manual Seed');
}
if (hrfEnabled && !isSDXL) {
badges.push('HiRes Fix');
}
return { badges, isSDXL };
});
);
const scalingLabelProps: FormLabelProps = {
minW: '4.5rem',

View File

@ -2,7 +2,7 @@ import type { FormLabelProps } from '@invoke-ai/ui-library';
import { Flex, FormControlGroup, StandaloneAccordion, Text } from '@invoke-ai/ui-library';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppSelector } from 'app/store/storeHooks';
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
import { selectParamsSlice } from 'features/controlLayers/store/paramsSlice';
import ParamSDXLRefinerCFGScale from 'features/sdxl/components/SDXLRefiner/ParamSDXLRefinerCFGScale';
import ParamSDXLRefinerModelSelect from 'features/sdxl/components/SDXLRefiner/ParamSDXLRefinerModelSelect';
import ParamSDXLRefinerNegativeAestheticScore from 'features/sdxl/components/SDXLRefiner/ParamSDXLRefinerNegativeAestheticScore';
@ -24,7 +24,7 @@ const stepsScaleLabelProps: FormLabelProps = {
minW: '5rem',
};
const selectBadges = createMemoizedSelector(selectCanvasV2Slice, ({ params }) =>
const selectBadges = createMemoizedSelector(selectParamsSlice, (params) =>
params.refinerModel ? ['Enabled'] : undefined
);
@ -60,7 +60,7 @@ const RefinerSettingsAccordionNoRefiner: React.FC = memo(() => {
RefinerSettingsAccordionNoRefiner.displayName = 'RefinerSettingsAccordionNoRefiner';
const RefinerSettingsAccordionContent: React.FC = memo(() => {
const isRefinerModelSelected = useAppSelector((state) => !isNil(state.canvasV2.params.refinerModel));
const isRefinerModelSelected = useAppSelector((state) => !isNil(state.params.refinerModel));
return (
<FormControlGroup isDisabled={!isRefinerModelSelected}>

View File

@ -10,7 +10,7 @@ import { useControlNetModels } from 'services/api/hooks/modelsByType';
export const UpscaleWarning = () => {
const { t } = useTranslation();
const model = useAppSelector((s) => s.canvasV2.params.model);
const model = useAppSelector((s) => s.params.model);
const upscaleModel = useAppSelector((s) => s.upscale.upscaleModel);
const tileControlnetModel = useAppSelector((s) => s.upscale.tileControlnetModel);
const upscaleInitialImage = useAppSelector((s) => s.upscale.upscaleInitialImage);

View File

@ -1,6 +1,6 @@
import { Badge, Flex, IconButton, Spacer, Text, Tooltip } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { negativePromptChanged, positivePromptChanged } from 'features/controlLayers/store/canvasV2Slice';
import { negativePromptChanged, positivePromptChanged } from 'features/controlLayers/store/paramsSlice';
import { usePresetModifiedPrompts } from 'features/stylePresets/hooks/usePresetModifiedPrompts';
import { activeStylePresetIdChanged, viewModeChanged } from 'features/stylePresets/store/stylePresetSlice';
import type { MouseEventHandler } from 'react';

View File

@ -10,8 +10,8 @@ export const buildPresetModifiedPrompt = (presetPrompt: string, currentPrompt: s
};
export const usePresetModifiedPrompts = () => {
const positivePrompt = useAppSelector((s) => s.canvasV2.params.positivePrompt);
const negativePrompt = useAppSelector((s) => s.canvasV2.params.negativePrompt);
const positivePrompt = useAppSelector((s) => s.params.positivePrompt);
const negativePrompt = useAppSelector((s) => s.params.negativePrompt);
const activeStylePresetId = useAppSelector((s) => s.stylePreset.activeStylePresetId);

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/controlLayers/store/canvasV2Slice';
import { shouldUseCpuNoiseChanged } from 'features/controlLayers/store/paramsSlice';
import { SettingsDeveloperLogIsEnabled } from 'features/system/components/SettingsModal/SettingsDeveloperLogIsEnabled';
import { SettingsDeveloperLogLevel } from 'features/system/components/SettingsModal/SettingsDeveloperLogLevel';
import { SettingsDeveloperLogNamespaces } from 'features/system/components/SettingsModal/SettingsDeveloperLogNamespaces';
@ -89,7 +89,7 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => {
const { isOpen: isRefreshModalOpen, onOpen: onRefreshModalOpen, onClose: onRefreshModalClose } = useDisclosure();
const shouldUseCpuNoise = useAppSelector((s) => s.canvasV2.params.shouldUseCpuNoise);
const shouldUseCpuNoise = useAppSelector((s) => s.params.shouldUseCpuNoise);
const shouldConfirmOnDelete = useAppSelector((s) => s.system.shouldConfirmOnDelete);
const shouldShowProgressInViewer = useAppSelector((s) => s.ui.shouldShowProgressInViewer);
const shouldAntialiasProgressImage = useAppSelector((s) => s.system.shouldAntialiasProgressImage);

View File

@ -49,7 +49,7 @@ const ParametersPanelTextToImage = () => {
}
return `${t('controlLayers.controlLayers')} (${controlLayersCount})`;
}, [controlLayersCount, t]);
const isSDXL = useAppSelector((s) => s.canvasV2.params.model?.base === 'sdxl');
const isSDXL = useAppSelector((s) => s.params.model?.base === 'sdxl');
const onChangeTabs = useCallback(
(i: number) => {
if (i === 1) {

View File

@ -3,7 +3,7 @@ import { useAppSelector } from 'app/store/storeHooks';
import { useGetModelConfigQuery } from 'services/api/endpoints/models';
export const useSelectedModelConfig = () => {
const key = useAppSelector((s) => s.canvasV2.params.model?.key);
const key = useAppSelector((s) => s.params.model?.key);
const { currentData: modelConfig } = useGetModelConfigQuery(key ?? skipToken);
return modelConfig;