import { createSlice } from '@reduxjs/toolkit'; import type { PayloadAction } from '@reduxjs/toolkit'; import * as InvokeAI from 'app/invokeai'; import promptToString from 'common/util/promptToString'; import { seedWeightsToString } from 'common/util/seedWeightPairs'; import { FACETOOL_TYPES } from 'app/constants'; import { InvokeTabName, tabMap } from 'features/tabs/tabMap'; export type UpscalingLevel = 2 | 4; export type FacetoolType = typeof FACETOOL_TYPES[number]; export interface OptionsState { activeTab: number; cfgScale: number; codeformerFidelity: number; currentTheme: string; facetoolStrength: number; facetoolType: FacetoolType; height: number; hiresFix: boolean; img2imgStrength: number; infillMethod: string; initialImage?: InvokeAI.Image | string; // can be an Image or url isLightBoxOpen: boolean; iterations: number; maskPath: string; optionsPanelScrollPosition: number; perlin: number; prompt: string; sampler: string; seamBlur: number; seamless: boolean; seamSize: number; seamSteps: number; seamStrength: number; seed: number; seedWeights: string; shouldFitToWidthHeight: boolean; shouldGenerateVariations: boolean; shouldHoldOptionsPanelOpen: boolean; shouldLoopback: boolean; shouldPinOptionsPanel: boolean; shouldRandomizeSeed: boolean; shouldRunESRGAN: boolean; shouldRunFacetool: boolean; shouldShowImageDetails: boolean; shouldShowOptionsPanel: boolean; showAdvancedOptions: boolean; showDualDisplay: boolean; steps: number; threshold: number; tileSize: number; upscalingLevel: UpscalingLevel; upscalingStrength: number; variationAmount: number; width: number; shouldUseCanvasBetaLayout: boolean; } const initialOptionsState: OptionsState = { activeTab: 0, cfgScale: 7.5, codeformerFidelity: 0.75, currentTheme: 'dark', facetoolStrength: 0.8, facetoolType: 'gfpgan', height: 512, hiresFix: false, img2imgStrength: 0.75, infillMethod: 'patchmatch', isLightBoxOpen: false, iterations: 1, maskPath: '', optionsPanelScrollPosition: 0, perlin: 0, prompt: '', sampler: 'k_lms', seamBlur: 16, seamless: false, seamSize: 96, seamSteps: 10, seamStrength: 0.7, seed: 0, seedWeights: '', shouldFitToWidthHeight: true, shouldGenerateVariations: false, shouldHoldOptionsPanelOpen: false, shouldLoopback: false, shouldPinOptionsPanel: true, shouldRandomizeSeed: true, shouldRunESRGAN: false, shouldRunFacetool: false, shouldShowImageDetails: false, shouldShowOptionsPanel: true, showAdvancedOptions: true, showDualDisplay: true, steps: 50, threshold: 0, tileSize: 32, upscalingLevel: 4, upscalingStrength: 0.75, variationAmount: 0.1, width: 512, shouldUseCanvasBetaLayout: false, }; const initialState: OptionsState = initialOptionsState; export const optionsSlice = createSlice({ name: 'options', initialState, reducers: { setPrompt: (state, action: PayloadAction) => { const newPrompt = action.payload; if (typeof newPrompt === 'string') { state.prompt = newPrompt; } else { state.prompt = promptToString(newPrompt); } }, setIterations: (state, action: PayloadAction) => { state.iterations = action.payload; }, setSteps: (state, action: PayloadAction) => { state.steps = action.payload; }, setCfgScale: (state, action: PayloadAction) => { state.cfgScale = action.payload; }, setThreshold: (state, action: PayloadAction) => { state.threshold = action.payload; }, setPerlin: (state, action: PayloadAction) => { state.perlin = action.payload; }, setHeight: (state, action: PayloadAction) => { state.height = action.payload; }, setWidth: (state, action: PayloadAction) => { state.width = action.payload; }, setSampler: (state, action: PayloadAction) => { state.sampler = action.payload; }, setSeed: (state, action: PayloadAction) => { state.seed = action.payload; state.shouldRandomizeSeed = false; }, setImg2imgStrength: (state, action: PayloadAction) => { state.img2imgStrength = action.payload; }, setFacetoolStrength: (state, action: PayloadAction) => { state.facetoolStrength = action.payload; }, setCodeformerFidelity: (state, action: PayloadAction) => { state.codeformerFidelity = action.payload; }, setUpscalingLevel: (state, action: PayloadAction) => { state.upscalingLevel = action.payload; }, setUpscalingStrength: (state, action: PayloadAction) => { state.upscalingStrength = action.payload; }, setMaskPath: (state, action: PayloadAction) => { state.maskPath = action.payload; }, setSeamless: (state, action: PayloadAction) => { state.seamless = action.payload; }, setHiresFix: (state, action: PayloadAction) => { state.hiresFix = action.payload; }, setShouldFitToWidthHeight: (state, action: PayloadAction) => { state.shouldFitToWidthHeight = action.payload; }, resetSeed: (state) => { state.seed = -1; }, setParameter: ( state, action: PayloadAction<{ key: string; value: string | number | boolean }> ) => { // TODO: This probably needs to be refactored. const { key, value } = action.payload; const temp = { ...state, [key]: value }; if (key === 'seed') { temp.shouldRandomizeSeed = false; } return temp; }, setShouldGenerateVariations: (state, action: PayloadAction) => { state.shouldGenerateVariations = action.payload; }, setVariationAmount: (state, action: PayloadAction) => { state.variationAmount = action.payload; }, setSeedWeights: (state, action: PayloadAction) => { state.seedWeights = action.payload; state.shouldGenerateVariations = true; state.variationAmount = 0; }, setAllTextToImageParameters: ( state, action: PayloadAction ) => { const { sampler, prompt, seed, variations, steps, cfg_scale, threshold, perlin, seamless, hires_fix, width, height, } = action.payload.image; if (variations && variations.length > 0) { state.seedWeights = seedWeightsToString(variations); state.shouldGenerateVariations = true; state.variationAmount = 0; } else { state.shouldGenerateVariations = false; } if (seed) { state.seed = seed; state.shouldRandomizeSeed = false; } if (prompt) state.prompt = promptToString(prompt); if (sampler) state.sampler = sampler; if (steps) state.steps = steps; if (cfg_scale) state.cfgScale = cfg_scale; if (threshold) state.threshold = threshold; if (typeof threshold === 'undefined') state.threshold = 0; if (perlin) state.perlin = perlin; if (typeof perlin === 'undefined') state.perlin = 0; if (typeof seamless === 'boolean') state.seamless = seamless; if (typeof hires_fix === 'boolean') state.hiresFix = hires_fix; if (width) state.width = width; if (height) state.height = height; }, setAllImageToImageParameters: ( state, action: PayloadAction ) => { const { type, strength, fit, init_image_path, mask_image_path } = action.payload.image; if (type === 'img2img') { if (init_image_path) state.initialImage = init_image_path; if (mask_image_path) state.maskPath = mask_image_path; if (strength) state.img2imgStrength = strength; if (typeof fit === 'boolean') state.shouldFitToWidthHeight = fit; } }, setAllParameters: (state, action: PayloadAction) => { const { type, sampler, prompt, seed, variations, steps, cfg_scale, threshold, perlin, seamless, hires_fix, width, height, strength, fit, init_image_path, mask_image_path, } = action.payload.image; if (type === 'img2img') { if (init_image_path) state.initialImage = init_image_path; if (mask_image_path) state.maskPath = mask_image_path; if (strength) state.img2imgStrength = strength; if (typeof fit === 'boolean') state.shouldFitToWidthHeight = fit; } if (variations && variations.length > 0) { state.seedWeights = seedWeightsToString(variations); state.shouldGenerateVariations = true; state.variationAmount = 0; } else { state.shouldGenerateVariations = false; } if (seed) { state.seed = seed; state.shouldRandomizeSeed = false; } if (prompt) state.prompt = promptToString(prompt); if (sampler) state.sampler = sampler; if (steps) state.steps = steps; if (cfg_scale) state.cfgScale = cfg_scale; if (threshold) state.threshold = threshold; if (typeof threshold === 'undefined') state.threshold = 0; if (perlin) state.perlin = perlin; if (typeof perlin === 'undefined') state.perlin = 0; if (typeof seamless === 'boolean') state.seamless = seamless; if (typeof hires_fix === 'boolean') state.hiresFix = hires_fix; if (width) state.width = width; if (height) state.height = height; state.shouldRunESRGAN = false; state.shouldRunFacetool = false; }, resetOptionsState: (state) => { return { ...state, ...initialOptionsState, }; }, setShouldRunFacetool: (state, action: PayloadAction) => { state.shouldRunFacetool = action.payload; }, setFacetoolType: (state, action: PayloadAction) => { state.facetoolType = action.payload; }, setShouldRunESRGAN: (state, action: PayloadAction) => { state.shouldRunESRGAN = action.payload; }, setShouldRandomizeSeed: (state, action: PayloadAction) => { state.shouldRandomizeSeed = action.payload; }, setShowAdvancedOptions: (state, action: PayloadAction) => { state.showAdvancedOptions = action.payload; }, setActiveTab: (state, action: PayloadAction) => { if (typeof action.payload === 'number') { state.activeTab = action.payload; } else { state.activeTab = tabMap.indexOf(action.payload); } }, setShouldShowImageDetails: (state, action: PayloadAction) => { state.shouldShowImageDetails = action.payload; }, setShowDualDisplay: (state, action: PayloadAction) => { state.showDualDisplay = action.payload; }, setInitialImage: ( state, action: PayloadAction ) => { state.initialImage = action.payload; }, clearInitialImage: (state) => { state.initialImage = undefined; }, setShouldPinOptionsPanel: (state, action: PayloadAction) => { state.shouldPinOptionsPanel = action.payload; }, setShouldShowOptionsPanel: (state, action: PayloadAction) => { state.shouldShowOptionsPanel = action.payload; }, setOptionsPanelScrollPosition: (state, action: PayloadAction) => { state.optionsPanelScrollPosition = action.payload; }, setShouldHoldOptionsPanelOpen: (state, action: PayloadAction) => { state.shouldHoldOptionsPanelOpen = action.payload; }, setShouldLoopback: (state, action: PayloadAction) => { state.shouldLoopback = action.payload; }, setCurrentTheme: (state, action: PayloadAction) => { state.currentTheme = action.payload; }, setIsLightBoxOpen: (state, action: PayloadAction) => { state.isLightBoxOpen = action.payload; }, setSeamSize: (state, action: PayloadAction) => { state.seamSize = action.payload; }, setSeamBlur: (state, action: PayloadAction) => { state.seamBlur = action.payload; }, setSeamStrength: (state, action: PayloadAction) => { state.seamStrength = action.payload; }, setSeamSteps: (state, action: PayloadAction) => { state.seamSteps = action.payload; }, setTileSize: (state, action: PayloadAction) => { state.tileSize = action.payload; }, setInfillMethod: (state, action: PayloadAction) => { state.infillMethod = action.payload; }, setShouldUseCanvasBetaLayout: (state, action: PayloadAction) => { state.shouldUseCanvasBetaLayout = action.payload; }, }, }); export const { clearInitialImage, resetOptionsState, resetSeed, setActiveTab, setAllImageToImageParameters, setAllParameters, setAllTextToImageParameters, setCfgScale, setCodeformerFidelity, setCurrentTheme, setFacetoolStrength, setFacetoolType, setHeight, setHiresFix, setImg2imgStrength, setInfillMethod, setInitialImage, setIsLightBoxOpen, setIterations, setMaskPath, setOptionsPanelScrollPosition, setParameter, setPerlin, setPrompt, setSampler, setSeamBlur, setSeamless, setSeamSize, setSeamSteps, setSeamStrength, setSeed, setSeedWeights, setShouldFitToWidthHeight, setShouldGenerateVariations, setShouldHoldOptionsPanelOpen, setShouldLoopback, setShouldPinOptionsPanel, setShouldRandomizeSeed, setShouldRunESRGAN, setShouldRunFacetool, setShouldShowImageDetails, setShouldShowOptionsPanel, setShowAdvancedOptions, setShowDualDisplay, setSteps, setThreshold, setTileSize, setUpscalingLevel, setUpscalingStrength, setVariationAmount, setWidth, setShouldUseCanvasBetaLayout, } = optionsSlice.actions; export default optionsSlice.reducer;