mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-02-13 06:14:58 -05:00
refactor(ui): use zod for all redux state (wip)
needed for confidence w/ state rehydration logic
This commit is contained in:
@@ -1,10 +1,19 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import type { Slice } from '@reduxjs/toolkit';
|
||||
import type { UndoableOptions } from 'redux-undo';
|
||||
import type { ZodType } from 'zod';
|
||||
|
||||
type StateFromSlice<T extends Slice> = T extends Slice<infer U> ? U : never;
|
||||
|
||||
export type SliceConfig<T extends Slice = Slice> = {
|
||||
export type SliceConfig<T extends Slice> = {
|
||||
/**
|
||||
* The redux slice (return of createSlice).
|
||||
*/
|
||||
slice: T;
|
||||
/**
|
||||
* The zod schema for the slice.
|
||||
*/
|
||||
zSchema: ZodType<StateFromSlice<T>>;
|
||||
/**
|
||||
* A function that returns the initial state of the slice.
|
||||
*/
|
||||
@@ -18,7 +27,7 @@ export type SliceConfig<T extends Slice = Slice> = {
|
||||
* @param state The rehydrated state.
|
||||
* @returns A correctly-shaped state.
|
||||
*/
|
||||
migrate: (state: unknown) => StateFromSlice<T>;
|
||||
migrate: (state: any) => StateFromSlice<T>;
|
||||
/**
|
||||
* Keys to omit from the persisted state.
|
||||
*/
|
||||
|
||||
@@ -1,130 +1,300 @@
|
||||
import type { FilterType } from 'features/controlLayers/store/filters';
|
||||
import type { ParameterPrecision, ParameterScheduler } from 'features/parameters/types/parameterSchemas';
|
||||
import type { TabName } from 'features/ui/store/uiTypes';
|
||||
import { zFilterType } from 'features/controlLayers/store/filters';
|
||||
import { zParameterPrecision, zParameterScheduler } from 'features/parameters/types/parameterSchemas';
|
||||
import { zTabName } from 'features/ui/store/uiTypes';
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
import z from 'zod';
|
||||
|
||||
/**
|
||||
* A disable-able application feature
|
||||
*/
|
||||
export type AppFeature =
|
||||
| 'faceRestore'
|
||||
| 'upscaling'
|
||||
| 'lightbox'
|
||||
| 'modelManager'
|
||||
| 'githubLink'
|
||||
| 'discordLink'
|
||||
| 'bugLink'
|
||||
| 'aboutModal'
|
||||
| 'localization'
|
||||
| 'consoleLogging'
|
||||
| 'dynamicPrompting'
|
||||
| 'batches'
|
||||
| 'syncModels'
|
||||
| 'multiselect'
|
||||
| 'pauseQueue'
|
||||
| 'resumeQueue'
|
||||
| 'invocationCache'
|
||||
| 'modelCache'
|
||||
| 'bulkDownload'
|
||||
| 'starterModels'
|
||||
| 'hfToken'
|
||||
| 'retryQueueItem'
|
||||
| 'cancelAndClearAll'
|
||||
| 'chatGPT4oHigh'
|
||||
| 'modelRelationships';
|
||||
/**
|
||||
* A disable-able Stable Diffusion feature
|
||||
*/
|
||||
export type SDFeature =
|
||||
| 'controlNet'
|
||||
| 'noise'
|
||||
| 'perlinNoise'
|
||||
| 'noiseThreshold'
|
||||
| 'variation'
|
||||
| 'symmetry'
|
||||
| 'seamless'
|
||||
| 'hires'
|
||||
| 'lora'
|
||||
| 'embedding'
|
||||
| 'vae'
|
||||
| 'hrf';
|
||||
const zAppFeature = z.enum([
|
||||
'faceRestore',
|
||||
'upscaling',
|
||||
'lightbox',
|
||||
'modelManager',
|
||||
'githubLink',
|
||||
'discordLink',
|
||||
'bugLink',
|
||||
'aboutModal',
|
||||
'localization',
|
||||
'consoleLogging',
|
||||
'dynamicPrompting',
|
||||
'batches',
|
||||
'syncModels',
|
||||
'multiselect',
|
||||
'pauseQueue',
|
||||
'resumeQueue',
|
||||
'invocationCache',
|
||||
'modelCache',
|
||||
'bulkDownload',
|
||||
'starterModels',
|
||||
'hfToken',
|
||||
'retryQueueItem',
|
||||
'cancelAndClearAll',
|
||||
'chatGPT4oHigh',
|
||||
'modelRelationships',
|
||||
]);
|
||||
export type AppFeature = z.infer<typeof zAppFeature>;
|
||||
|
||||
export type NumericalParameterConfig = {
|
||||
initial: number;
|
||||
sliderMin: number;
|
||||
sliderMax: number;
|
||||
numberInputMin: number;
|
||||
numberInputMax: number;
|
||||
fineStep: number;
|
||||
coarseStep: number;
|
||||
};
|
||||
const zSDFeature = z.enum([
|
||||
'controlNet',
|
||||
'noise',
|
||||
'perlinNoise',
|
||||
'noiseThreshold',
|
||||
'variation',
|
||||
'symmetry',
|
||||
'seamless',
|
||||
'hires',
|
||||
'lora',
|
||||
'embedding',
|
||||
'vae',
|
||||
'hrf',
|
||||
]);
|
||||
export type SDFeature = z.infer<typeof zSDFeature>;
|
||||
|
||||
const zNumericalParameterConfig = z.object({
|
||||
initial: z.number().default(512),
|
||||
sliderMin: z.number().default(64),
|
||||
sliderMax: z.number().default(1536),
|
||||
numberInputMin: z.number().default(64),
|
||||
numberInputMax: z.number().default(4096),
|
||||
fineStep: z.number().default(8),
|
||||
coarseStep: z.number().default(64),
|
||||
});
|
||||
export type NumericalParameterConfig = z.infer<typeof zNumericalParameterConfig>;
|
||||
|
||||
/**
|
||||
* Configuration options for the InvokeAI UI.
|
||||
* Distinct from system settings which may be changed inside the app.
|
||||
*/
|
||||
export type AppConfig = {
|
||||
export const zAppConfig = z.object({
|
||||
/**
|
||||
* Whether or not we should update image urls when image loading errors
|
||||
*/
|
||||
shouldUpdateImagesOnConnect: boolean;
|
||||
shouldFetchMetadataFromApi: boolean;
|
||||
shouldUpdateImagesOnConnect: z.boolean(),
|
||||
shouldFetchMetadataFromApi: z.boolean(),
|
||||
/**
|
||||
* Sets a size limit for outputs on the upscaling tab. This is a maximum dimension, so the actual max number of pixels
|
||||
* will be the square of this value.
|
||||
*/
|
||||
maxUpscaleDimension?: number;
|
||||
allowPrivateBoards: boolean;
|
||||
allowPrivateStylePresets: boolean;
|
||||
allowClientSideUpload: boolean;
|
||||
allowPublishWorkflows: boolean;
|
||||
allowPromptExpansion: boolean;
|
||||
disabledTabs: TabName[];
|
||||
disabledFeatures: AppFeature[];
|
||||
disabledSDFeatures: SDFeature[];
|
||||
nodesAllowlist: string[] | undefined;
|
||||
nodesDenylist: string[] | undefined;
|
||||
metadataFetchDebounce?: number;
|
||||
workflowFetchDebounce?: number;
|
||||
isLocal?: boolean;
|
||||
shouldShowCredits: boolean;
|
||||
sd: {
|
||||
defaultModel?: string;
|
||||
disabledControlNetModels: string[];
|
||||
disabledControlNetProcessors: FilterType[];
|
||||
maxUpscaleDimension: z.number().optional(),
|
||||
allowPrivateBoards: z.boolean(),
|
||||
allowPrivateStylePresets: z.boolean(),
|
||||
allowClientSideUpload: z.boolean(),
|
||||
allowPublishWorkflows: z.boolean(),
|
||||
allowPromptExpansion: z.boolean(),
|
||||
disabledTabs: z.array(zTabName),
|
||||
disabledFeatures: z.array(zAppFeature),
|
||||
disabledSDFeatures: z.array(zSDFeature),
|
||||
nodesAllowlist: z.array(z.string()).optional(),
|
||||
nodesDenylist: z.array(z.string()).optional(),
|
||||
metadataFetchDebounce: z.number().int().optional(),
|
||||
workflowFetchDebounce: z.number().int().optional(),
|
||||
isLocal: z.boolean().optional(),
|
||||
shouldShowCredits: z.boolean().optional(),
|
||||
sd: z.object({
|
||||
defaultModel: z.string().optional(),
|
||||
disabledControlNetModels: z.array(z.string()),
|
||||
disabledControlNetProcessors: z.array(zFilterType),
|
||||
// Core parameters
|
||||
iterations: NumericalParameterConfig;
|
||||
width: NumericalParameterConfig; // initial value comes from model
|
||||
height: NumericalParameterConfig; // initial value comes from model
|
||||
steps: NumericalParameterConfig;
|
||||
guidance: NumericalParameterConfig;
|
||||
cfgRescaleMultiplier: NumericalParameterConfig;
|
||||
img2imgStrength: NumericalParameterConfig;
|
||||
scheduler?: ParameterScheduler;
|
||||
vaePrecision?: ParameterPrecision;
|
||||
iterations: zNumericalParameterConfig,
|
||||
width: zNumericalParameterConfig,
|
||||
height: zNumericalParameterConfig,
|
||||
steps: zNumericalParameterConfig,
|
||||
guidance: zNumericalParameterConfig,
|
||||
cfgRescaleMultiplier: zNumericalParameterConfig,
|
||||
img2imgStrength: zNumericalParameterConfig,
|
||||
scheduler: zParameterScheduler.optional(),
|
||||
vaePrecision: zParameterPrecision.optional(),
|
||||
// Canvas
|
||||
boundingBoxHeight: NumericalParameterConfig; // initial value comes from model
|
||||
boundingBoxWidth: NumericalParameterConfig; // initial value comes from model
|
||||
scaledBoundingBoxHeight: NumericalParameterConfig; // initial value comes from model
|
||||
scaledBoundingBoxWidth: NumericalParameterConfig; // initial value comes from model
|
||||
canvasCoherenceStrength: NumericalParameterConfig;
|
||||
canvasCoherenceEdgeSize: NumericalParameterConfig;
|
||||
infillTileSize: NumericalParameterConfig;
|
||||
infillPatchmatchDownscaleSize: NumericalParameterConfig;
|
||||
boundingBoxHeight: zNumericalParameterConfig,
|
||||
boundingBoxWidth: zNumericalParameterConfig,
|
||||
scaledBoundingBoxHeight: zNumericalParameterConfig,
|
||||
scaledBoundingBoxWidth: zNumericalParameterConfig,
|
||||
canvasCoherenceStrength: zNumericalParameterConfig,
|
||||
canvasCoherenceEdgeSize: zNumericalParameterConfig,
|
||||
infillTileSize: zNumericalParameterConfig,
|
||||
infillPatchmatchDownscaleSize: zNumericalParameterConfig,
|
||||
// Misc advanced
|
||||
clipSkip: NumericalParameterConfig; // slider and input max are ignored for this, because the values depend on the model
|
||||
maskBlur: NumericalParameterConfig;
|
||||
hrfStrength: NumericalParameterConfig;
|
||||
dynamicPrompts: {
|
||||
maxPrompts: NumericalParameterConfig;
|
||||
};
|
||||
ca: {
|
||||
weight: NumericalParameterConfig;
|
||||
};
|
||||
};
|
||||
flux: {
|
||||
guidance: NumericalParameterConfig;
|
||||
};
|
||||
};
|
||||
clipSkip: zNumericalParameterConfig, // slider and input max are ignored for this, because the values depend on the model
|
||||
maskBlur: zNumericalParameterConfig,
|
||||
hrfStrength: zNumericalParameterConfig,
|
||||
dynamicPrompts: z.object({
|
||||
maxPrompts: zNumericalParameterConfig,
|
||||
}),
|
||||
ca: z.object({
|
||||
weight: zNumericalParameterConfig,
|
||||
}),
|
||||
}),
|
||||
flux: z.object({
|
||||
guidance: zNumericalParameterConfig,
|
||||
}),
|
||||
});
|
||||
|
||||
export type AppConfig = z.infer<typeof zAppConfig>;
|
||||
export type PartialAppConfig = PartialDeep<AppConfig>;
|
||||
|
||||
export const getDefaultAppConfig = (): AppConfig => ({
|
||||
isLocal: true,
|
||||
shouldUpdateImagesOnConnect: false,
|
||||
shouldFetchMetadataFromApi: false,
|
||||
allowPrivateBoards: false,
|
||||
allowPrivateStylePresets: false,
|
||||
allowClientSideUpload: false,
|
||||
allowPublishWorkflows: false,
|
||||
allowPromptExpansion: false,
|
||||
shouldShowCredits: false,
|
||||
disabledTabs: [],
|
||||
disabledFeatures: ['lightbox', 'faceRestore', 'batches'] satisfies AppFeature[],
|
||||
disabledSDFeatures: ['variation', 'symmetry', 'hires', 'perlinNoise', 'noiseThreshold'] satisfies SDFeature[],
|
||||
sd: {
|
||||
disabledControlNetModels: [],
|
||||
disabledControlNetProcessors: [],
|
||||
iterations: {
|
||||
initial: 1,
|
||||
sliderMin: 1,
|
||||
sliderMax: 1000,
|
||||
numberInputMin: 1,
|
||||
numberInputMax: 10000,
|
||||
fineStep: 1,
|
||||
coarseStep: 1,
|
||||
},
|
||||
width: zNumericalParameterConfig.parse({}), // initial value comes from model
|
||||
height: zNumericalParameterConfig.parse({}), // initial value comes from model
|
||||
boundingBoxWidth: zNumericalParameterConfig.parse({}), // initial value comes from model
|
||||
boundingBoxHeight: zNumericalParameterConfig.parse({}), // initial value comes from model
|
||||
scaledBoundingBoxWidth: zNumericalParameterConfig.parse({}), // initial value comes from model
|
||||
scaledBoundingBoxHeight: zNumericalParameterConfig.parse({}), // initial value comes from model
|
||||
scheduler: 'dpmpp_3m_k' as const,
|
||||
vaePrecision: 'fp32' as const,
|
||||
steps: {
|
||||
initial: 30,
|
||||
sliderMin: 1,
|
||||
sliderMax: 100,
|
||||
numberInputMin: 1,
|
||||
numberInputMax: 500,
|
||||
fineStep: 1,
|
||||
coarseStep: 1,
|
||||
},
|
||||
guidance: {
|
||||
initial: 7,
|
||||
sliderMin: 1,
|
||||
sliderMax: 20,
|
||||
numberInputMin: 1,
|
||||
numberInputMax: 200,
|
||||
fineStep: 0.1,
|
||||
coarseStep: 0.5,
|
||||
},
|
||||
img2imgStrength: {
|
||||
initial: 0.7,
|
||||
sliderMin: 0,
|
||||
sliderMax: 1,
|
||||
numberInputMin: 0,
|
||||
numberInputMax: 1,
|
||||
fineStep: 0.01,
|
||||
coarseStep: 0.05,
|
||||
},
|
||||
canvasCoherenceStrength: {
|
||||
initial: 0.3,
|
||||
sliderMin: 0,
|
||||
sliderMax: 1,
|
||||
numberInputMin: 0,
|
||||
numberInputMax: 1,
|
||||
fineStep: 0.01,
|
||||
coarseStep: 0.05,
|
||||
},
|
||||
hrfStrength: {
|
||||
initial: 0.45,
|
||||
sliderMin: 0,
|
||||
sliderMax: 1,
|
||||
numberInputMin: 0,
|
||||
numberInputMax: 1,
|
||||
fineStep: 0.01,
|
||||
coarseStep: 0.05,
|
||||
},
|
||||
canvasCoherenceEdgeSize: {
|
||||
initial: 16,
|
||||
sliderMin: 0,
|
||||
sliderMax: 128,
|
||||
numberInputMin: 0,
|
||||
numberInputMax: 1024,
|
||||
fineStep: 8,
|
||||
coarseStep: 16,
|
||||
},
|
||||
cfgRescaleMultiplier: {
|
||||
initial: 0,
|
||||
sliderMin: 0,
|
||||
sliderMax: 0.99,
|
||||
numberInputMin: 0,
|
||||
numberInputMax: 0.99,
|
||||
fineStep: 0.05,
|
||||
coarseStep: 0.1,
|
||||
},
|
||||
clipSkip: {
|
||||
initial: 0,
|
||||
sliderMin: 0,
|
||||
sliderMax: 12, // determined by model selection, unused in practice
|
||||
numberInputMin: 0,
|
||||
numberInputMax: 12, // determined by model selection, unused in practice
|
||||
fineStep: 1,
|
||||
coarseStep: 1,
|
||||
},
|
||||
infillPatchmatchDownscaleSize: {
|
||||
initial: 1,
|
||||
sliderMin: 1,
|
||||
sliderMax: 10,
|
||||
numberInputMin: 1,
|
||||
numberInputMax: 10,
|
||||
fineStep: 1,
|
||||
coarseStep: 1,
|
||||
},
|
||||
infillTileSize: {
|
||||
initial: 32,
|
||||
sliderMin: 16,
|
||||
sliderMax: 64,
|
||||
numberInputMin: 16,
|
||||
numberInputMax: 256,
|
||||
fineStep: 1,
|
||||
coarseStep: 1,
|
||||
},
|
||||
maskBlur: {
|
||||
initial: 16,
|
||||
sliderMin: 0,
|
||||
sliderMax: 128,
|
||||
numberInputMin: 0,
|
||||
numberInputMax: 512,
|
||||
fineStep: 1,
|
||||
coarseStep: 1,
|
||||
},
|
||||
ca: {
|
||||
weight: {
|
||||
initial: 1,
|
||||
sliderMin: 0,
|
||||
sliderMax: 2,
|
||||
numberInputMin: -1,
|
||||
numberInputMax: 2,
|
||||
fineStep: 0.01,
|
||||
coarseStep: 0.05,
|
||||
},
|
||||
},
|
||||
dynamicPrompts: {
|
||||
maxPrompts: {
|
||||
initial: 100,
|
||||
sliderMin: 1,
|
||||
sliderMax: 1000,
|
||||
numberInputMin: 1,
|
||||
numberInputMax: 10000,
|
||||
fineStep: 1,
|
||||
coarseStep: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
flux: {
|
||||
guidance: {
|
||||
initial: 4,
|
||||
sliderMin: 2,
|
||||
sliderMax: 6,
|
||||
numberInputMin: 1,
|
||||
numberInputMax: 20,
|
||||
fineStep: 0.1,
|
||||
coarseStep: 0.5,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import type { ChangeBoardModalState } from './types';
|
||||
|
||||
export const initialState: ChangeBoardModalState = {
|
||||
isModalOpen: false,
|
||||
image_names: [],
|
||||
};
|
||||
@@ -2,15 +2,19 @@ import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import type { SliceConfig } from 'app/store/types';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import z from 'zod';
|
||||
|
||||
import { initialState } from './initialState';
|
||||
const zChangeBoardModalState = z.object({
|
||||
isModalOpen: z.boolean().default(false),
|
||||
image_names: z.array(z.string()).default(() => []),
|
||||
});
|
||||
type ChangeBoardModalState = z.infer<typeof zChangeBoardModalState>;
|
||||
|
||||
const getInitialState = () => deepClone(initialState);
|
||||
const getInitialState = (): ChangeBoardModalState => zChangeBoardModalState.parse({});
|
||||
|
||||
const slice = createSlice({
|
||||
name: 'changeBoardModal',
|
||||
initialState,
|
||||
initialState: getInitialState(),
|
||||
reducers: {
|
||||
isModalOpenChanged: (state, action: PayloadAction<boolean>) => {
|
||||
state.isModalOpen = action.payload;
|
||||
@@ -31,5 +35,6 @@ export const selectChangeBoardModalSlice = (state: RootState) => state.changeBoa
|
||||
|
||||
export const changeBoardModalSliceConfig: SliceConfig<typeof slice> = {
|
||||
slice,
|
||||
zSchema: zChangeBoardModalState,
|
||||
getInitialState,
|
||||
};
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
export type ChangeBoardModalState = {
|
||||
isModalOpen: boolean;
|
||||
image_names: string[];
|
||||
};
|
||||
@@ -12,32 +12,32 @@ const zCanvasSettingsState = z.object({
|
||||
/**
|
||||
* Whether to show HUD (Heads-Up Display) on the canvas.
|
||||
*/
|
||||
showHUD: z.boolean().default(true),
|
||||
showHUD: z.boolean(),
|
||||
/**
|
||||
* Whether to clip lines and shapes to the generation bounding box. If disabled, lines and shapes will be clipped to
|
||||
* the canvas bounds.
|
||||
*/
|
||||
clipToBbox: z.boolean().default(false),
|
||||
clipToBbox: z.boolean(),
|
||||
/**
|
||||
* Whether to show a dynamic grid on the canvas. If disabled, a checkerboard pattern will be shown instead.
|
||||
*/
|
||||
dynamicGrid: z.boolean().default(false),
|
||||
dynamicGrid: z.boolean(),
|
||||
/**
|
||||
* Whether to invert the scroll direction when adjusting the brush or eraser width with the scroll wheel.
|
||||
*/
|
||||
invertScrollForToolWidth: z.boolean().default(false),
|
||||
invertScrollForToolWidth: z.boolean(),
|
||||
/**
|
||||
* The width of the brush tool.
|
||||
*/
|
||||
brushWidth: z.int().gt(0).default(50),
|
||||
brushWidth: z.int().gt(0),
|
||||
/**
|
||||
* The width of the eraser tool.
|
||||
*/
|
||||
eraserWidth: z.int().gt(0).default(50),
|
||||
eraserWidth: z.int().gt(0),
|
||||
/**
|
||||
* The color to use when drawing lines or filling shapes.
|
||||
*/
|
||||
color: zRgbaColor.default({ r: 31, g: 160, b: 224, a: 1 }), // invokeBlue.500
|
||||
color: zRgbaColor,
|
||||
/**
|
||||
* Whether to composite inpainted/outpainted regions back onto the source image when saving canvas generations.
|
||||
*
|
||||
@@ -45,55 +45,75 @@ const zCanvasSettingsState = z.object({
|
||||
*
|
||||
* When `sendToCanvas` is disabled, this setting is ignored, masked regions will always be composited.
|
||||
*/
|
||||
outputOnlyMaskedRegions: z.boolean().default(true),
|
||||
outputOnlyMaskedRegions: z.boolean(),
|
||||
/**
|
||||
* Whether to automatically process the operations like filtering and auto-masking.
|
||||
*/
|
||||
autoProcess: z.boolean().default(true),
|
||||
autoProcess: z.boolean(),
|
||||
/**
|
||||
* The snap-to-grid setting for the canvas.
|
||||
*/
|
||||
snapToGrid: z.boolean().default(true),
|
||||
snapToGrid: z.boolean(),
|
||||
/**
|
||||
* Whether to show progress on the canvas when generating images.
|
||||
*/
|
||||
showProgressOnCanvas: z.boolean().default(true),
|
||||
showProgressOnCanvas: z.boolean(),
|
||||
/**
|
||||
* Whether to show the bounding box overlay on the canvas.
|
||||
*/
|
||||
bboxOverlay: z.boolean().default(false),
|
||||
bboxOverlay: z.boolean(),
|
||||
/**
|
||||
* Whether to preserve the masked region instead of inpainting it.
|
||||
*/
|
||||
preserveMask: z.boolean().default(false),
|
||||
preserveMask: z.boolean(),
|
||||
/**
|
||||
* Whether to show only raster layers while staging.
|
||||
*/
|
||||
isolatedStagingPreview: z.boolean().default(true),
|
||||
isolatedStagingPreview: z.boolean(),
|
||||
/**
|
||||
* Whether to show only the selected layer while filtering, transforming, or doing other operations.
|
||||
*/
|
||||
isolatedLayerPreview: z.boolean().default(true),
|
||||
isolatedLayerPreview: z.boolean(),
|
||||
/**
|
||||
* Whether to use pressure sensitivity for the brush and eraser tool when a pen device is used.
|
||||
*/
|
||||
pressureSensitivity: z.boolean().default(true),
|
||||
pressureSensitivity: z.boolean(),
|
||||
/**
|
||||
* Whether to show the rule of thirds composition guide overlay on the canvas.
|
||||
*/
|
||||
ruleOfThirds: z.boolean().default(false),
|
||||
ruleOfThirds: z.boolean(),
|
||||
/**
|
||||
* Whether to save all staging images to the gallery instead of keeping them as intermediate images.
|
||||
*/
|
||||
saveAllImagesToGallery: z.boolean().default(false),
|
||||
saveAllImagesToGallery: z.boolean(),
|
||||
/**
|
||||
* The auto-switch mode for the canvas staging area.
|
||||
*/
|
||||
stagingAreaAutoSwitch: zAutoSwitchMode.default('switch_on_start'),
|
||||
stagingAreaAutoSwitch: zAutoSwitchMode,
|
||||
});
|
||||
|
||||
type CanvasSettingsState = z.infer<typeof zCanvasSettingsState>;
|
||||
const getInitialState = () => zCanvasSettingsState.parse({});
|
||||
const getInitialState = (): CanvasSettingsState => ({
|
||||
showHUD: true,
|
||||
clipToBbox: false,
|
||||
dynamicGrid: false,
|
||||
invertScrollForToolWidth: false,
|
||||
brushWidth: 50,
|
||||
eraserWidth: 50,
|
||||
color: { r: 31, g: 160, b: 224, a: 1 }, // invokeBlue.500
|
||||
outputOnlyMaskedRegions: true,
|
||||
autoProcess: true,
|
||||
snapToGrid: true,
|
||||
showProgressOnCanvas: true,
|
||||
bboxOverlay: false,
|
||||
preserveMask: false,
|
||||
isolatedStagingPreview: true,
|
||||
isolatedLayerPreview: true,
|
||||
pressureSensitivity: true,
|
||||
ruleOfThirds: false,
|
||||
saveAllImagesToGallery: false,
|
||||
stagingAreaAutoSwitch: 'switch_on_start' as const,
|
||||
});
|
||||
|
||||
const slice = createSlice({
|
||||
name: 'canvasSettings',
|
||||
@@ -187,16 +207,12 @@ export const {
|
||||
settingsStagingAreaAutoSwitchChanged,
|
||||
} = slice.actions;
|
||||
|
||||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||
const migrate = (state: any): any => {
|
||||
return state;
|
||||
};
|
||||
|
||||
export const canvasSettingsSliceConfig: SliceConfig<typeof slice> = {
|
||||
slice,
|
||||
zSchema: zCanvasSettingsState,
|
||||
getInitialState,
|
||||
persistConfig: {
|
||||
migrate,
|
||||
migrate: (state) => zCanvasSettingsState.parse(state),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -80,6 +80,7 @@ import {
|
||||
isFLUXReduxConfig,
|
||||
isImagenAspectRatioID,
|
||||
isIPAdapterConfig,
|
||||
zCanvasState,
|
||||
} from './types';
|
||||
import {
|
||||
converters,
|
||||
@@ -1677,11 +1678,6 @@ export const {
|
||||
// inpaintMaskRecalled,
|
||||
} = slice.actions;
|
||||
|
||||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||
const migrate = (state: any): any => {
|
||||
return state;
|
||||
};
|
||||
|
||||
const syncScaledSize = (state: CanvasState) => {
|
||||
if (API_BASE_MODELS.includes(state.bbox.modelBase)) {
|
||||
// Imagen3 has fixed sizes. Scaled bbox is not supported.
|
||||
@@ -1724,8 +1720,9 @@ const canvasUndoableConfig: UndoableOptions<CanvasState, UnknownAction> = {
|
||||
export const canvasSliceConfig: SliceConfig<typeof slice> = {
|
||||
slice,
|
||||
getInitialState: getInitialCanvasState,
|
||||
zSchema: zCanvasState,
|
||||
persistConfig: {
|
||||
migrate,
|
||||
migrate: (state) => zCanvasState.parse(state),
|
||||
},
|
||||
undoableConfig: {
|
||||
reduxUndoOptions: canvasUndoableConfig,
|
||||
|
||||
@@ -3,24 +3,19 @@ import { EMPTY_ARRAY } from 'app/store/constants';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import type { SliceConfig } from 'app/store/types';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import { useMemo } from 'react';
|
||||
import { queueApi } from 'services/api/endpoints/queue';
|
||||
import z from 'zod';
|
||||
|
||||
type CanvasStagingAreaState = {
|
||||
_version: 1;
|
||||
canvasSessionId: string;
|
||||
canvasDiscardedQueueItems: number[];
|
||||
};
|
||||
const zCanvasStagingAreaState = z.object({
|
||||
_version: z.literal(1).default(1),
|
||||
canvasSessionId: z.string().default(() => getPrefixedId('canvas')),
|
||||
canvasDiscardedQueueItems: z.array(z.number().int()).default(() => []),
|
||||
});
|
||||
type CanvasStagingAreaState = z.infer<typeof zCanvasStagingAreaState>;
|
||||
|
||||
const INITIAL_STATE: CanvasStagingAreaState = {
|
||||
_version: 1,
|
||||
canvasSessionId: getPrefixedId('canvas'),
|
||||
canvasDiscardedQueueItems: [],
|
||||
};
|
||||
|
||||
const getInitialState = (): CanvasStagingAreaState => deepClone(INITIAL_STATE);
|
||||
const getInitialState = (): CanvasStagingAreaState => zCanvasStagingAreaState.parse({});
|
||||
|
||||
const slice = createSlice({
|
||||
name: 'canvasSession',
|
||||
@@ -51,20 +46,22 @@ const slice = createSlice({
|
||||
|
||||
export const { canvasSessionReset, canvasQueueItemDiscarded } = slice.actions;
|
||||
|
||||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||
const migrate = (state: any): any => {
|
||||
if (!('_version' in state)) {
|
||||
state._version = 1;
|
||||
state.canvasSessionId = state.canvasSessionId ?? getPrefixedId('canvas');
|
||||
}
|
||||
|
||||
return state;
|
||||
};
|
||||
|
||||
export const canvasSessionSliceConfig: SliceConfig<typeof slice> = {
|
||||
slice,
|
||||
zSchema: zCanvasStagingAreaState,
|
||||
getInitialState,
|
||||
persistConfig: { migrate },
|
||||
persistConfig: {
|
||||
migrate: (state) => {
|
||||
{
|
||||
if (!('_version' in state)) {
|
||||
state._version = 1;
|
||||
state.canvasSessionId = state.canvasSessionId ?? getPrefixedId('canvas');
|
||||
}
|
||||
|
||||
return zCanvasStagingAreaState.parse(state);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const selectCanvasSessionSlice = (s: RootState) => s[slice.name];
|
||||
|
||||
@@ -166,7 +166,7 @@ const _zFilterConfig = z.discriminatedUnion('type', [
|
||||
]);
|
||||
export type FilterConfig = z.infer<typeof _zFilterConfig>;
|
||||
|
||||
const zFilterType = z.enum([
|
||||
export const zFilterType = z.enum([
|
||||
'adjust_image',
|
||||
'canny_edge_detection',
|
||||
'color_map',
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
isChatGPT4oAspectRatioID,
|
||||
isFluxKontextAspectRatioID,
|
||||
isImagenAspectRatioID,
|
||||
zParamsState,
|
||||
} from 'features/controlLayers/store/types';
|
||||
import { calculateNewSize } from 'features/controlLayers/util/getScaledBoundingBoxDimensions';
|
||||
import { CLIP_SKIP_MAP } from 'features/parameters/types/constants';
|
||||
@@ -400,15 +401,13 @@ export const {
|
||||
paramsReset,
|
||||
} = slice.actions;
|
||||
|
||||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||
const migrate = (state: any): any => {
|
||||
return state;
|
||||
};
|
||||
|
||||
export const paramsSliceConfig: SliceConfig<typeof slice> = {
|
||||
slice,
|
||||
zSchema: zParamsState,
|
||||
getInitialState: getInitialParamsState,
|
||||
persistConfig: { migrate },
|
||||
persistConfig: {
|
||||
migrate: (state) => zParamsState.parse(state),
|
||||
},
|
||||
};
|
||||
|
||||
export const selectParamsSlice = (state: RootState) => state.params;
|
||||
|
||||
@@ -19,7 +19,7 @@ import { assert } from 'tsafe';
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
|
||||
import type { CLIPVisionModelV2, IPMethodV2, RefImageState } from './types';
|
||||
import { getInitialRefImagesState, isFLUXReduxConfig, isIPAdapterConfig } from './types';
|
||||
import { getInitialRefImagesState, isFLUXReduxConfig, isIPAdapterConfig, zRefImagesState } from './types';
|
||||
import {
|
||||
getReferenceImageState,
|
||||
imageDTOToImageWithDims,
|
||||
@@ -273,6 +273,7 @@ const migrate = (state: any): any => {
|
||||
|
||||
export const refImagesSliceConfig: SliceConfig<typeof slice> = {
|
||||
slice,
|
||||
zSchema: zRefImagesState,
|
||||
getInitialState: getInitialRefImagesState,
|
||||
persistConfig: {
|
||||
migrate,
|
||||
|
||||
@@ -522,62 +522,108 @@ const zDimensionsState = z.object({
|
||||
aspectRatio: zAspectRatioConfig,
|
||||
});
|
||||
|
||||
const zParamsState = z.object({
|
||||
maskBlur: z.number().default(16),
|
||||
maskBlurMethod: zParameterMaskBlurMethod.default('box'),
|
||||
canvasCoherenceMode: zParameterCanvasCoherenceMode.default('Gaussian Blur'),
|
||||
canvasCoherenceMinDenoise: zParameterStrength.default(0),
|
||||
canvasCoherenceEdgeSize: z.number().default(16),
|
||||
infillMethod: z.string().default('lama'),
|
||||
infillTileSize: z.number().default(32),
|
||||
infillPatchmatchDownscaleSize: z.number().default(1),
|
||||
infillColorValue: zRgbaColor.default({ r: 0, g: 0, b: 0, a: 1 }),
|
||||
cfgScale: zParameterCFGScale.default(7.5),
|
||||
cfgRescaleMultiplier: zParameterCFGRescaleMultiplier.default(0),
|
||||
guidance: zParameterGuidance.default(4),
|
||||
img2imgStrength: zParameterStrength.default(0.75),
|
||||
optimizedDenoisingEnabled: z.boolean().default(true),
|
||||
iterations: z.number().default(1),
|
||||
scheduler: zParameterScheduler.default('dpmpp_3m_k'),
|
||||
upscaleScheduler: zParameterScheduler.default('kdpm_2'),
|
||||
upscaleCfgScale: zParameterCFGScale.default(2),
|
||||
seed: zParameterSeed.default(0),
|
||||
shouldRandomizeSeed: z.boolean().default(true),
|
||||
steps: zParameterSteps.default(30),
|
||||
model: zParameterModel.nullable().default(null),
|
||||
vae: zParameterVAEModel.nullable().default(null),
|
||||
vaePrecision: zParameterPrecision.default('fp32'),
|
||||
fluxVAE: zParameterVAEModel.nullable().default(null),
|
||||
seamlessXAxis: z.boolean().default(false),
|
||||
seamlessYAxis: z.boolean().default(false),
|
||||
clipSkip: z.number().default(0),
|
||||
shouldUseCpuNoise: z.boolean().default(true),
|
||||
positivePrompt: zParameterPositivePrompt.default(''),
|
||||
// Negative prompt may be disabled, in which case it will be null
|
||||
negativePrompt: zParameterNegativePrompt.default(null),
|
||||
positivePrompt2: zParameterPositiveStylePromptSDXL.default(''),
|
||||
negativePrompt2: zParameterNegativeStylePromptSDXL.default(''),
|
||||
shouldConcatPrompts: z.boolean().default(true),
|
||||
refinerModel: zParameterSDXLRefinerModel.nullable().default(null),
|
||||
refinerSteps: z.number().default(20),
|
||||
refinerCFGScale: z.number().default(7.5),
|
||||
refinerScheduler: zParameterScheduler.default('euler'),
|
||||
refinerPositiveAestheticScore: z.number().default(6),
|
||||
refinerNegativeAestheticScore: z.number().default(2.5),
|
||||
refinerStart: z.number().default(0.8),
|
||||
t5EncoderModel: zParameterT5EncoderModel.nullable().default(null),
|
||||
clipEmbedModel: zParameterCLIPEmbedModel.nullable().default(null),
|
||||
clipLEmbedModel: zParameterCLIPLEmbedModel.nullable().default(null),
|
||||
clipGEmbedModel: zParameterCLIPGEmbedModel.nullable().default(null),
|
||||
controlLora: zParameterControlLoRAModel.nullable().default(null),
|
||||
dimensions: zDimensionsState.default({
|
||||
rect: { x: 0, y: 0, width: 512, height: 512 },
|
||||
aspectRatio: DEFAULT_ASPECT_RATIO_CONFIG,
|
||||
}),
|
||||
export const zParamsState = z.object({
|
||||
maskBlur: z.number(),
|
||||
maskBlurMethod: zParameterMaskBlurMethod,
|
||||
canvasCoherenceMode: zParameterCanvasCoherenceMode,
|
||||
canvasCoherenceMinDenoise: zParameterStrength,
|
||||
canvasCoherenceEdgeSize: z.number(),
|
||||
infillMethod: z.string(),
|
||||
infillTileSize: z.number(),
|
||||
infillPatchmatchDownscaleSize: z.number(),
|
||||
infillColorValue: zRgbaColor,
|
||||
cfgScale: zParameterCFGScale,
|
||||
cfgRescaleMultiplier: zParameterCFGRescaleMultiplier,
|
||||
guidance: zParameterGuidance,
|
||||
img2imgStrength: zParameterStrength,
|
||||
optimizedDenoisingEnabled: z.boolean(),
|
||||
iterations: z.number(),
|
||||
scheduler: zParameterScheduler,
|
||||
upscaleScheduler: zParameterScheduler,
|
||||
upscaleCfgScale: zParameterCFGScale,
|
||||
seed: zParameterSeed,
|
||||
shouldRandomizeSeed: z.boolean(),
|
||||
steps: zParameterSteps,
|
||||
model: zParameterModel.nullable(),
|
||||
vae: zParameterVAEModel.nullable(),
|
||||
vaePrecision: zParameterPrecision,
|
||||
fluxVAE: zParameterVAEModel.nullable(),
|
||||
seamlessXAxis: z.boolean(),
|
||||
seamlessYAxis: z.boolean(),
|
||||
clipSkip: z.number(),
|
||||
shouldUseCpuNoise: z.boolean(),
|
||||
positivePrompt: zParameterPositivePrompt,
|
||||
negativePrompt: zParameterNegativePrompt,
|
||||
positivePrompt2: zParameterPositiveStylePromptSDXL,
|
||||
negativePrompt2: zParameterNegativeStylePromptSDXL,
|
||||
shouldConcatPrompts: z.boolean(),
|
||||
refinerModel: zParameterSDXLRefinerModel.nullable(),
|
||||
refinerSteps: z.number(),
|
||||
refinerCFGScale: z.number(),
|
||||
refinerScheduler: zParameterScheduler,
|
||||
refinerPositiveAestheticScore: z.number(),
|
||||
refinerNegativeAestheticScore: z.number(),
|
||||
refinerStart: z.number(),
|
||||
t5EncoderModel: zParameterT5EncoderModel.nullable(),
|
||||
clipEmbedModel: zParameterCLIPEmbedModel.nullable(),
|
||||
clipLEmbedModel: zParameterCLIPLEmbedModel.nullable(),
|
||||
clipGEmbedModel: zParameterCLIPGEmbedModel.nullable(),
|
||||
controlLora: zParameterControlLoRAModel.nullable(),
|
||||
dimensions: zDimensionsState,
|
||||
});
|
||||
export type ParamsState = z.infer<typeof zParamsState>;
|
||||
const INITIAL_PARAMS_STATE = zParamsState.parse({});
|
||||
export const getInitialParamsState = () => deepClone(INITIAL_PARAMS_STATE);
|
||||
export const getInitialParamsState = (): ParamsState => ({
|
||||
maskBlur: 16,
|
||||
maskBlurMethod: 'box' as const,
|
||||
canvasCoherenceMode: 'Gaussian Blur' as const,
|
||||
canvasCoherenceMinDenoise: 0,
|
||||
canvasCoherenceEdgeSize: 16,
|
||||
infillMethod: 'lama' as const,
|
||||
infillTileSize: 32,
|
||||
infillPatchmatchDownscaleSize: 1,
|
||||
infillColorValue: { r: 0, g: 0, b: 0, a: 1 },
|
||||
cfgScale: 7.5,
|
||||
cfgRescaleMultiplier: 0,
|
||||
guidance: 4,
|
||||
img2imgStrength: 0.75,
|
||||
optimizedDenoisingEnabled: true,
|
||||
iterations: 1,
|
||||
scheduler: 'dpmpp_3m_k' as const,
|
||||
upscaleScheduler: 'kdpm_2' as const,
|
||||
upscaleCfgScale: 2,
|
||||
seed: 0,
|
||||
shouldRandomizeSeed: true,
|
||||
steps: 30,
|
||||
model: null,
|
||||
vae: null,
|
||||
vaePrecision: 'fp32' as const,
|
||||
fluxVAE: null,
|
||||
seamlessXAxis: false,
|
||||
seamlessYAxis: false,
|
||||
clipSkip: 0,
|
||||
shouldUseCpuNoise: true,
|
||||
positivePrompt: '',
|
||||
negativePrompt: null,
|
||||
positivePrompt2: '',
|
||||
negativePrompt2: '',
|
||||
shouldConcatPrompts: true,
|
||||
refinerModel: null,
|
||||
refinerSteps: 20,
|
||||
refinerCFGScale: 7.5,
|
||||
refinerScheduler: 'euler' as const,
|
||||
refinerPositiveAestheticScore: 6,
|
||||
refinerNegativeAestheticScore: 2.5,
|
||||
refinerStart: 0.8,
|
||||
t5EncoderModel: null,
|
||||
clipEmbedModel: null,
|
||||
clipLEmbedModel: null,
|
||||
clipGEmbedModel: null,
|
||||
controlLora: null,
|
||||
dimensions: {
|
||||
rect: { x: 0, y: 0, width: 512, height: 512 },
|
||||
aspectRatio: deepClone(DEFAULT_ASPECT_RATIO_CONFIG),
|
||||
},
|
||||
});
|
||||
|
||||
const zInpaintMasks = z.object({
|
||||
isHidden: z.boolean(),
|
||||
@@ -595,38 +641,45 @@ const zRegionalGuidance = z.object({
|
||||
isHidden: z.boolean(),
|
||||
entities: z.array(zCanvasRegionalGuidanceState),
|
||||
});
|
||||
const zCanvasState = z.object({
|
||||
_version: z.literal(3).default(3),
|
||||
selectedEntityIdentifier: zCanvasEntityIdentifer.nullable().default(null),
|
||||
bookmarkedEntityIdentifier: zCanvasEntityIdentifer.nullable().default(null),
|
||||
inpaintMasks: zInpaintMasks.default({ isHidden: false, entities: [] }),
|
||||
rasterLayers: zRasterLayers.default({ isHidden: false, entities: [] }),
|
||||
controlLayers: zControlLayers.default({ isHidden: false, entities: [] }),
|
||||
regionalGuidance: zRegionalGuidance.default({ isHidden: false, entities: [] }),
|
||||
bbox: zBboxState.default({
|
||||
rect: { x: 0, y: 0, width: 512, height: 512 },
|
||||
aspectRatio: DEFAULT_ASPECT_RATIO_CONFIG,
|
||||
scaleMethod: 'auto',
|
||||
scaledSize: { width: 512, height: 512 },
|
||||
modelBase: 'sd-1',
|
||||
}),
|
||||
export const zCanvasState = z.object({
|
||||
_version: z.literal(3),
|
||||
selectedEntityIdentifier: zCanvasEntityIdentifer.nullable(),
|
||||
bookmarkedEntityIdentifier: zCanvasEntityIdentifer.nullable(),
|
||||
inpaintMasks: zInpaintMasks,
|
||||
rasterLayers: zRasterLayers,
|
||||
controlLayers: zControlLayers,
|
||||
regionalGuidance: zRegionalGuidance,
|
||||
bbox: zBboxState,
|
||||
});
|
||||
export type CanvasState = z.infer<typeof zCanvasState>;
|
||||
export const getInitialCanvasState = (): CanvasState => ({
|
||||
_version: 3 as const,
|
||||
selectedEntityIdentifier: null,
|
||||
bookmarkedEntityIdentifier: null,
|
||||
inpaintMasks: { isHidden: false, entities: [] },
|
||||
rasterLayers: { isHidden: false, entities: [] },
|
||||
controlLayers: { isHidden: false, entities: [] },
|
||||
regionalGuidance: { isHidden: false, entities: [] },
|
||||
bbox: {
|
||||
rect: { x: 0, y: 0, width: 512, height: 512 },
|
||||
aspectRatio: deepClone(DEFAULT_ASPECT_RATIO_CONFIG),
|
||||
scaleMethod: 'auto' as const,
|
||||
scaledSize: { width: 512, height: 512 },
|
||||
modelBase: 'sd-1' as const,
|
||||
},
|
||||
});
|
||||
|
||||
const zRefImagesState = z.object({
|
||||
selectedEntityId: z.string().nullable().default(null),
|
||||
isPanelOpen: z.boolean().default(false),
|
||||
entities: z.array(zRefImageState).default(() => []),
|
||||
export const zRefImagesState = z.object({
|
||||
selectedEntityId: z.string().nullable(),
|
||||
isPanelOpen: z.boolean(),
|
||||
entities: z.array(zRefImageState),
|
||||
});
|
||||
export type RefImagesState = z.infer<typeof zRefImagesState>;
|
||||
const INITIAL_REF_IMAGES_STATE = zRefImagesState.parse({});
|
||||
export const getInitialRefImagesState = () => deepClone(INITIAL_REF_IMAGES_STATE);
|
||||
|
||||
/**
|
||||
* Gets a fresh canvas initial state with no references in memory to existing objects.
|
||||
*/
|
||||
const CANVAS_INITIAL_STATE = zCanvasState.parse({});
|
||||
export const getInitialCanvasState = () => deepClone(CANVAS_INITIAL_STATE);
|
||||
export const getInitialRefImagesState = (): RefImagesState => ({
|
||||
selectedEntityId: null,
|
||||
isPanelOpen: false,
|
||||
entities: [],
|
||||
});
|
||||
|
||||
export const zCanvasReferenceImageState_OLD = zCanvasEntityBase.extend({
|
||||
type: z.literal('reference_image'),
|
||||
|
||||
@@ -9,16 +9,17 @@ const zSeedBehaviour = z.enum(['PER_ITERATION', 'PER_PROMPT']);
|
||||
export const isSeedBehaviour = buildZodTypeGuard(zSeedBehaviour);
|
||||
export type SeedBehaviour = z.infer<typeof zSeedBehaviour>;
|
||||
|
||||
export interface DynamicPromptsState {
|
||||
_version: 1;
|
||||
maxPrompts: number;
|
||||
combinatorial: boolean;
|
||||
prompts: string[];
|
||||
parsingError: string | undefined | null;
|
||||
isError: boolean;
|
||||
isLoading: boolean;
|
||||
seedBehaviour: SeedBehaviour;
|
||||
}
|
||||
const zDynamicPromptsState = z.object({
|
||||
_version: z.literal(1),
|
||||
maxPrompts: z.number().int().min(1).max(1000),
|
||||
combinatorial: z.boolean(),
|
||||
prompts: z.array(z.string()),
|
||||
parsingError: z.string().nullish(),
|
||||
isError: z.boolean(),
|
||||
isLoading: z.boolean(),
|
||||
seedBehaviour: zSeedBehaviour,
|
||||
});
|
||||
type DynamicPromptsState = z.infer<typeof zDynamicPromptsState>;
|
||||
|
||||
const getInitialState = (): DynamicPromptsState => ({
|
||||
_version: 1,
|
||||
@@ -66,19 +67,17 @@ export const {
|
||||
seedBehaviourChanged,
|
||||
} = slice.actions;
|
||||
|
||||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||
const migrate = (state: any): any => {
|
||||
if (!('_version' in state)) {
|
||||
state._version = 1;
|
||||
}
|
||||
return state;
|
||||
};
|
||||
|
||||
export const dynamicPromptsSliceConfig: SliceConfig<typeof slice> = {
|
||||
slice,
|
||||
zSchema: zDynamicPromptsState,
|
||||
getInitialState,
|
||||
persistConfig: {
|
||||
migrate,
|
||||
migrate: (state) => {
|
||||
if (!('_version' in state)) {
|
||||
state._version = 1;
|
||||
}
|
||||
return zDynamicPromptsState.parse(state);
|
||||
},
|
||||
persistDenylist: ['prompts', 'parsingError', 'isError', 'isLoading'],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -2,190 +2,19 @@ import type { PayloadAction, Selector } from '@reduxjs/toolkit';
|
||||
import { createSelector, createSlice } from '@reduxjs/toolkit';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import type { SliceConfig } from 'app/store/types';
|
||||
import type { AppConfig, NumericalParameterConfig, PartialAppConfig } from 'app/types/invokeai';
|
||||
import { getDefaultAppConfig, type PartialAppConfig, zAppConfig } from 'app/types/invokeai';
|
||||
import { merge } from 'es-toolkit/compat';
|
||||
import z from 'zod';
|
||||
|
||||
const baseDimensionConfig: NumericalParameterConfig = {
|
||||
initial: 512, // determined by model selection, unused in practice
|
||||
sliderMin: 64,
|
||||
sliderMax: 1536,
|
||||
numberInputMin: 64,
|
||||
numberInputMax: 4096,
|
||||
fineStep: 8,
|
||||
coarseStep: 64,
|
||||
};
|
||||
|
||||
type ConfigState = AppConfig & { didLoad: boolean };
|
||||
const zConfigState = z.object({
|
||||
...zAppConfig.shape,
|
||||
didLoad: z.boolean(),
|
||||
});
|
||||
type ConfigState = z.infer<typeof zConfigState>;
|
||||
|
||||
const getInitialState = (): ConfigState => ({
|
||||
...getDefaultAppConfig(),
|
||||
didLoad: false,
|
||||
isLocal: true,
|
||||
shouldUpdateImagesOnConnect: false,
|
||||
shouldFetchMetadataFromApi: false,
|
||||
allowPrivateBoards: false,
|
||||
allowPrivateStylePresets: false,
|
||||
allowClientSideUpload: false,
|
||||
allowPublishWorkflows: false,
|
||||
allowPromptExpansion: false,
|
||||
shouldShowCredits: false,
|
||||
disabledTabs: [],
|
||||
disabledFeatures: ['lightbox', 'faceRestore', 'batches'],
|
||||
disabledSDFeatures: ['variation', 'symmetry', 'hires', 'perlinNoise', 'noiseThreshold'],
|
||||
nodesAllowlist: undefined,
|
||||
nodesDenylist: undefined,
|
||||
sd: {
|
||||
disabledControlNetModels: [],
|
||||
disabledControlNetProcessors: [],
|
||||
iterations: {
|
||||
initial: 1,
|
||||
sliderMin: 1,
|
||||
sliderMax: 1000,
|
||||
numberInputMin: 1,
|
||||
numberInputMax: 10000,
|
||||
fineStep: 1,
|
||||
coarseStep: 1,
|
||||
},
|
||||
width: { ...baseDimensionConfig },
|
||||
height: { ...baseDimensionConfig },
|
||||
boundingBoxWidth: { ...baseDimensionConfig },
|
||||
boundingBoxHeight: { ...baseDimensionConfig },
|
||||
scaledBoundingBoxWidth: { ...baseDimensionConfig },
|
||||
scaledBoundingBoxHeight: { ...baseDimensionConfig },
|
||||
scheduler: 'dpmpp_3m_k',
|
||||
vaePrecision: 'fp32',
|
||||
steps: {
|
||||
initial: 30,
|
||||
sliderMin: 1,
|
||||
sliderMax: 100,
|
||||
numberInputMin: 1,
|
||||
numberInputMax: 500,
|
||||
fineStep: 1,
|
||||
coarseStep: 1,
|
||||
},
|
||||
guidance: {
|
||||
initial: 7,
|
||||
sliderMin: 1,
|
||||
sliderMax: 20,
|
||||
numberInputMin: 1,
|
||||
numberInputMax: 200,
|
||||
fineStep: 0.1,
|
||||
coarseStep: 0.5,
|
||||
},
|
||||
img2imgStrength: {
|
||||
initial: 0.7,
|
||||
sliderMin: 0,
|
||||
sliderMax: 1,
|
||||
numberInputMin: 0,
|
||||
numberInputMax: 1,
|
||||
fineStep: 0.01,
|
||||
coarseStep: 0.05,
|
||||
},
|
||||
canvasCoherenceStrength: {
|
||||
initial: 0.3,
|
||||
sliderMin: 0,
|
||||
sliderMax: 1,
|
||||
numberInputMin: 0,
|
||||
numberInputMax: 1,
|
||||
fineStep: 0.01,
|
||||
coarseStep: 0.05,
|
||||
},
|
||||
hrfStrength: {
|
||||
initial: 0.45,
|
||||
sliderMin: 0,
|
||||
sliderMax: 1,
|
||||
numberInputMin: 0,
|
||||
numberInputMax: 1,
|
||||
fineStep: 0.01,
|
||||
coarseStep: 0.05,
|
||||
},
|
||||
canvasCoherenceEdgeSize: {
|
||||
initial: 16,
|
||||
sliderMin: 0,
|
||||
sliderMax: 128,
|
||||
numberInputMin: 0,
|
||||
numberInputMax: 1024,
|
||||
fineStep: 8,
|
||||
coarseStep: 16,
|
||||
},
|
||||
cfgRescaleMultiplier: {
|
||||
initial: 0,
|
||||
sliderMin: 0,
|
||||
sliderMax: 0.99,
|
||||
numberInputMin: 0,
|
||||
numberInputMax: 0.99,
|
||||
fineStep: 0.05,
|
||||
coarseStep: 0.1,
|
||||
},
|
||||
clipSkip: {
|
||||
initial: 0,
|
||||
sliderMin: 0,
|
||||
sliderMax: 12, // determined by model selection, unused in practice
|
||||
numberInputMin: 0,
|
||||
numberInputMax: 12, // determined by model selection, unused in practice
|
||||
fineStep: 1,
|
||||
coarseStep: 1,
|
||||
},
|
||||
infillPatchmatchDownscaleSize: {
|
||||
initial: 1,
|
||||
sliderMin: 1,
|
||||
sliderMax: 10,
|
||||
numberInputMin: 1,
|
||||
numberInputMax: 10,
|
||||
fineStep: 1,
|
||||
coarseStep: 1,
|
||||
},
|
||||
infillTileSize: {
|
||||
initial: 32,
|
||||
sliderMin: 16,
|
||||
sliderMax: 64,
|
||||
numberInputMin: 16,
|
||||
numberInputMax: 256,
|
||||
fineStep: 1,
|
||||
coarseStep: 1,
|
||||
},
|
||||
maskBlur: {
|
||||
initial: 16,
|
||||
sliderMin: 0,
|
||||
sliderMax: 128,
|
||||
numberInputMin: 0,
|
||||
numberInputMax: 512,
|
||||
fineStep: 1,
|
||||
coarseStep: 1,
|
||||
},
|
||||
ca: {
|
||||
weight: {
|
||||
initial: 1,
|
||||
sliderMin: 0,
|
||||
sliderMax: 2,
|
||||
numberInputMin: -1,
|
||||
numberInputMax: 2,
|
||||
fineStep: 0.01,
|
||||
coarseStep: 0.05,
|
||||
},
|
||||
},
|
||||
dynamicPrompts: {
|
||||
maxPrompts: {
|
||||
initial: 100,
|
||||
sliderMin: 1,
|
||||
sliderMax: 1000,
|
||||
numberInputMin: 1,
|
||||
numberInputMax: 10000,
|
||||
fineStep: 1,
|
||||
coarseStep: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
flux: {
|
||||
guidance: {
|
||||
initial: 4,
|
||||
sliderMin: 2,
|
||||
sliderMax: 6,
|
||||
numberInputMin: 1,
|
||||
numberInputMax: 20,
|
||||
fineStep: 0.1,
|
||||
coarseStep: 0.5,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const slice = createSlice({
|
||||
@@ -203,6 +32,7 @@ export const { configChanged } = slice.actions;
|
||||
|
||||
export const configSliceConfig: SliceConfig<typeof slice> = {
|
||||
slice,
|
||||
zSchema: zConfigState,
|
||||
getInitialState,
|
||||
};
|
||||
|
||||
|
||||
@@ -2,15 +2,12 @@ import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import type { SliceConfig } from 'app/store/types';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
|
||||
import { INITIAL_STATE, type UIState } from './uiTypes';
|
||||
|
||||
const getInitialState = (): UIState => deepClone(INITIAL_STATE);
|
||||
import { getInitialUIState, type UIState, zUIState } from './uiTypes';
|
||||
|
||||
const slice = createSlice({
|
||||
name: 'ui',
|
||||
initialState: getInitialState(),
|
||||
initialState: getInitialUIState(),
|
||||
reducers: {
|
||||
setActiveTab: (state, action: PayloadAction<UIState['activeTab']>) => {
|
||||
state.activeTab = action.payload;
|
||||
@@ -88,27 +85,25 @@ export const {
|
||||
|
||||
export const selectUiSlice = (state: RootState) => state.ui;
|
||||
|
||||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||
const migrate = (state: any): any => {
|
||||
if (!('_version' in state)) {
|
||||
state._version = 1;
|
||||
}
|
||||
if (state._version === 1) {
|
||||
state.activeTab = 'generation';
|
||||
state._version = 2;
|
||||
}
|
||||
if (state._version === 2) {
|
||||
state.activeTab = 'canvas';
|
||||
state._version = 3;
|
||||
}
|
||||
return state;
|
||||
};
|
||||
|
||||
export const uiSliceConfig: SliceConfig<typeof slice> = {
|
||||
slice,
|
||||
getInitialState,
|
||||
zSchema: zUIState,
|
||||
getInitialState: getInitialUIState,
|
||||
persistConfig: {
|
||||
migrate,
|
||||
migrate: (state) => {
|
||||
if (!('_version' in state)) {
|
||||
state._version = 1;
|
||||
}
|
||||
if (state._version === 1) {
|
||||
state.activeTab = 'generation';
|
||||
state._version = 2;
|
||||
}
|
||||
if (state._version === 2) {
|
||||
state.activeTab = 'canvas';
|
||||
state._version = 3;
|
||||
}
|
||||
return zUIState.parse(state);
|
||||
},
|
||||
persistDenylist: ['shouldShowImageDetails'],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { isPlainObject } from 'es-toolkit';
|
||||
import { z } from 'zod';
|
||||
|
||||
const zTabName = z.enum(['generate', 'canvas', 'upscaling', 'workflows', 'models', 'queue']);
|
||||
export const zTabName = z.enum(['generate', 'canvas', 'upscaling', 'workflows', 'models', 'queue']);
|
||||
export type TabName = z.infer<typeof zTabName>;
|
||||
|
||||
const zPartialDimensions = z.object({
|
||||
@@ -12,17 +12,28 @@ const zPartialDimensions = z.object({
|
||||
const zSerializable = z.any().refine(isPlainObject);
|
||||
export type Serializable = z.infer<typeof zSerializable>;
|
||||
|
||||
const zUIState = z.object({
|
||||
_version: z.literal(3).default(3),
|
||||
activeTab: zTabName.default('generate'),
|
||||
shouldShowImageDetails: z.boolean().default(false),
|
||||
shouldShowProgressInViewer: z.boolean().default(true),
|
||||
accordions: z.record(z.string(), z.boolean()).default(() => ({})),
|
||||
expanders: z.record(z.string(), z.boolean()).default(() => ({})),
|
||||
textAreaSizes: z.record(z.string(), zPartialDimensions).default({}),
|
||||
panels: z.record(z.string(), zSerializable).default({}),
|
||||
shouldShowNotificationV2: z.boolean().default(true),
|
||||
pickerCompactViewStates: z.record(z.string(), z.boolean()).default(() => ({})),
|
||||
export const zUIState = z.object({
|
||||
_version: z.literal(3),
|
||||
activeTab: zTabName,
|
||||
shouldShowImageDetails: z.boolean(),
|
||||
shouldShowProgressInViewer: z.boolean(),
|
||||
accordions: z.record(z.string(), z.boolean()),
|
||||
expanders: z.record(z.string(), z.boolean()),
|
||||
textAreaSizes: z.record(z.string(), zPartialDimensions),
|
||||
panels: z.record(z.string(), zSerializable),
|
||||
shouldShowNotificationV2: z.boolean(),
|
||||
pickerCompactViewStates: z.record(z.string(), z.boolean()),
|
||||
});
|
||||
export const INITIAL_STATE = zUIState.parse({});
|
||||
export type UIState = z.infer<typeof zUIState>;
|
||||
export const getInitialUIState = (): UIState => ({
|
||||
_version: 3 as const,
|
||||
activeTab: 'generate' as const,
|
||||
shouldShowImageDetails: false,
|
||||
shouldShowProgressInViewer: true,
|
||||
accordions: {},
|
||||
expanders: {},
|
||||
textAreaSizes: {},
|
||||
panels: {},
|
||||
shouldShowNotificationV2: true,
|
||||
pickerCompactViewStates: {},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user