Reorganises internal state

`options` slice was huge and managed a mix of generation parameters and general app settings. It has been split up:

- Generation parameters are now in `generationSlice`.
- Postprocessing parameters are now in `postprocessingSlice`
- UI related things are now in `uiSlice`

There is probably more to be done, like `gallerySlice` perhaps should only manage internal gallery state, and not if the gallery is displayed.

Full-slice selectors have been made for each slice.

Other organisational tweaks.
This commit is contained in:
psychedelicious 2023-02-04 11:32:22 +11:00 committed by mauwii
parent 511df2963b
commit f80a64a0f4
No known key found for this signature in database
GPG Key ID: D923DB04ADB3F5AB
179 changed files with 7463 additions and 1165 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -7,8 +7,13 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>InvokeAI - A Stable Diffusion Toolkit</title> <title>InvokeAI - A Stable Diffusion Toolkit</title>
<link rel="shortcut icon" type="icon" href="./assets/favicon.0d253ced.ico" /> <link rel="shortcut icon" type="icon" href="./assets/favicon.0d253ced.ico" />
<<<<<<< HEAD
<script type="module" crossorigin src="./assets/index.f3fa9388.js"></script> <script type="module" crossorigin src="./assets/index.f3fa9388.js"></script>
<link rel="stylesheet" href="./assets/index.1536494e.css"> <link rel="stylesheet" href="./assets/index.1536494e.css">
=======
<script type="module" crossorigin src="./assets/index.5c5bc3f1.js"></script>
<link rel="stylesheet" href="./assets/index.8badc8b4.css">
>>>>>>> 64c0b743 (Reorganises internal state)
<script type="module">try{import.meta.url;import("_").catch(()=>1);}catch(e){}window.__vite_is_modern_browser=true;</script> <script type="module">try{import.meta.url;import("_").catch(()=>1);}catch(e){}window.__vite_is_modern_browser=true;</script>
<script type="module">!function(){if(window.__vite_is_modern_browser)return;console.warn("vite: loading legacy build because dynamic import or import.meta.url is unsupported, syntax error above should be ignored");var e=document.getElementById("vite-legacy-polyfill"),n=document.createElement("script");n.src=e.src,n.onload=function(){System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))},document.body.appendChild(n)}();</script> <script type="module">!function(){if(window.__vite_is_modern_browser)return;console.warn("vite: loading legacy build because dynamic import or import.meta.url is unsupported, syntax error above should be ignored");var e=document.getElementById("vite-legacy-polyfill"),n=document.createElement("script");n.src=e.src,n.onload=function(){System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))},document.body.appendChild(n)}();</script>
</head> </head>
@ -18,6 +23,10 @@
<script nomodule>!function(){var e=document,t=e.createElement("script");if(!("noModule"in t)&&"onbeforeload"in t){var n=!1;e.addEventListener("beforeload",(function(e){if(e.target===t)n=!0;else if(!e.target.hasAttribute("nomodule")||!n)return;e.preventDefault()}),!0),t.type="module",t.src=".",e.head.appendChild(t),t.remove()}}();</script> <script nomodule>!function(){var e=document,t=e.createElement("script");if(!("noModule"in t)&&"onbeforeload"in t){var n=!1;e.addEventListener("beforeload",(function(e){if(e.target===t)n=!0;else if(!e.target.hasAttribute("nomodule")||!n)return;e.preventDefault()}),!0),t.type="module",t.src=".",e.head.appendChild(t),t.remove()}}();</script>
<script nomodule crossorigin id="vite-legacy-polyfill" src="./assets/polyfills-legacy-dde3a68a.js"></script> <script nomodule crossorigin id="vite-legacy-polyfill" src="./assets/polyfills-legacy-dde3a68a.js"></script>
<<<<<<< HEAD
<script nomodule crossorigin id="vite-legacy-entry" data-src="./assets/index-legacy-4add591a.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script> <script nomodule crossorigin id="vite-legacy-entry" data-src="./assets/index-legacy-4add591a.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
=======
<script nomodule crossorigin id="vite-legacy-entry" data-src="./assets/index-legacy-35973932.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
>>>>>>> 64c0b743 (Reorganises internal state)
</body> </body>
</html> </html>

View File

@ -48,8 +48,8 @@
"sendTo": "Send to", "sendTo": "Send to",
"sendToImg2Img": "Send to Image to Image", "sendToImg2Img": "Send to Image to Image",
"sendToUnifiedCanvas": "Send To Unified Canvas", "sendToUnifiedCanvas": "Send To Unified Canvas",
"copyImage": "Copy Image",
"copyImageToLink": "Copy Image To Link", "copyImageToLink": "Copy Image To Link",
"copyImage": "Copy Image",
"downloadImage": "Download Image", "downloadImage": "Download Image",
"openInViewer": "Open In Viewer", "openInViewer": "Open In Viewer",
"closeViewer": "Close Viewer", "closeViewer": "Close Viewer",

View File

@ -2,13 +2,13 @@ import ProgressBar from 'features/system/components/ProgressBar';
import SiteHeader from 'features/system/components/SiteHeader'; import SiteHeader from 'features/system/components/SiteHeader';
import Console from 'features/system/components/Console'; import Console from 'features/system/components/Console';
import { keepGUIAlive } from './utils'; import { keepGUIAlive } from './utils';
import InvokeTabs from 'features/tabs/components/InvokeTabs'; import InvokeTabs from 'features/ui/components/InvokeTabs';
import ImageUploader from 'common/components/ImageUploader'; import ImageUploader from 'common/components/ImageUploader';
import useToastWatcher from 'features/system/hooks/useToastWatcher'; import useToastWatcher from 'features/system/hooks/useToastWatcher';
import FloatingOptionsPanelButtons from 'features/tabs/components/FloatingOptionsPanelButtons'; import FloatingParametersPanelButtons from 'features/ui/components/FloatingParametersPanelButtons';
import FloatingGalleryButton from 'features/tabs/components/FloatingGalleryButton'; import FloatingGalleryButton from 'features/ui/components/FloatingGalleryButton';
keepGUIAlive(); keepGUIAlive();
@ -27,7 +27,7 @@ const App = () => {
<Console /> <Console />
</div> </div>
</ImageUploader> </ImageUploader>
<FloatingOptionsPanelButtons /> <FloatingParametersPanelButtons />
<FloatingGalleryButton /> <FloatingGalleryButton />
</div> </div>
); );

View File

@ -12,7 +12,7 @@
* 'gfpgan'. * 'gfpgan'.
*/ */
import { InvokeTabName } from 'features/tabs/tabMap'; import { InvokeTabName } from 'features/ui/store/tabMap';
import { IRect } from 'konva/lib/types'; import { IRect } from 'konva/lib/types';
/** /**

View File

@ -1,32 +1,26 @@
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import _ from 'lodash'; import _ from 'lodash';
import { RootState } from 'app/store'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { activeTabNameSelector } from 'features/options/store/optionsSelectors'; import { systemSelector } from 'features/system/store/systemSelectors';
import { OptionsState } from 'features/options/store/optionsSlice';
import { SystemState } from 'features/system/store/systemSlice';
import { validateSeedWeights } from 'common/util/seedWeightPairs'; import { validateSeedWeights } from 'common/util/seedWeightPairs';
import { initialCanvasImageSelector } from 'features/canvas/store/canvasSelectors'; import { initialCanvasImageSelector } from 'features/canvas/store/canvasSelectors';
import { generationSelector } from 'features/parameters/store/generationSelectors';
export const readinessSelector = createSelector( export const readinessSelector = createSelector(
[ [
(state: RootState) => state.options, generationSelector,
(state: RootState) => state.system, systemSelector,
initialCanvasImageSelector, initialCanvasImageSelector,
activeTabNameSelector, activeTabNameSelector,
], ],
( (generation, system, initialCanvasImage, activeTabName) => {
options: OptionsState,
system: SystemState,
initialCanvasImage,
activeTabName
) => {
const { const {
prompt, prompt,
shouldGenerateVariations, shouldGenerateVariations,
seedWeights, seedWeights,
initialImage, initialImage,
seed, seed,
} = options; } = generation;
const { isProcessing, isConnected } = system; const { isProcessing, isConnected } = system;

View File

@ -1,6 +1,6 @@
import { createAction } from '@reduxjs/toolkit'; import { createAction } from '@reduxjs/toolkit';
import { GalleryCategory } from 'features/gallery/store/gallerySlice'; import { GalleryCategory } from 'features/gallery/store/gallerySlice';
import { InvokeTabName } from 'features/tabs/tabMap'; import { InvokeTabName } from 'features/ui/store/tabMap';
import * as InvokeAI from 'app/invokeai'; import * as InvokeAI from 'app/invokeai';
/** /**

View File

@ -10,14 +10,13 @@ import {
GalleryState, GalleryState,
removeImage, removeImage,
} from 'features/gallery/store/gallerySlice'; } from 'features/gallery/store/gallerySlice';
import { OptionsState } from 'features/options/store/optionsSlice';
import { import {
addLogEntry, addLogEntry,
generationRequested, generationRequested,
modelChangeRequested, modelChangeRequested,
setIsProcessing, setIsProcessing,
} from 'features/system/store/systemSlice'; } from 'features/system/store/systemSlice';
import { InvokeTabName } from 'features/tabs/tabMap'; import { InvokeTabName } from 'features/ui/store/tabMap';
import * as InvokeAI from 'app/invokeai'; import * as InvokeAI from 'app/invokeai';
import type { RootState } from 'app/store'; import type { RootState } from 'app/store';
@ -39,7 +38,8 @@ const makeSocketIOEmitters = (
const state: RootState = getState(); const state: RootState = getState();
const { const {
options: optionsState, generation: generationState,
postprocessing: postprocessingState,
system: systemState, system: systemState,
canvas: canvasState, canvas: canvasState,
} = state; } = state;
@ -47,7 +47,8 @@ const makeSocketIOEmitters = (
const frontendToBackendParametersConfig: FrontendToBackendParametersConfig = const frontendToBackendParametersConfig: FrontendToBackendParametersConfig =
{ {
generationMode, generationMode,
optionsState, generationState,
postprocessingState,
canvasState, canvasState,
systemState, systemState,
}; };
@ -90,8 +91,11 @@ const makeSocketIOEmitters = (
}, },
emitRunESRGAN: (imageToProcess: InvokeAI.Image) => { emitRunESRGAN: (imageToProcess: InvokeAI.Image) => {
dispatch(setIsProcessing(true)); dispatch(setIsProcessing(true));
const options: OptionsState = getState().options;
const { upscalingLevel, upscalingStrength } = options; const {
postprocessing: { upscalingLevel, upscalingStrength },
} = getState();
const esrganParameters = { const esrganParameters = {
upscale: [upscalingLevel, upscalingStrength], upscale: [upscalingLevel, upscalingStrength],
}; };
@ -111,8 +115,10 @@ const makeSocketIOEmitters = (
}, },
emitRunFacetool: (imageToProcess: InvokeAI.Image) => { emitRunFacetool: (imageToProcess: InvokeAI.Image) => {
dispatch(setIsProcessing(true)); dispatch(setIsProcessing(true));
const options: OptionsState = getState().options;
const { facetoolType, facetoolStrength, codeformerFidelity } = options; const {
postprocessing: { facetoolType, facetoolStrength, codeformerFidelity },
} = getState();
const facetoolParameters: Record<string, unknown> = { const facetoolParameters: Record<string, unknown> = {
facetool_strength: facetoolStrength, facetool_strength: facetoolStrength,

View File

@ -35,14 +35,14 @@ import {
setInfillMethod, setInfillMethod,
setInitialImage, setInitialImage,
setMaskPath, setMaskPath,
} from 'features/options/store/optionsSlice'; } from 'features/parameters/store/generationSlice';
import { import {
requestImages, requestImages,
requestNewImages, requestNewImages,
requestSystemConfig, requestSystemConfig,
} from './actions'; } from './actions';
import { addImageToStagingArea } from 'features/canvas/store/canvasSlice'; import { addImageToStagingArea } from 'features/canvas/store/canvasSlice';
import { tabMap } from 'features/tabs/tabMap'; import { tabMap } from 'features/ui/store/tabMap';
import type { RootState } from 'app/store'; import type { RootState } from 'app/store';
/** /**
@ -104,8 +104,9 @@ const makeSocketIOListeners = (
*/ */
onGenerationResult: (data: InvokeAI.ImageResultResponse) => { onGenerationResult: (data: InvokeAI.ImageResultResponse) => {
try { try {
const state: RootState = getState(); const state = getState();
const { shouldLoopback, activeTab } = state.options; const { activeTab } = state.ui;
const { shouldLoopback } = state.postprocessing;
const { boundingBox: _, generationMode, ...rest } = data; const { boundingBox: _, generationMode, ...rest } = data;
const newImage = { const newImage = {
@ -327,7 +328,9 @@ const makeSocketIOListeners = (
dispatch(removeImage(data)); dispatch(removeImage(data));
// remove references to image in options // remove references to image in options
const { initialImage, maskPath } = getState().options; const {
generation: { initialImage, maskPath },
} = getState();
if ( if (
initialImage === url || initialImage === url ||

View File

@ -5,10 +5,13 @@ import storage from 'redux-persist/lib/storage'; // defaults to localStorage for
import { getPersistConfig } from 'redux-deep-persist'; import { getPersistConfig } from 'redux-deep-persist';
import optionsReducer from 'features/options/store/optionsSlice'; import generationReducer from 'features/parameters/store/generationSlice';
import postprocessingReducer from 'features/parameters/store/postprocessingSlice';
import galleryReducer from 'features/gallery/store/gallerySlice'; import galleryReducer from 'features/gallery/store/gallerySlice';
import systemReducer from 'features/system/store/systemSlice'; import systemReducer from 'features/system/store/systemSlice';
import canvasReducer from 'features/canvas/store/canvasSlice'; import canvasReducer from 'features/canvas/store/canvasSlice';
import uiReducer from 'features/ui/store/uiSlice';
import lightboxReducer from 'features/lightbox/store/lightboxSlice';
import { socketioMiddleware } from './socketio/middleware'; import { socketioMiddleware } from './socketio/middleware';
@ -58,10 +61,13 @@ const galleryBlacklist = [
].map((blacklistItem) => `gallery.${blacklistItem}`); ].map((blacklistItem) => `gallery.${blacklistItem}`);
const rootReducer = combineReducers({ const rootReducer = combineReducers({
options: optionsReducer, generation: generationReducer,
postprocessing: postprocessingReducer,
gallery: galleryReducer, gallery: galleryReducer,
system: systemReducer, system: systemReducer,
canvas: canvasReducer, canvas: canvasReducer,
ui: uiReducer,
lightbox: lightboxReducer,
}); });
const rootPersistConfig = getPersistConfig({ const rootPersistConfig = getPersistConfig({
@ -89,8 +95,8 @@ export const store = configureStore({
'canvas/setStageCoordinates', 'canvas/setStageCoordinates',
'canvas/setStageScale', 'canvas/setStageScale',
'canvas/setIsDrawing', 'canvas/setIsDrawing',
// 'canvas/setBoundingBoxCoordinates', 'canvas/setBoundingBoxCoordinates',
// 'canvas/setBoundingBoxDimensions', 'canvas/setBoundingBoxDimensions',
'canvas/setIsDrawing', 'canvas/setIsDrawing',
'canvas/addPointToCurrentLine', 'canvas/addPointToCurrentLine',
], ],

View File

@ -7,23 +7,23 @@ import {
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { SystemState } from 'features/system/store/systemSlice'; import { SystemState } from 'features/system/store/systemSlice';
import { useAppSelector } from 'app/storeHooks'; import { useAppSelector } from 'app/storeHooks';
import { RootState } from 'app/store';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { ReactElement } from 'react'; import { ReactElement } from 'react';
import { Feature, useFeatureHelpInfo } from 'app/features'; import { Feature, useFeatureHelpInfo } from 'app/features';
import { systemSelector } from 'features/system/store/systemSelectors';
type GuideProps = { type GuideProps = {
children: ReactElement; children: ReactElement;
feature: Feature; feature: Feature;
}; };
const systemSelector = createSelector( const guidePopoverSelector = createSelector(
(state: RootState) => state.system, systemSelector,
(system: SystemState) => system.shouldDisplayGuides (system: SystemState) => system.shouldDisplayGuides
); );
const GuidePopover = ({ children, feature }: GuideProps) => { const GuidePopover = ({ children, feature }: GuideProps) => {
const shouldDisplayGuides = useAppSelector(systemSelector); const shouldDisplayGuides = useAppSelector(guidePopoverSelector);
const { text } = useFeatureHelpInfo(feature); const { text } = useFeatureHelpInfo(feature);
if (!shouldDisplayGuides) return null; if (!shouldDisplayGuides) return null;

View File

@ -9,8 +9,8 @@ import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import { FileRejection, useDropzone } from 'react-dropzone'; import { FileRejection, useDropzone } from 'react-dropzone';
import { useToast } from '@chakra-ui/react'; import { useToast } from '@chakra-ui/react';
import { ImageUploaderTriggerContext } from 'app/contexts/ImageUploaderTriggerContext'; import { ImageUploaderTriggerContext } from 'app/contexts/ImageUploaderTriggerContext';
import { activeTabNameSelector } from 'features/options/store/optionsSelectors'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { tabDict } from 'features/tabs/components/InvokeTabs'; import { tabDict } from 'features/ui/components/InvokeTabs';
import ImageUploadOverlay from './ImageUploadOverlay'; import ImageUploadOverlay from './ImageUploadOverlay';
import { uploadImage } from 'features/gallery/store/thunks/uploadImage'; import { uploadImage } from 'features/gallery/store/thunks/uploadImage';
import useImageUploader from 'common/hooks/useImageUploader'; import useImageUploader from 'common/hooks/useImageUploader';

View File

@ -1,12 +1,12 @@
import { NUMPY_RAND_MAX, NUMPY_RAND_MIN } from 'app/constants'; import { NUMPY_RAND_MAX, NUMPY_RAND_MIN } from 'app/constants';
import { OptionsState } from 'features/options/store/optionsSlice'; import { GenerationState } from 'features/parameters/store/generationSlice';
import { SystemState } from 'features/system/store/systemSlice'; import { SystemState } from 'features/system/store/systemSlice';
import { Vector2d } from 'konva/lib/types'; import { Vector2d } from 'konva/lib/types';
import { Dimensions } from 'features/canvas/store/canvasTypes'; import { Dimensions } from 'features/canvas/store/canvasTypes';
import { stringToSeedWeightsArray } from './seedWeightPairs'; import { stringToSeedWeightsArray } from './seedWeightPairs';
import randomInt from './randomInt'; import randomInt from './randomInt';
import { InvokeTabName } from 'features/tabs/tabMap'; import { InvokeTabName } from 'features/ui/store/tabMap';
import { import {
CanvasState, CanvasState,
isCanvasMaskLine, isCanvasMaskLine,
@ -17,11 +17,13 @@ import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider';
import type { import type {
UpscalingLevel, UpscalingLevel,
FacetoolType, FacetoolType,
} from 'features/options/store/optionsSlice'; } from 'features/parameters/store/postprocessingSlice';
import { PostprocessingState } from 'features/parameters/store/postprocessingSlice';
export type FrontendToBackendParametersConfig = { export type FrontendToBackendParametersConfig = {
generationMode: InvokeTabName; generationMode: InvokeTabName;
optionsState: OptionsState; generationState: GenerationState;
postprocessingState: PostprocessingState;
canvasState: CanvasState; canvasState: CanvasState;
systemState: SystemState; systemState: SystemState;
imageToProcessUrl?: string; imageToProcessUrl?: string;
@ -91,16 +93,31 @@ export const frontendToBackendParameters = (
): BackendParameters => { ): BackendParameters => {
const canvasBaseLayer = getCanvasBaseLayer(); const canvasBaseLayer = getCanvasBaseLayer();
const { generationMode, optionsState, canvasState, systemState } = config; const {
generationMode,
generationState,
postprocessingState,
canvasState,
systemState,
} = config;
const { const {
cfgScale,
codeformerFidelity, codeformerFidelity,
facetoolStrength, facetoolStrength,
facetoolType, facetoolType,
height,
hiresFix, hiresFix,
hiresStrength, hiresStrength,
shouldRunESRGAN,
shouldRunFacetool,
upscalingLevel,
upscalingStrength,
} = postprocessingState;
const {
cfgScale,
height,
img2imgStrength, img2imgStrength,
infillMethod, infillMethod,
initialImage, initialImage,
@ -119,16 +136,14 @@ export const frontendToBackendParameters = (
shouldFitToWidthHeight, shouldFitToWidthHeight,
shouldGenerateVariations, shouldGenerateVariations,
shouldRandomizeSeed, shouldRandomizeSeed,
shouldRunESRGAN,
shouldRunFacetool,
steps, steps,
threshold, threshold,
tileSize, tileSize,
upscalingLevel,
upscalingStrength,
variationAmount, variationAmount,
width, width,
} = optionsState; } = generationState;
const { const {
shouldDisplayInProgressType, shouldDisplayInProgressType,

View File

@ -1,7 +1,7 @@
import { Spinner } from '@chakra-ui/react'; import { Spinner } from '@chakra-ui/react';
import { useLayoutEffect, useRef } from 'react'; import { useLayoutEffect, useRef } from 'react';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import { activeTabNameSelector } from 'features/options/store/optionsSelectors'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { import {
resizeAndScaleCanvas, resizeAndScaleCanvas,
resizeCanvas, resizeCanvas,

View File

@ -3,7 +3,7 @@ import { useHotkeys } from 'react-hotkeys-hook';
import { FaRedo } from 'react-icons/fa'; import { FaRedo } from 'react-icons/fa';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAIIconButton from 'common/components/IAIIconButton'; import IAIIconButton from 'common/components/IAIIconButton';
import { activeTabNameSelector } from 'features/options/store/optionsSelectors'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { canvasSelector } from 'features/canvas/store/canvasSelectors'; import { canvasSelector } from 'features/canvas/store/canvasSelectors';
import _ from 'lodash'; import _ from 'lodash';

View File

@ -6,7 +6,7 @@ import IAIIconButton from 'common/components/IAIIconButton';
import { canvasSelector } from 'features/canvas/store/canvasSelectors'; import { canvasSelector } from 'features/canvas/store/canvasSelectors';
import _ from 'lodash'; import _ from 'lodash';
import { activeTabNameSelector } from 'features/options/store/optionsSelectors'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { undo } from 'features/canvas/store/canvasSlice'; import { undo } from 'features/canvas/store/canvasSlice';
import { systemSelector } from 'features/system/store/systemSelectors'; import { systemSelector } from 'features/system/store/systemSelectors';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';

View File

@ -1,7 +1,7 @@
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import _ from 'lodash'; import _ from 'lodash';
import { useHotkeys } from 'react-hotkeys-hook'; import { useHotkeys } from 'react-hotkeys-hook';
import { activeTabNameSelector } from 'features/options/store/optionsSelectors'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { import {
clearMask, clearMask,
resetCanvasInteractionState, resetCanvasInteractionState,

View File

@ -1,6 +1,6 @@
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import { activeTabNameSelector } from 'features/options/store/optionsSelectors'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import Konva from 'konva'; import Konva from 'konva';
import { KonvaEventObject } from 'konva/lib/Node'; import { KonvaEventObject } from 'konva/lib/Node';
import _ from 'lodash'; import _ from 'lodash';

View File

@ -1,6 +1,6 @@
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import { activeTabNameSelector } from 'features/options/store/optionsSelectors'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import Konva from 'konva'; import Konva from 'konva';
import { Vector2d } from 'konva/lib/types'; import { Vector2d } from 'konva/lib/types';
import _ from 'lodash'; import _ from 'lodash';

View File

@ -1,6 +1,6 @@
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import { activeTabNameSelector } from 'features/options/store/optionsSelectors'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import Konva from 'konva'; import Konva from 'konva';
import _ from 'lodash'; import _ from 'lodash';
import { MutableRefObject, useCallback } from 'react'; import { MutableRefObject, useCallback } from 'react';

View File

@ -1,6 +1,6 @@
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { activeTabNameSelector } from 'features/options/store/optionsSelectors'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { systemSelector } from 'features/system/store/systemSelectors'; import { systemSelector } from 'features/system/store/systemSelectors';
import { CanvasImage, CanvasState, isCanvasBaseImage } from './canvasTypes'; import { CanvasImage, CanvasState, isCanvasBaseImage } from './canvasTypes';

View File

@ -1,28 +1,42 @@
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
import { ButtonGroup, Link, useToast } from '@chakra-ui/react';
import { runESRGAN, runFacetool } from 'app/socketio/actions';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import { RootState } from 'app/store'; import IAIButton from 'common/components/IAIButton';
import IAIIconButton from 'common/components/IAIIconButton';
import IAIPopover from 'common/components/IAIPopover';
import { getPromptAndNegative } from 'common/util/getPromptAndNegative';
import {
setDoesCanvasNeedScaling,
setInitialCanvasImage,
} from 'features/canvas/store/canvasSlice';
import { GalleryState } from 'features/gallery/store/gallerySlice';
import { lightboxSelector } from 'features/lightbox/store/lightboxSelectors';
import { setIsLightboxOpen } from 'features/lightbox/store/lightboxSlice';
import FaceRestoreSettings from 'features/parameters/components/AdvancedParameters/FaceRestore/FaceRestoreSettings';
import UpscaleSettings from 'features/parameters/components/AdvancedParameters/Upscale/UpscaleSettings';
import { import {
OptionsState,
setActiveTab,
setAllParameters, setAllParameters,
setInitialImage, setInitialImage,
setIsLightBoxOpen,
setNegativePrompt, setNegativePrompt,
setPrompt, setPrompt,
setSeed, setSeed,
setShouldShowImageDetails, } from 'features/parameters/store/generationSlice';
} from 'features/options/store/optionsSlice'; import { postprocessingSelector } from 'features/parameters/store/postprocessingSelectors';
import DeleteImageModal from './DeleteImageModal'; import { systemSelector } from 'features/system/store/systemSelectors';
import { SystemState } from 'features/system/store/systemSlice'; import { SystemState } from 'features/system/store/systemSlice';
import IAIButton from 'common/components/IAIButton'; import {
import { runESRGAN, runFacetool } from 'app/socketio/actions'; activeTabNameSelector,
import IAIIconButton from 'common/components/IAIIconButton'; uiSelector,
import UpscaleOptions from 'features/options/components/AdvancedOptions/Upscale/UpscaleOptions'; } from 'features/ui/store/uiSelectors';
import FaceRestoreOptions from 'features/options/components/AdvancedOptions/FaceRestore/FaceRestoreOptions'; import {
setActiveTab,
setShouldShowImageDetails,
} from 'features/ui/store/uiSlice';
import { useHotkeys } from 'react-hotkeys-hook'; import { useHotkeys } from 'react-hotkeys-hook';
import { ButtonGroup, Link, useToast } from '@chakra-ui/react'; import { useTranslation } from 'react-i18next';
import { import {
FaAsterisk, FaAsterisk,
FaCode, FaCode,
@ -37,38 +51,34 @@ import {
FaShareAlt, FaShareAlt,
FaTrash, FaTrash,
} from 'react-icons/fa'; } from 'react-icons/fa';
import { import { gallerySelector } from '../store/gallerySelectors';
setDoesCanvasNeedScaling, import DeleteImageModal from './DeleteImageModal';
setInitialCanvasImage,
} from 'features/canvas/store/canvasSlice';
import { GalleryState } from 'features/gallery/store/gallerySlice';
import { activeTabNameSelector } from 'features/options/store/optionsSelectors';
import IAIPopover from 'common/components/IAIPopover';
import { useTranslation } from 'react-i18next';
import { getPromptAndNegative } from 'common/util/getPromptAndNegative';
const systemSelector = createSelector( const currentImageButtonsSelector = createSelector(
[ [
(state: RootState) => state.system, systemSelector,
(state: RootState) => state.options, gallerySelector,
(state: RootState) => state.gallery, postprocessingSelector,
uiSelector,
lightboxSelector,
activeTabNameSelector, activeTabNameSelector,
], ],
( (
system: SystemState, system: SystemState,
options: OptionsState,
gallery: GalleryState, gallery: GalleryState,
postprocessing,
ui,
lightbox,
activeTabName activeTabName
) => { ) => {
const { isProcessing, isConnected, isGFPGANAvailable, isESRGANAvailable } = const { isProcessing, isConnected, isGFPGANAvailable, isESRGANAvailable } =
system; system;
const { const { upscalingLevel, facetoolStrength } = postprocessing;
upscalingLevel,
facetoolStrength, const { isLightboxOpen } = lightbox;
shouldShowImageDetails,
isLightBoxOpen, const { shouldShowImageDetails } = ui;
} = options;
const { intermediateImage, currentImage } = gallery; const { intermediateImage, currentImage } = gallery;
@ -83,7 +93,7 @@ const systemSelector = createSelector(
currentImage, currentImage,
shouldShowImageDetails, shouldShowImageDetails,
activeTabName, activeTabName,
isLightBoxOpen, isLightboxOpen,
}; };
}, },
{ {
@ -109,36 +119,20 @@ const CurrentImageButtons = () => {
shouldDisableToolbarButtons, shouldDisableToolbarButtons,
shouldShowImageDetails, shouldShowImageDetails,
currentImage, currentImage,
isLightBoxOpen, isLightboxOpen,
activeTabName, activeTabName,
} = useAppSelector(systemSelector); } = useAppSelector(currentImageButtonsSelector);
const toast = useToast(); const toast = useToast();
const { t } = useTranslation(); const { t } = useTranslation();
const handleClickUseAsInitialImage = () => { const handleClickUseAsInitialImage = () => {
if (!currentImage) return; if (!currentImage) return;
if (isLightBoxOpen) dispatch(setIsLightBoxOpen(false)); if (isLightboxOpen) dispatch(setIsLightboxOpen(false));
dispatch(setInitialImage(currentImage)); dispatch(setInitialImage(currentImage));
dispatch(setActiveTab('img2img')); dispatch(setActiveTab('img2img'));
}; };
const handleCopyImage = async () => {
if (!currentImage) return;
const blob = await fetch(currentImage.url).then((res) => res.blob());
const data = [new ClipboardItem({ [blob.type]: blob })];
await navigator.clipboard.write(data);
toast({
title: t('toast:imageCopied'),
status: 'success',
duration: 2500,
isClosable: true,
});
};
const handleCopyImageLink = () => { const handleCopyImageLink = () => {
navigator.clipboard navigator.clipboard
.writeText( .writeText(
@ -353,7 +347,7 @@ const CurrentImageButtons = () => {
const handleSendToCanvas = () => { const handleSendToCanvas = () => {
if (!currentImage) return; if (!currentImage) return;
if (isLightBoxOpen) dispatch(setIsLightBoxOpen(false)); if (isLightboxOpen) dispatch(setIsLightboxOpen(false));
dispatch(setInitialCanvasImage(currentImage)); dispatch(setInitialCanvasImage(currentImage));
dispatch(setDoesCanvasNeedScaling(true)); dispatch(setDoesCanvasNeedScaling(true));
@ -388,7 +382,7 @@ const CurrentImageButtons = () => {
); );
const handleLightBox = () => { const handleLightBox = () => {
dispatch(setIsLightBoxOpen(!isLightBoxOpen)); dispatch(setIsLightboxOpen(!isLightboxOpen));
}; };
return ( return (
@ -398,7 +392,7 @@ const CurrentImageButtons = () => {
trigger="hover" trigger="hover"
triggerComponent={ triggerComponent={
<IAIIconButton <IAIIconButton
aria-label={`${t('options:sendTo')}...`} aria-label={`${t('parameters:sendTo')}...`}
icon={<FaShareAlt />} icon={<FaShareAlt />}
/> />
} }
@ -409,34 +403,26 @@ const CurrentImageButtons = () => {
onClick={handleClickUseAsInitialImage} onClick={handleClickUseAsInitialImage}
leftIcon={<FaShare />} leftIcon={<FaShare />}
> >
{t('options:sendToImg2Img')} {t('parameters:sendToImg2Img')}
</IAIButton> </IAIButton>
<IAIButton <IAIButton
size={'sm'} size={'sm'}
onClick={handleSendToCanvas} onClick={handleSendToCanvas}
leftIcon={<FaShare />} leftIcon={<FaShare />}
> >
{t('options:sendToUnifiedCanvas')} {t('parameters:sendToUnifiedCanvas')}
</IAIButton>
<IAIButton
size={'sm'}
onClick={handleCopyImage}
leftIcon={<FaCopy />}
>
{t('options:copyImage')}
</IAIButton> </IAIButton>
<IAIButton <IAIButton
size={'sm'} size={'sm'}
onClick={handleCopyImageLink} onClick={handleCopyImageLink}
leftIcon={<FaCopy />} leftIcon={<FaCopy />}
> >
{t('options:copyImageToLink')} {t('parameters:copyImageToLink')}
</IAIButton> </IAIButton>
<Link download={true} href={currentImage?.url}> <Link download={true} href={currentImage?.url}>
<IAIButton leftIcon={<FaDownload />} size={'sm'} w="100%"> <IAIButton leftIcon={<FaDownload />} size={'sm'} w="100%">
{t('options:downloadImage')} {t('parameters:downloadImage')}
</IAIButton> </IAIButton>
</Link> </Link>
</div> </div>
@ -444,16 +430,16 @@ const CurrentImageButtons = () => {
<IAIIconButton <IAIIconButton
icon={<FaExpand />} icon={<FaExpand />}
tooltip={ tooltip={
!isLightBoxOpen !isLightboxOpen
? `${t('options:openInViewer')} (Z)` ? `${t('parameters:openInViewer')} (Z)`
: `${t('options:closeViewer')} (Z)` : `${t('parameters:closeViewer')} (Z)`
} }
aria-label={ aria-label={
!isLightBoxOpen !isLightboxOpen
? `${t('options:openInViewer')} (Z)` ? `${t('parameters:openInViewer')} (Z)`
: `${t('options:closeViewer')} (Z)` : `${t('parameters:closeViewer')} (Z)`
} }
data-selected={isLightBoxOpen} data-selected={isLightboxOpen}
onClick={handleLightBox} onClick={handleLightBox}
/> />
</ButtonGroup> </ButtonGroup>
@ -461,24 +447,24 @@ const CurrentImageButtons = () => {
<ButtonGroup isAttached={true}> <ButtonGroup isAttached={true}>
<IAIIconButton <IAIIconButton
icon={<FaQuoteRight />} icon={<FaQuoteRight />}
tooltip={`${t('options:usePrompt')} (P)`} tooltip={`${t('parameters:usePrompt')} (P)`}
aria-label={`${t('options:usePrompt')} (P)`} aria-label={`${t('parameters:usePrompt')} (P)`}
isDisabled={!currentImage?.metadata?.image?.prompt} isDisabled={!currentImage?.metadata?.image?.prompt}
onClick={handleClickUsePrompt} onClick={handleClickUsePrompt}
/> />
<IAIIconButton <IAIIconButton
icon={<FaSeedling />} icon={<FaSeedling />}
tooltip={`${t('options:useSeed')} (S)`} tooltip={`${t('parameters:useSeed')} (S)`}
aria-label={`${t('options:useSeed')} (S)`} aria-label={`${t('parameters:useSeed')} (S)`}
isDisabled={!currentImage?.metadata?.image?.seed} isDisabled={!currentImage?.metadata?.image?.seed}
onClick={handleClickUseSeed} onClick={handleClickUseSeed}
/> />
<IAIIconButton <IAIIconButton
icon={<FaAsterisk />} icon={<FaAsterisk />}
tooltip={`${t('options:useAll')} (A)`} tooltip={`${t('parameters:useAll')} (A)`}
aria-label={`${t('options:useAll')} (A)`} aria-label={`${t('parameters:useAll')} (A)`}
isDisabled={ isDisabled={
!['txt2img', 'img2img'].includes( !['txt2img', 'img2img'].includes(
currentImage?.metadata?.image?.type currentImage?.metadata?.image?.type
@ -494,12 +480,12 @@ const CurrentImageButtons = () => {
triggerComponent={ triggerComponent={
<IAIIconButton <IAIIconButton
icon={<FaGrinStars />} icon={<FaGrinStars />}
aria-label={t('options:restoreFaces')} aria-label={t('parameters:restoreFaces')}
/> />
} }
> >
<div className="current-image-postprocessing-popover"> <div className="current-image-postprocessing-popover">
<FaceRestoreOptions /> <FaceRestoreSettings />
<IAIButton <IAIButton
isDisabled={ isDisabled={
!isGFPGANAvailable || !isGFPGANAvailable ||
@ -509,7 +495,7 @@ const CurrentImageButtons = () => {
} }
onClick={handleClickFixFaces} onClick={handleClickFixFaces}
> >
{t('options:restoreFaces')} {t('parameters:restoreFaces')}
</IAIButton> </IAIButton>
</div> </div>
</IAIPopover> </IAIPopover>
@ -519,12 +505,12 @@ const CurrentImageButtons = () => {
triggerComponent={ triggerComponent={
<IAIIconButton <IAIIconButton
icon={<FaExpandArrowsAlt />} icon={<FaExpandArrowsAlt />}
aria-label={t('options:upscale')} aria-label={t('parameters:upscale')}
/> />
} }
> >
<div className="current-image-postprocessing-popover"> <div className="current-image-postprocessing-popover">
<UpscaleOptions /> <UpscaleSettings />
<IAIButton <IAIButton
isDisabled={ isDisabled={
!isESRGANAvailable || !isESRGANAvailable ||
@ -534,7 +520,7 @@ const CurrentImageButtons = () => {
} }
onClick={handleClickUpscale} onClick={handleClickUpscale}
> >
{t('options:upscaleImage')} {t('parameters:upscaleImage')}
</IAIButton> </IAIButton>
</div> </div>
</IAIPopover> </IAIPopover>
@ -543,8 +529,8 @@ const CurrentImageButtons = () => {
<ButtonGroup isAttached={true}> <ButtonGroup isAttached={true}>
<IAIIconButton <IAIIconButton
icon={<FaCode />} icon={<FaCode />}
tooltip={`${t('options:info')} (I)`} tooltip={`${t('parameters:info')} (I)`}
aria-label={`${t('options:info')} (I)`} aria-label={`${t('parameters:info')} (I)`}
data-selected={shouldShowImageDetails} data-selected={shouldShowImageDetails}
onClick={handleClickShowImageDetails} onClick={handleClickShowImageDetails}
/> />
@ -553,8 +539,8 @@ const CurrentImageButtons = () => {
<DeleteImageModal image={currentImage}> <DeleteImageModal image={currentImage}>
<IAIIconButton <IAIIconButton
icon={<FaTrash />} icon={<FaTrash />}
tooltip={`${t('options:deleteImage')} (Del)`} tooltip={`${t('parameters:deleteImage')} (Del)`}
aria-label={`${t('options:deleteImage')} (Del)`} aria-label={`${t('parameters:deleteImage')} (Del)`}
isDisabled={!currentImage || !isConnected || isProcessing} isDisabled={!currentImage || !isConnected || isProcessing}
style={{ backgroundColor: 'var(--btn-delete-image)' }} style={{ backgroundColor: 'var(--btn-delete-image)' }}
/> />

View File

@ -1,23 +1,21 @@
import { RootState } from 'app/store';
import { useAppSelector } from 'app/storeHooks'; import { useAppSelector } from 'app/storeHooks';
import CurrentImageButtons from './CurrentImageButtons'; import CurrentImageButtons from './CurrentImageButtons';
import { MdPhoto } from 'react-icons/md'; import { MdPhoto } from 'react-icons/md';
import CurrentImagePreview from './CurrentImagePreview'; import CurrentImagePreview from './CurrentImagePreview';
import { GalleryState } from 'features/gallery/store/gallerySlice'; import { GalleryState } from 'features/gallery/store/gallerySlice';
import { OptionsState } from 'features/options/store/optionsSlice';
import _ from 'lodash'; import _ from 'lodash';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { activeTabNameSelector } from 'features/options/store/optionsSelectors'; import {
activeTabNameSelector,
uiSelector,
} from 'features/ui/store/uiSelectors';
import { gallerySelector } from '../store/gallerySelectors';
export const currentImageDisplaySelector = createSelector( export const currentImageDisplaySelector = createSelector(
[ [gallerySelector, uiSelector, activeTabNameSelector],
(state: RootState) => state.gallery, (gallery: GalleryState, ui, activeTabName) => {
(state: RootState) => state.options,
activeTabNameSelector,
],
(gallery: GalleryState, options: OptionsState, activeTabName) => {
const { currentImage, intermediateImage } = gallery; const { currentImage, intermediateImage } = gallery;
const { shouldShowImageDetails } = options; const { shouldShowImageDetails } = ui;
return { return {
activeTabName, activeTabName,

View File

@ -1,7 +1,6 @@
import { IconButton, Image } from '@chakra-ui/react'; import { IconButton, Image } from '@chakra-ui/react';
import { useState } from 'react'; import { useState } from 'react';
import { FaAngleLeft, FaAngleRight } from 'react-icons/fa'; import { FaAngleLeft, FaAngleRight } from 'react-icons/fa';
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import { import {
GalleryCategory, GalleryCategory,
@ -11,14 +10,15 @@ import {
} from 'features/gallery/store/gallerySlice'; } from 'features/gallery/store/gallerySlice';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import _ from 'lodash'; import _ from 'lodash';
import { OptionsState } from 'features/options/store/optionsSlice';
import ImageMetadataViewer from './ImageMetaDataViewer/ImageMetadataViewer'; import ImageMetadataViewer from './ImageMetaDataViewer/ImageMetadataViewer';
import { uiSelector } from 'features/ui/store/uiSelectors';
import { gallerySelector } from '../store/gallerySelectors';
export const imagesSelector = createSelector( export const imagesSelector = createSelector(
[(state: RootState) => state.gallery, (state: RootState) => state.options], [gallerySelector, uiSelector],
(gallery: GalleryState, options: OptionsState) => { (gallery: GalleryState, ui) => {
const { currentCategory, currentImage, intermediateImage } = gallery; const { currentCategory, currentImage, intermediateImage } = gallery;
const { shouldShowImageDetails } = options; const { shouldShowImageDetails } = ui;
const tempImages = const tempImages =
gallery.categories[ gallery.categories[

View File

@ -18,7 +18,6 @@ import { ChangeEvent, ReactElement, SyntheticEvent } from 'react';
import { cloneElement, forwardRef, useRef } from 'react'; import { cloneElement, forwardRef, useRef } from 'react';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import { deleteImage } from 'app/socketio/actions'; import { deleteImage } from 'app/socketio/actions';
import { RootState } from 'app/store';
import { import {
setShouldConfirmOnDelete, setShouldConfirmOnDelete,
SystemState, SystemState,
@ -26,9 +25,10 @@ import {
import * as InvokeAI from 'app/invokeai'; import * as InvokeAI from 'app/invokeai';
import { useHotkeys } from 'react-hotkeys-hook'; import { useHotkeys } from 'react-hotkeys-hook';
import _ from 'lodash'; import _ from 'lodash';
import { systemSelector } from 'features/system/store/systemSelectors';
const systemSelector = createSelector( const deleteImageModalSelector = createSelector(
(state: RootState) => state.system, systemSelector,
(system: SystemState) => { (system: SystemState) => {
const { shouldConfirmOnDelete, isConnected, isProcessing } = system; const { shouldConfirmOnDelete, isConnected, isProcessing } = system;
return { shouldConfirmOnDelete, isConnected, isProcessing }; return { shouldConfirmOnDelete, isConnected, isProcessing };
@ -60,8 +60,9 @@ const DeleteImageModal = forwardRef(
({ image, children }: DeleteImageModalProps, ref) => { ({ image, children }: DeleteImageModalProps, ref) => {
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { shouldConfirmOnDelete, isConnected, isProcessing } = const { shouldConfirmOnDelete, isConnected, isProcessing } = useAppSelector(
useAppSelector(systemSelector); deleteImageModalSelector
);
const cancelRef = useRef<HTMLButtonElement>(null); const cancelRef = useRef<HTMLButtonElement>(null);
const handleClickDelete = (e: SyntheticEvent) => { const handleClickDelete = (e: SyntheticEvent) => {

View File

@ -4,28 +4,28 @@ import {
setCurrentImage, setCurrentImage,
setShouldHoldGalleryOpen, setShouldHoldGalleryOpen,
} from 'features/gallery/store/gallerySlice'; } from 'features/gallery/store/gallerySlice';
import { FaCheck, FaTrashAlt } from 'react-icons/fa';
import DeleteImageModal from './DeleteImageModal';
import { DragEvent, memo, useState } from 'react';
import { import {
setActiveTab,
setAllImageToImageParameters, setAllImageToImageParameters,
setAllParameters, setAllParameters,
setInitialImage, setInitialImage,
setIsLightBoxOpen,
setNegativePrompt, setNegativePrompt,
setPrompt, setPrompt,
setSeed, setSeed,
} from 'features/options/store/optionsSlice'; } from 'features/parameters/store/generationSlice';
import * as InvokeAI from 'app/invokeai'; import { DragEvent, memo, useState } from 'react';
import { FaCheck, FaTrashAlt } from 'react-icons/fa';
import DeleteImageModal from './DeleteImageModal';
import * as ContextMenu from '@radix-ui/react-context-menu'; import * as ContextMenu from '@radix-ui/react-context-menu';
import * as InvokeAI from 'app/invokeai';
import { getPromptAndNegative } from 'common/util/getPromptAndNegative';
import { import {
resizeAndScaleCanvas, resizeAndScaleCanvas,
setInitialCanvasImage, setInitialCanvasImage,
} from 'features/canvas/store/canvasSlice'; } from 'features/canvas/store/canvasSlice';
import { hoverableImageSelector } from 'features/gallery/store/gallerySliceSelectors'; import { hoverableImageSelector } from 'features/gallery/store/gallerySelectors';
import { setActiveTab } from 'features/ui/store/uiSlice';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { getPromptAndNegative } from 'common/util/getPromptAndNegative';
interface HoverableImageProps { interface HoverableImageProps {
image: InvokeAI.Image; image: InvokeAI.Image;
@ -47,7 +47,6 @@ const HoverableImage = memo((props: HoverableImageProps) => {
galleryImageObjectFit, galleryImageObjectFit,
galleryImageMinimumWidth, galleryImageMinimumWidth,
mayDeleteImage, mayDeleteImage,
isLightBoxOpen,
shouldUseSingleGalleryColumn, shouldUseSingleGalleryColumn,
} = useAppSelector(hoverableImageSelector); } = useAppSelector(hoverableImageSelector);
const { image, isSelected } = props; const { image, isSelected } = props;
@ -94,7 +93,6 @@ const HoverableImage = memo((props: HoverableImageProps) => {
}; };
const handleSendToImageToImage = () => { const handleSendToImageToImage = () => {
if (isLightBoxOpen) dispatch(setIsLightBoxOpen(false));
dispatch(setInitialImage(image)); dispatch(setInitialImage(image));
if (activeTabName !== 'img2img') { if (activeTabName !== 'img2img') {
dispatch(setActiveTab('img2img')); dispatch(setActiveTab('img2img'));
@ -108,8 +106,6 @@ const HoverableImage = memo((props: HoverableImageProps) => {
}; };
const handleSendToCanvas = () => { const handleSendToCanvas = () => {
if (isLightBoxOpen) dispatch(setIsLightBoxOpen(false));
dispatch(setInitialCanvasImage(image)); dispatch(setInitialCanvasImage(image));
dispatch(resizeAndScaleCanvas()); dispatch(resizeAndScaleCanvas());
@ -168,7 +164,6 @@ const HoverableImage = memo((props: HoverableImageProps) => {
}; };
const handleLightBox = () => { const handleLightBox = () => {
dispatch(setIsLightBoxOpen(true));
dispatch(setCurrentImage(image)); dispatch(setCurrentImage(image));
}; };
@ -212,7 +207,7 @@ const HoverableImage = memo((props: HoverableImageProps) => {
<div className="hoverable-image-delete-button"> <div className="hoverable-image-delete-button">
<DeleteImageModal image={image}> <DeleteImageModal image={image}>
<IconButton <IconButton
aria-label={t('options:deleteImage')} aria-label={t('parameters:deleteImage')}
icon={<FaTrashAlt />} icon={<FaTrashAlt />}
size="xs" size="xs"
variant={'imageHoverIconButton'} variant={'imageHoverIconButton'}
@ -232,20 +227,20 @@ const HoverableImage = memo((props: HoverableImageProps) => {
}} }}
> >
<ContextMenu.Item onClickCapture={handleLightBox}> <ContextMenu.Item onClickCapture={handleLightBox}>
{t('options:openInViewer')} {t('parameters:openInViewer')}
</ContextMenu.Item> </ContextMenu.Item>
<ContextMenu.Item <ContextMenu.Item
onClickCapture={handleUsePrompt} onClickCapture={handleUsePrompt}
disabled={image?.metadata?.image?.prompt === undefined} disabled={image?.metadata?.image?.prompt === undefined}
> >
{t('options:usePrompt')} {t('parameters:usePrompt')}
</ContextMenu.Item> </ContextMenu.Item>
<ContextMenu.Item <ContextMenu.Item
onClickCapture={handleUseSeed} onClickCapture={handleUseSeed}
disabled={image?.metadata?.image?.seed === undefined} disabled={image?.metadata?.image?.seed === undefined}
> >
{t('options:useSeed')} {t('parameters:useSeed')}
</ContextMenu.Item> </ContextMenu.Item>
<ContextMenu.Item <ContextMenu.Item
onClickCapture={handleUseAllParameters} onClickCapture={handleUseAllParameters}
@ -253,23 +248,23 @@ const HoverableImage = memo((props: HoverableImageProps) => {
!['txt2img', 'img2img'].includes(image?.metadata?.image?.type) !['txt2img', 'img2img'].includes(image?.metadata?.image?.type)
} }
> >
{t('options:useAll')} {t('parameters:useAll')}
</ContextMenu.Item> </ContextMenu.Item>
<ContextMenu.Item <ContextMenu.Item
onClickCapture={handleUseInitialImage} onClickCapture={handleUseInitialImage}
disabled={image?.metadata?.image?.type !== 'img2img'} disabled={image?.metadata?.image?.type !== 'img2img'}
> >
{t('options:useInitImg')} {t('parameters:useInitImg')}
</ContextMenu.Item> </ContextMenu.Item>
<ContextMenu.Item onClickCapture={handleSendToImageToImage}> <ContextMenu.Item onClickCapture={handleSendToImageToImage}>
{t('options:sendToImg2Img')} {t('parameters:sendToImg2Img')}
</ContextMenu.Item> </ContextMenu.Item>
<ContextMenu.Item onClickCapture={handleSendToCanvas}> <ContextMenu.Item onClickCapture={handleSendToCanvas}>
{t('options:sendToUnifiedCanvas')} {t('parameters:sendToUnifiedCanvas')}
</ContextMenu.Item> </ContextMenu.Item>
<ContextMenu.Item data-warning> <ContextMenu.Item data-warning>
<DeleteImageModal image={image}> <DeleteImageModal image={image}>
<p>{t('options:deleteImage')}</p> <p>{t('parameters:deleteImage')}</p>
</DeleteImageModal> </DeleteImageModal>
</ContextMenu.Item> </ContextMenu.Item>
</ContextMenu.Content> </ContextMenu.Content>

View File

@ -32,7 +32,7 @@ import { setShouldShowGallery } from 'features/gallery/store/gallerySlice';
import { ButtonGroup } from '@chakra-ui/react'; import { ButtonGroup } from '@chakra-ui/react';
import { CSSTransition } from 'react-transition-group'; import { CSSTransition } from 'react-transition-group';
import { Direction } from 're-resizable/lib/resizer'; import { Direction } from 're-resizable/lib/resizer';
import { imageGallerySelector } from 'features/gallery/store/gallerySliceSelectors'; import { imageGallerySelector } from 'features/gallery/store/gallerySelectors';
import { FaImage, FaUser, FaWrench } from 'react-icons/fa'; import { FaImage, FaUser, FaWrench } from 'react-icons/fa';
import IAIPopover from 'common/components/IAIPopover'; import IAIPopover from 'common/components/IAIPopover';
import IAISlider from 'common/components/IAISlider'; import IAISlider from 'common/components/IAISlider';
@ -41,7 +41,7 @@ import IAICheckbox from 'common/components/IAICheckbox';
import { setDoesCanvasNeedScaling } from 'features/canvas/store/canvasSlice'; import { setDoesCanvasNeedScaling } from 'features/canvas/store/canvasSlice';
import _ from 'lodash'; import _ from 'lodash';
import IAIButton from 'common/components/IAIButton'; import IAIButton from 'common/components/IAIButton';
import { InvokeTabName } from 'features/tabs/tabMap'; import { InvokeTabName } from 'features/ui/store/tabMap';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const GALLERY_SHOW_BUTTONS_MIN_WIDTH = 320; const GALLERY_SHOW_BUTTONS_MIN_WIDTH = 320;
@ -81,13 +81,13 @@ export default function ImageGallery() {
shouldAutoSwitchToNewImages, shouldAutoSwitchToNewImages,
areMoreImagesAvailable, areMoreImagesAvailable,
galleryWidth, galleryWidth,
isLightBoxOpen, isLightboxOpen,
isStaging, isStaging,
shouldEnableResize, shouldEnableResize,
shouldUseSingleGalleryColumn, shouldUseSingleGalleryColumn,
} = useAppSelector(imageGallerySelector); } = useAppSelector(imageGallerySelector);
const { galleryMinWidth, galleryMaxWidth } = isLightBoxOpen const { galleryMinWidth, galleryMaxWidth } = isLightboxOpen
? { ? {
galleryMinWidth: LIGHTBOX_GALLERY_WIDTH, galleryMinWidth: LIGHTBOX_GALLERY_WIDTH,
galleryMaxWidth: LIGHTBOX_GALLERY_WIDTH, galleryMaxWidth: LIGHTBOX_GALLERY_WIDTH,

View File

@ -14,11 +14,7 @@ import { useAppDispatch } from 'app/storeHooks';
import * as InvokeAI from 'app/invokeai'; import * as InvokeAI from 'app/invokeai';
import { import {
setCfgScale, setCfgScale,
setFacetoolStrength,
setCodeformerFidelity,
setFacetoolType,
setHeight, setHeight,
setHiresFix,
setImg2imgStrength, setImg2imgStrength,
setMaskPath, setMaskPath,
setPrompt, setPrompt,
@ -28,14 +24,20 @@ import {
setSeedWeights, setSeedWeights,
setShouldFitToWidthHeight, setShouldFitToWidthHeight,
setSteps, setSteps,
setUpscalingLevel,
setUpscalingStrength,
setWidth, setWidth,
setInitialImage, setInitialImage,
setShouldShowImageDetails,
setThreshold, setThreshold,
setPerlin, setPerlin,
} from 'features/options/store/optionsSlice'; } from 'features/parameters/store/generationSlice';
import {
setFacetoolStrength,
setCodeformerFidelity,
setFacetoolType,
setHiresFix,
setUpscalingLevel,
setUpscalingStrength,
} from 'features/parameters/store/postprocessingSlice';
import { setShouldShowImageDetails } from 'features/ui/store/uiSlice';
import promptToString from 'common/util/promptToString'; import promptToString from 'common/util/promptToString';
import { seedWeightsToString } from 'common/util/seedWeightPairs'; import { seedWeightsToString } from 'common/util/seedWeightPairs';
import { FaCopy } from 'react-icons/fa'; import { FaCopy } from 'react-icons/fa';

View File

@ -1,6 +1,6 @@
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/storeHooks'; import { useAppSelector } from 'app/storeHooks';
import { gallerySelector } from '../store/gallerySliceSelectors'; import { gallerySelector } from '../store/gallerySelectors';
const selector = createSelector(gallerySelector, (gallery) => ({ const selector = createSelector(gallerySelector, (gallery) => ({
resultImages: gallery.categories.result.images, resultImages: gallery.categories.result.images,

View File

@ -1,20 +1,17 @@
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { activeTabNameSelector } from 'features/options/store/optionsSelectors'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { OptionsState } from 'features/options/store/optionsSlice';
import { SystemState } from 'features/system/store/systemSlice';
import { GalleryState } from './gallerySlice'; import { GalleryState } from './gallerySlice';
import _ from 'lodash'; import _ from 'lodash';
import { isStagingSelector } from 'features/canvas/store/canvasSelectors'; import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
import { lightboxSelector } from 'features/lightbox/store/lightboxSelectors';
import { systemSelector } from 'features/system/store/systemSelectors';
export const gallerySelector = (state: RootState) => state.gallery;
export const imageGallerySelector = createSelector( export const imageGallerySelector = createSelector(
[ [gallerySelector, lightboxSelector, isStagingSelector, activeTabNameSelector],
(state: RootState) => state.gallery, (gallery: GalleryState, lightbox, isStaging, activeTabName) => {
(state: RootState) => state.options,
isStagingSelector,
activeTabNameSelector,
],
(gallery: GalleryState, options: OptionsState, isStaging, activeTabName) => {
const { const {
categories, categories,
currentCategory, currentCategory,
@ -30,7 +27,7 @@ export const imageGallerySelector = createSelector(
shouldUseSingleGalleryColumn, shouldUseSingleGalleryColumn,
} = gallery; } = gallery;
const { isLightBoxOpen } = options; const { isLightboxOpen } = lightbox;
return { return {
currentImageUuid, currentImageUuid,
@ -50,10 +47,10 @@ export const imageGallerySelector = createSelector(
categories[currentCategory].areMoreImagesAvailable, categories[currentCategory].areMoreImagesAvailable,
currentCategory, currentCategory,
galleryWidth, galleryWidth,
isLightBoxOpen, isLightboxOpen,
isStaging, isStaging,
shouldEnableResize: shouldEnableResize:
isLightBoxOpen || isLightboxOpen ||
(activeTabName === 'unifiedCanvas' && shouldPinGallery) (activeTabName === 'unifiedCanvas' && shouldPinGallery)
? false ? false
: true, : true,
@ -68,25 +65,15 @@ export const imageGallerySelector = createSelector(
); );
export const hoverableImageSelector = createSelector( export const hoverableImageSelector = createSelector(
[ [gallerySelector, systemSelector, lightboxSelector, activeTabNameSelector],
(state: RootState) => state.options, (gallery: GalleryState, system, lightbox, activeTabName) => {
(state: RootState) => state.gallery,
(state: RootState) => state.system,
activeTabNameSelector,
],
(
options: OptionsState,
gallery: GalleryState,
system: SystemState,
activeTabName
) => {
return { return {
mayDeleteImage: system.isConnected && !system.isProcessing, mayDeleteImage: system.isConnected && !system.isProcessing,
galleryImageObjectFit: gallery.galleryImageObjectFit, galleryImageObjectFit: gallery.galleryImageObjectFit,
galleryImageMinimumWidth: gallery.galleryImageMinimumWidth, galleryImageMinimumWidth: gallery.galleryImageMinimumWidth,
shouldUseSingleGalleryColumn: gallery.shouldUseSingleGalleryColumn, shouldUseSingleGalleryColumn: gallery.shouldUseSingleGalleryColumn,
activeTabName, activeTabName,
isLightBoxOpen: options.isLightBoxOpen, isLightboxOpen: lightbox.isLightboxOpen,
}; };
}, },
{ {
@ -95,5 +82,3 @@ export const hoverableImageSelector = createSelector(
}, },
} }
); );
export const gallerySelector = (state: RootState) => state.gallery;

View File

@ -3,7 +3,7 @@ import type { PayloadAction } from '@reduxjs/toolkit';
import _, { clamp } from 'lodash'; import _, { clamp } from 'lodash';
import * as InvokeAI from 'app/invokeai'; import * as InvokeAI from 'app/invokeai';
import { IRect } from 'konva/lib/types'; import { IRect } from 'konva/lib/types';
import { InvokeTabName } from 'features/tabs/tabMap'; import { InvokeTabName } from 'features/ui/store/tabMap';
export type GalleryCategory = 'user' | 'result'; export type GalleryCategory = 'user' | 'result';
@ -241,6 +241,7 @@ export const gallerySlice = createSlice({
setShouldShowGallery: (state, action: PayloadAction<boolean>) => { setShouldShowGallery: (state, action: PayloadAction<boolean>) => {
state.shouldShowGallery = action.payload; state.shouldShowGallery = action.payload;
}, },
setGalleryScrollPosition: (state, action: PayloadAction<number>) => { setGalleryScrollPosition: (state, action: PayloadAction<number>) => {
state.galleryScrollPosition = action.payload; state.galleryScrollPosition = action.payload;
}, },

View File

@ -2,9 +2,9 @@ import { AnyAction, ThunkAction } from '@reduxjs/toolkit';
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import * as InvokeAI from 'app/invokeai'; import * as InvokeAI from 'app/invokeai';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import { activeTabNameSelector } from 'features/options/store/optionsSelectors'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice'; import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
import { setInitialImage } from 'features/options/store/optionsSlice'; import { setInitialImage } from 'features/parameters/store/generationSlice';
import { addImage } from '../gallerySlice'; import { addImage } from '../gallerySlice';
type UploadImageConfig = { type UploadImageConfig = {

View File

@ -10,7 +10,7 @@ import {
} from 'features/gallery/store/gallerySlice'; } from 'features/gallery/store/gallerySlice';
import ImageGallery from 'features/gallery/components/ImageGallery'; import ImageGallery from 'features/gallery/components/ImageGallery';
import ImageMetadataViewer from 'features/gallery/components/ImageMetaDataViewer/ImageMetadataViewer'; import ImageMetadataViewer from 'features/gallery/components/ImageMetaDataViewer/ImageMetadataViewer';
import { setIsLightBoxOpen } from 'features/options/store/optionsSlice'; import { setIsLightboxOpen } from 'features/lightbox/store/lightboxSlice';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { useHotkeys } from 'react-hotkeys-hook'; import { useHotkeys } from 'react-hotkeys-hook';
import { BiExit } from 'react-icons/bi'; import { BiExit } from 'react-icons/bi';
@ -20,7 +20,7 @@ import ReactPanZoom from './ReactPanZoom';
export default function Lightbox() { export default function Lightbox() {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const isLightBoxOpen = useAppSelector( const isLightBoxOpen = useAppSelector(
(state: RootState) => state.options.isLightBoxOpen (state: RootState) => state.lightbox.isLightboxOpen
); );
const { const {
@ -52,7 +52,7 @@ export default function Lightbox() {
useHotkeys( useHotkeys(
'Esc', 'Esc',
() => { () => {
if (isLightBoxOpen) dispatch(setIsLightBoxOpen(false)); if (isLightBoxOpen) dispatch(setIsLightboxOpen(false));
}, },
[isLightBoxOpen] [isLightBoxOpen]
); );
@ -64,7 +64,7 @@ export default function Lightbox() {
aria-label="Exit Viewer" aria-label="Exit Viewer"
className="lightbox-close-btn" className="lightbox-close-btn"
onClick={() => { onClick={() => {
dispatch(setIsLightBoxOpen(false)); dispatch(setIsLightboxOpen(false));
}} }}
fontSize={20} fontSize={20}
/> />

View File

@ -0,0 +1,13 @@
import { createSelector } from '@reduxjs/toolkit';
import { RootState } from 'app/store';
import _ from 'lodash';
export const lightboxSelector = createSelector(
(state: RootState) => state.lightbox,
(lightbox) => lightbox,
{
memoizeOptions: {
equalityCheck: _.isEqual,
},
}
);

View File

@ -0,0 +1,26 @@
import { createSlice } from '@reduxjs/toolkit';
import type { PayloadAction } from '@reduxjs/toolkit';
export interface LightboxState {
isLightboxOpen: boolean;
}
const initialLightboxState: LightboxState = {
isLightboxOpen: false,
};
const initialState: LightboxState = initialLightboxState;
export const lightboxSlice = createSlice({
name: 'lightbox',
initialState,
reducers: {
setIsLightboxOpen: (state, action: PayloadAction<boolean>) => {
state.isLightboxOpen = action.payload;
},
},
});
export const { setIsLightboxOpen } = lightboxSlice.actions;
export default lightboxSlice.reducer;

View File

@ -1,12 +0,0 @@
import { Flex } from '@chakra-ui/react';
import SeamlessOptions from './SeamlessOptions';
const ImageToImageOutputOptions = () => {
return (
<Flex gap={2} direction={'column'}>
<SeamlessOptions />
</Flex>
);
};
export default ImageToImageOutputOptions;

View File

@ -1,14 +0,0 @@
import { Flex } from '@chakra-ui/react';
import HiresOptions from './HiresOptions';
import SeamlessOptions from './SeamlessOptions';
const OutputOptions = () => {
return (
<Flex gap={2} direction={'column'}>
<SeamlessOptions />
<HiresOptions />
</Flex>
);
};
export default OutputOptions;

View File

@ -1,24 +0,0 @@
import React, { ChangeEvent } from 'react';
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAICheckbox from 'common/components/IAICheckbox';
import { setShowAdvancedOptions } from 'features/options/store/optionsSlice';
export default function MainAdvancedOptionsCheckbox() {
const showAdvancedOptions = useAppSelector(
(state: RootState) => state.options.showAdvancedOptions
);
const dispatch = useAppDispatch();
const handleShowAdvancedOptions = (e: ChangeEvent<HTMLInputElement>) =>
dispatch(setShowAdvancedOptions(e.target.checked));
return (
<IAICheckbox
label="Advanced Options"
styleClass="advanced-options-checkbox"
onChange={handleShowAdvancedOptions}
isChecked={showAdvancedOptions}
/>
);
}

View File

@ -1,32 +0,0 @@
import { createSelector } from '@reduxjs/toolkit';
import _ from 'lodash';
import { RootState } from 'app/store';
import { tabMap } from 'features/tabs/tabMap';
import { OptionsState } from './optionsSlice';
export const activeTabNameSelector = createSelector(
(state: RootState) => state.options,
(options: OptionsState) => tabMap[options.activeTab],
{
memoizeOptions: {
equalityCheck: _.isEqual,
},
}
);
export const mayGenerateMultipleImagesSelector = createSelector(
(state: RootState) => state.options,
(options: OptionsState) => {
const { shouldRandomizeSeed, shouldGenerateVariations } = options;
return shouldRandomizeSeed || shouldGenerateVariations;
},
{
memoizeOptions: {
resultEqualityCheck: _.isEqual,
},
}
);
export const optionsSelector = (state: RootState): OptionsState =>
state.options;

View File

@ -1,12 +1,12 @@
@use '../../../../styles/Mixins/' as *; @use '../../../../styles/Mixins/' as *;
.advanced-settings { .advanced-parameters {
padding-top: 0.5rem; padding-top: 0.5rem;
display: grid; display: grid;
row-gap: 0.5rem; row-gap: 0.5rem;
} }
.advanced-settings-item { .advanced-parameters-item {
display: grid; display: grid;
max-width: $options-bar-max-width; max-width: $options-bar-max-width;
border: none; border: none;
@ -20,7 +20,7 @@
} }
} }
.advanced-settings-panel { .advanced-parameters-panel {
background-color: var(--tab-panel-bg); background-color: var(--tab-panel-bg);
border-radius: 0 0 0.4rem 0.4rem; border-radius: 0 0 0.4rem 0.4rem;
padding: 1rem; padding: 1rem;
@ -39,7 +39,7 @@
} }
} }
.advanced-settings-header { .advanced-parameters-header {
border-radius: 0.4rem; border-radius: 0.4rem;
font-weight: bold; font-weight: bold;

View File

@ -21,8 +21,8 @@ export default function InvokeAccordionItem(props: InvokeAccordionItemProps) {
const { header, feature, content, additionalHeaderComponents } = props; const { header, feature, content, additionalHeaderComponents } = props;
return ( return (
<AccordionItem className="advanced-settings-item"> <AccordionItem className="advanced-parameters-item">
<AccordionButton className="advanced-settings-header"> <AccordionButton className="advanced-parameters-header">
<Flex width={'100%'} gap={'0.5rem'} align={'center'}> <Flex width={'100%'} gap={'0.5rem'} align={'center'}>
<Box flexGrow={1} textAlign={'left'}> <Box flexGrow={1} textAlign={'left'}>
{header} {header}
@ -32,7 +32,7 @@ export default function InvokeAccordionItem(props: InvokeAccordionItemProps) {
<AccordionIcon /> <AccordionIcon />
</Flex> </Flex>
</AccordionButton> </AccordionButton>
<AccordionPanel className="advanced-settings-panel"> <AccordionPanel className="advanced-parameters-panel">
{content} {content}
</AccordionPanel> </AccordionPanel>
</AccordionItem> </AccordionItem>

View File

@ -69,7 +69,7 @@ const BoundingBoxSettings = () => {
return ( return (
<Flex direction="column" gap="1rem"> <Flex direction="column" gap="1rem">
<IAISlider <IAISlider
label={t('options:width')} label={t('parameters:width')}
min={64} min={64}
max={1024} max={1024}
step={64} step={64}
@ -83,7 +83,7 @@ const BoundingBoxSettings = () => {
handleReset={handleResetWidth} handleReset={handleResetWidth}
/> />
<IAISlider <IAISlider
label={t('options:height')} label={t('parameters:height')}
min={64} min={64}
max={1024} max={1024}
step={64} step={64}
@ -106,7 +106,7 @@ export const BoundingBoxSettingsHeader = () => {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<Box flex="1" textAlign="left"> <Box flex="1" textAlign="left">
{t('options:boundingBoxHeader')} {t('parameters:boundingBoxHeader')}
</Box> </Box>
); );
}; };

View File

@ -12,20 +12,20 @@ import {
BoundingBoxScale, BoundingBoxScale,
BOUNDING_BOX_SCALES_DICT, BOUNDING_BOX_SCALES_DICT,
} from 'features/canvas/store/canvasTypes'; } from 'features/canvas/store/canvasTypes';
import { optionsSelector } from 'features/options/store/optionsSelectors'; import { generationSelector } from 'features/parameters/store/generationSelectors';
import { import {
setInfillMethod, setInfillMethod,
setTileSize, setTileSize,
} from 'features/options/store/optionsSlice'; } from 'features/parameters/store/generationSlice';
import { systemSelector } from 'features/system/store/systemSelectors'; import { systemSelector } from 'features/system/store/systemSelectors';
import _ from 'lodash'; import _ from 'lodash';
import { ChangeEvent } from 'react'; import { ChangeEvent } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const selector = createSelector( const selector = createSelector(
[optionsSelector, systemSelector, canvasSelector], [generationSelector, systemSelector, canvasSelector],
(options, system, canvas) => { (parameters, system, canvas) => {
const { tileSize, infillMethod } = options; const { tileSize, infillMethod } = parameters;
const { infill_methods: availableInfillMethods } = system; const { infill_methods: availableInfillMethods } = system;
@ -50,7 +50,7 @@ const selector = createSelector(
} }
); );
const InfillAndScalingOptions = () => { const InfillAndScalingSettings = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { const {
tileSize, tileSize,
@ -108,7 +108,7 @@ const InfillAndScalingOptions = () => {
return ( return (
<Flex direction="column" gap="1rem"> <Flex direction="column" gap="1rem">
<IAISelect <IAISelect
label={t('options:scaleBeforeProcessing')} label={t('parameters:scaleBeforeProcessing')}
validValues={BOUNDING_BOX_SCALES_DICT} validValues={BOUNDING_BOX_SCALES_DICT}
value={boundingBoxScale} value={boundingBoxScale}
onChange={handleChangeBoundingBoxScaleMethod} onChange={handleChangeBoundingBoxScaleMethod}
@ -117,7 +117,7 @@ const InfillAndScalingOptions = () => {
isInputDisabled={!isManual} isInputDisabled={!isManual}
isResetDisabled={!isManual} isResetDisabled={!isManual}
isSliderDisabled={!isManual} isSliderDisabled={!isManual}
label={t('options:scaledWidth')} label={t('parameters:scaledWidth')}
min={64} min={64}
max={1024} max={1024}
step={64} step={64}
@ -134,7 +134,7 @@ const InfillAndScalingOptions = () => {
isInputDisabled={!isManual} isInputDisabled={!isManual}
isResetDisabled={!isManual} isResetDisabled={!isManual}
isSliderDisabled={!isManual} isSliderDisabled={!isManual}
label={t('options:scaledHeight')} label={t('parameters:scaledHeight')}
min={64} min={64}
max={1024} max={1024}
step={64} step={64}
@ -148,7 +148,7 @@ const InfillAndScalingOptions = () => {
handleReset={handleResetScaledHeight} handleReset={handleResetScaledHeight}
/> />
<IAISelect <IAISelect
label={t('options:infillMethod')} label={t('parameters:infillMethod')}
value={infillMethod} value={infillMethod}
validValues={availableInfillMethods} validValues={availableInfillMethods}
onChange={(e) => dispatch(setInfillMethod(e.target.value))} onChange={(e) => dispatch(setInfillMethod(e.target.value))}
@ -158,7 +158,7 @@ const InfillAndScalingOptions = () => {
isResetDisabled={infillMethod !== 'tile'} isResetDisabled={infillMethod !== 'tile'}
isSliderDisabled={infillMethod !== 'tile'} isSliderDisabled={infillMethod !== 'tile'}
sliderMarkRightOffset={-4} sliderMarkRightOffset={-4}
label={t('options:tileSize')} label={t('parameters:tileSize')}
min={16} min={16}
max={64} max={64}
sliderNumberInputProps={{ max: 256 }} sliderNumberInputProps={{ max: 256 }}
@ -177,4 +177,4 @@ const InfillAndScalingOptions = () => {
); );
}; };
export default InfillAndScalingOptions; export default InfillAndScalingSettings;

View File

@ -1,19 +1,20 @@
import type { RootState } from 'app/store'; import type { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISlider from 'common/components/IAISlider'; import IAISlider from 'common/components/IAISlider';
import { setSeamBlur } from 'features/options/store/optionsSlice'; import { setSeamBlur } from 'features/parameters/store/generationSlice';
import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
export default function SeamBlur() { export default function SeamBlur() {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const seamBlur = useAppSelector((state: RootState) => state.options.seamBlur); const seamBlur = useAppSelector(
(state: RootState) => state.generation.seamBlur
);
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<IAISlider <IAISlider
sliderMarkRightOffset={-4} sliderMarkRightOffset={-4}
label={t('options:seamBlur')} label={t('parameters:seamBlur')}
min={0} min={0}
max={64} max={64}
sliderNumberInputProps={{ max: 512 }} sliderNumberInputProps={{ max: 512 }}

View File

@ -4,7 +4,7 @@ import SeamSize from './SeamSize';
import SeamSteps from './SeamSteps'; import SeamSteps from './SeamSteps';
import SeamStrength from './SeamStrength'; import SeamStrength from './SeamStrength';
const SeamCorrectionOptions = () => { const SeamCorrectionSettings = () => {
return ( return (
<Flex direction="column" gap="1rem"> <Flex direction="column" gap="1rem">
<SeamSize /> <SeamSize />
@ -15,4 +15,4 @@ const SeamCorrectionOptions = () => {
); );
}; };
export default SeamCorrectionOptions; export default SeamCorrectionSettings;

View File

@ -1,7 +1,7 @@
import type { RootState } from 'app/store'; import type { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISlider from 'common/components/IAISlider'; import IAISlider from 'common/components/IAISlider';
import { setSeamSize } from 'features/options/store/optionsSlice'; import { setSeamSize } from 'features/parameters/store/generationSlice';
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -9,12 +9,12 @@ export default function SeamSize() {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
const seamSize = useAppSelector((state: RootState) => state.options.seamSize); const seamSize = useAppSelector((state: RootState) => state.generation.seamSize);
return ( return (
<IAISlider <IAISlider
sliderMarkRightOffset={-6} sliderMarkRightOffset={-6}
label={t('options:seamSize')} label={t('parameters:seamSize')}
min={1} min={1}
max={256} max={256}
sliderNumberInputProps={{ max: 512 }} sliderNumberInputProps={{ max: 512 }}

View File

@ -1,21 +1,21 @@
import type { RootState } from 'app/store'; import type { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISlider from 'common/components/IAISlider'; import IAISlider from 'common/components/IAISlider';
import { setSeamSteps } from 'features/options/store/optionsSlice'; import { setSeamSteps } from 'features/parameters/store/generationSlice';
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
export default function SeamSteps() { export default function SeamSteps() {
const { t } = useTranslation(); const { t } = useTranslation();
const seamSteps = useAppSelector( const seamSteps = useAppSelector(
(state: RootState) => state.options.seamSteps (state: RootState) => state.generation.seamSteps
); );
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
return ( return (
<IAISlider <IAISlider
sliderMarkRightOffset={-4} sliderMarkRightOffset={-4}
label={t('options:seamSteps')} label={t('parameters:seamSteps')}
min={1} min={1}
max={100} max={100}
sliderNumberInputProps={{ max: 999 }} sliderNumberInputProps={{ max: 999 }}

View File

@ -1,7 +1,7 @@
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISlider from 'common/components/IAISlider'; import IAISlider from 'common/components/IAISlider';
import { setSeamStrength } from 'features/options/store/optionsSlice'; import { setSeamStrength } from 'features/parameters/store/generationSlice';
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -9,13 +9,13 @@ export default function SeamStrength() {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
const seamStrength = useAppSelector( const seamStrength = useAppSelector(
(state: RootState) => state.options.seamStrength (state: RootState) => state.generation.seamStrength
); );
return ( return (
<IAISlider <IAISlider
sliderMarkRightOffset={-7} sliderMarkRightOffset={-7}
label={t('options:seamStrength')} label={t('parameters:seamStrength')}
min={0.01} min={0.01}
max={0.99} max={0.99}
step={0.01} step={0.01}

View File

@ -1,46 +1,36 @@
import { Flex } from '@chakra-ui/react'; import { Flex } from '@chakra-ui/react';
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import { FacetoolType } from 'features/parameters/store/postprocessingSlice';
import { import {
FacetoolType,
OptionsState,
setCodeformerFidelity, setCodeformerFidelity,
setFacetoolStrength, setFacetoolStrength,
setFacetoolType, setFacetoolType,
} from 'features/options/store/optionsSlice'; } from 'features/parameters/store/postprocessingSlice';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
import { SystemState } from 'features/system/store/systemSlice';
import IAINumberInput from 'common/components/IAINumberInput'; import IAINumberInput from 'common/components/IAINumberInput';
import IAISelect from 'common/components/IAISelect'; import IAISelect from 'common/components/IAISelect';
import { FACETOOL_TYPES } from 'app/constants'; import { FACETOOL_TYPES } from 'app/constants';
import { ChangeEvent } from 'react'; import { ChangeEvent } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { postprocessingSelector } from 'features/parameters/store/postprocessingSelectors';
import { systemSelector } from 'features/system/store/systemSelectors';
const optionsSelector = createSelector( const optionsSelector = createSelector(
(state: RootState) => state.options, [postprocessingSelector, systemSelector],
(options: OptionsState) => { (
{ facetoolStrength, facetoolType, codeformerFidelity },
{ isGFPGANAvailable }
) => {
return { return {
facetoolStrength: options.facetoolStrength, facetoolStrength,
facetoolType: options.facetoolType, facetoolType,
codeformerFidelity: options.codeformerFidelity, codeformerFidelity,
}; isGFPGANAvailable,
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);
const systemSelector = createSelector(
(state: RootState) => state.system,
(system: SystemState) => {
return {
isGFPGANAvailable: system.isGFPGANAvailable,
}; };
}, },
{ {
@ -53,11 +43,14 @@ const systemSelector = createSelector(
/** /**
* Displays face-fixing/GFPGAN options (strength). * Displays face-fixing/GFPGAN options (strength).
*/ */
const FaceRestoreOptions = () => { const FaceRestoreSettings = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { facetoolStrength, facetoolType, codeformerFidelity } = const {
useAppSelector(optionsSelector); facetoolStrength,
const { isGFPGANAvailable } = useAppSelector(systemSelector); facetoolType,
codeformerFidelity,
isGFPGANAvailable,
} = useAppSelector(optionsSelector);
const handleChangeStrength = (v: number) => dispatch(setFacetoolStrength(v)); const handleChangeStrength = (v: number) => dispatch(setFacetoolStrength(v));
@ -72,14 +65,14 @@ const FaceRestoreOptions = () => {
return ( return (
<Flex direction={'column'} gap={2}> <Flex direction={'column'} gap={2}>
<IAISelect <IAISelect
label={t('options:type')} label={t('parameters:type')}
validValues={FACETOOL_TYPES.concat()} validValues={FACETOOL_TYPES.concat()}
value={facetoolType} value={facetoolType}
onChange={handleChangeFacetoolType} onChange={handleChangeFacetoolType}
/> />
<IAINumberInput <IAINumberInput
isDisabled={!isGFPGANAvailable} isDisabled={!isGFPGANAvailable}
label={t('options:strength')} label={t('parameters:strength')}
step={0.05} step={0.05}
min={0} min={0}
max={1} max={1}
@ -91,7 +84,7 @@ const FaceRestoreOptions = () => {
{facetoolType === 'codeformer' && ( {facetoolType === 'codeformer' && (
<IAINumberInput <IAINumberInput
isDisabled={!isGFPGANAvailable} isDisabled={!isGFPGANAvailable}
label={t('options:codeformerFidelity')} label={t('parameters:codeformerFidelity')}
step={0.05} step={0.05}
min={0} min={0}
max={1} max={1}
@ -105,4 +98,4 @@ const FaceRestoreOptions = () => {
); );
}; };
export default FaceRestoreOptions; export default FaceRestoreSettings;

View File

@ -2,7 +2,7 @@ import { ChangeEvent } from 'react';
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISwitch from 'common/components/IAISwitch'; import IAISwitch from 'common/components/IAISwitch';
import { setShouldRunFacetool } from 'features/options/store/optionsSlice'; import { setShouldRunFacetool } from 'features/parameters/store/postprocessingSlice';
export default function FaceRestoreToggle() { export default function FaceRestoreToggle() {
const isGFPGANAvailable = useAppSelector( const isGFPGANAvailable = useAppSelector(
@ -10,7 +10,7 @@ export default function FaceRestoreToggle() {
); );
const shouldRunFacetool = useAppSelector( const shouldRunFacetool = useAppSelector(
(state: RootState) => state.options.shouldRunFacetool (state: RootState) => state.postprocessing.shouldRunFacetool
); );
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();

View File

@ -2,14 +2,14 @@ import React, { ChangeEvent } from 'react';
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISwitch from 'common/components/IAISwitch'; import IAISwitch from 'common/components/IAISwitch';
import { setShouldFitToWidthHeight } from 'features/options/store/optionsSlice'; import { setShouldFitToWidthHeight } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
export default function ImageFit() { export default function ImageFit() {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const shouldFitToWidthHeight = useAppSelector( const shouldFitToWidthHeight = useAppSelector(
(state: RootState) => state.options.shouldFitToWidthHeight (state: RootState) => state.generation.shouldFitToWidthHeight
); );
const handleChangeFit = (e: ChangeEvent<HTMLInputElement>) => const handleChangeFit = (e: ChangeEvent<HTMLInputElement>) =>
@ -19,7 +19,7 @@ export default function ImageFit() {
return ( return (
<IAISwitch <IAISwitch
label={t('options:imageFit')} label={t('parameters:imageFit')}
isChecked={shouldFitToWidthHeight} isChecked={shouldFitToWidthHeight}
onChange={handleChangeFit} onChange={handleChangeFit}
/> />

View File

@ -2,7 +2,7 @@ import React from 'react';
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISlider from 'common/components/IAISlider'; import IAISlider from 'common/components/IAISlider';
import { setImg2imgStrength } from 'features/options/store/optionsSlice'; import { setImg2imgStrength } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
interface ImageToImageStrengthProps { interface ImageToImageStrengthProps {
@ -12,9 +12,9 @@ interface ImageToImageStrengthProps {
export default function ImageToImageStrength(props: ImageToImageStrengthProps) { export default function ImageToImageStrength(props: ImageToImageStrengthProps) {
const { t } = useTranslation(); const { t } = useTranslation();
const { label = `${t('options:strength')}`, styleClass } = props; const { label = `${t('parameters:strength')}`, styleClass } = props;
const img2imgStrength = useAppSelector( const img2imgStrength = useAppSelector(
(state: RootState) => state.options.img2imgStrength (state: RootState) => state.generation.img2imgStrength
); );
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();

View File

@ -1,21 +1,31 @@
import { Flex } from '@chakra-ui/react'; import { Flex } from '@chakra-ui/react';
import { ChangeEvent } from 'react'; import { createSelector } from '@reduxjs/toolkit';
import type { RootState } from 'app/store'; import type { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISlider from 'common/components/IAISlider';
import IAISwitch from 'common/components/IAISwitch'; import IAISwitch from 'common/components/IAISwitch';
import { postprocessingSelector } from 'features/parameters/store/postprocessingSelectors';
import { import {
setHiresFix, setHiresFix,
setHiresStrength, setHiresStrength,
} from 'features/options/store/optionsSlice'; } from 'features/parameters/store/postprocessingSlice';
import { isEqual } from 'lodash';
import { ChangeEvent } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import IAISlider from 'common/components/IAISlider';
function HighResStrength() { const hiresStrengthSelector = createSelector(
const hiresFix = useAppSelector((state: RootState) => state.options.hiresFix); [postprocessingSelector],
const hiresStrength = useAppSelector( ({ hiresFix, hiresStrength }) => ({ hiresFix, hiresStrength }),
(state: RootState) => state.options.hiresStrength {
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
); );
const HiresStrength = () => {
const { hiresFix, hiresStrength } = useAppSelector(hiresStrengthSelector);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
@ -30,7 +40,7 @@ function HighResStrength() {
return ( return (
<IAISlider <IAISlider
label={t('options:hiresStrength')} label={t('parameters:hiresStrength')}
step={0.01} step={0.01}
min={0.01} min={0.01}
max={0.99} max={0.99}
@ -47,15 +57,17 @@ function HighResStrength() {
isResetDisabled={!hiresFix} isResetDisabled={!hiresFix}
/> />
); );
} };
/** /**
* Hires Fix Toggle * Hires Fix Toggle
*/ */
const HiresOptions = () => { const HiresSettings = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const hiresFix = useAppSelector((state: RootState) => state.options.hiresFix); const hiresFix = useAppSelector(
(state: RootState) => state.postprocessing.hiresFix
);
const { t } = useTranslation(); const { t } = useTranslation();
@ -65,14 +77,14 @@ const HiresOptions = () => {
return ( return (
<Flex gap={2} direction={'column'}> <Flex gap={2} direction={'column'}>
<IAISwitch <IAISwitch
label={t('options:hiresOptim')} label={t('parameters:hiresOptim')}
fontSize={'md'} fontSize={'md'}
isChecked={hiresFix} isChecked={hiresFix}
onChange={handleChangeHiresFix} onChange={handleChangeHiresFix}
/> />
<HighResStrength /> <HiresStrength />
</Flex> </Flex>
); );
}; };
export default HiresOptions; export default HiresSettings;

View File

@ -0,0 +1,12 @@
import { Flex } from '@chakra-ui/react';
import SeamlessSettings from './SeamlessSettings';
const ImageToImageOutputSettings = () => {
return (
<Flex gap={2} direction={'column'}>
<SeamlessSettings />
</Flex>
);
};
export default ImageToImageOutputSettings;

View File

@ -0,0 +1,14 @@
import { Flex } from '@chakra-ui/react';
import HiresSettings from './HiresSettings';
import SeamlessSettings from './SeamlessSettings';
const OutputSettings = () => {
return (
<Flex gap={2} direction={'column'}>
<SeamlessSettings />
<HiresSettings />
</Flex>
);
};
export default OutputSettings;

View File

@ -3,16 +3,16 @@ import { ChangeEvent } from 'react';
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISwitch from 'common/components/IAISwitch'; import IAISwitch from 'common/components/IAISwitch';
import { setSeamless } from 'features/options/store/optionsSlice'; import { setSeamless } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
/** /**
* Seamless tiling toggle * Seamless tiling toggle
*/ */
const SeamlessOptions = () => { const SeamlessSettings = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const seamless = useAppSelector((state: RootState) => state.options.seamless); const seamless = useAppSelector((state: RootState) => state.generation.seamless);
const handleChangeSeamless = (e: ChangeEvent<HTMLInputElement>) => const handleChangeSeamless = (e: ChangeEvent<HTMLInputElement>) =>
dispatch(setSeamless(e.target.checked)); dispatch(setSeamless(e.target.checked));
@ -22,7 +22,7 @@ const SeamlessOptions = () => {
return ( return (
<Flex gap={2} direction={'column'}> <Flex gap={2} direction={'column'}>
<IAISwitch <IAISwitch
label={t('options:seamlessTiling')} label={t('parameters:seamlessTiling')}
fontSize={'md'} fontSize={'md'}
isChecked={seamless} isChecked={seamless}
onChange={handleChangeSeamless} onChange={handleChangeSeamless}
@ -31,4 +31,4 @@ const SeamlessOptions = () => {
); );
}; };
export default SeamlessOptions; export default SeamlessSettings;

View File

@ -2,19 +2,19 @@ import React from 'react';
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAINumberInput from 'common/components/IAINumberInput'; import IAINumberInput from 'common/components/IAINumberInput';
import { setPerlin } from 'features/options/store/optionsSlice'; import { setPerlin } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
export default function Perlin() { export default function Perlin() {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const perlin = useAppSelector((state: RootState) => state.options.perlin); const perlin = useAppSelector((state: RootState) => state.generation.perlin);
const { t } = useTranslation(); const { t } = useTranslation();
const handleChangePerlin = (v: number) => dispatch(setPerlin(v)); const handleChangePerlin = (v: number) => dispatch(setPerlin(v));
return ( return (
<IAINumberInput <IAINumberInput
label={t('options:perlinNoise')} label={t('parameters:perlinNoise')}
min={0} min={0}
max={1} max={1}
step={0.05} step={0.05}

View File

@ -4,7 +4,7 @@ import React from 'react';
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISwitch from 'common/components/IAISwitch'; import IAISwitch from 'common/components/IAISwitch';
import { setShouldRandomizeSeed } from 'features/options/store/optionsSlice'; import { setShouldRandomizeSeed } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
export default function RandomizeSeed() { export default function RandomizeSeed() {
@ -12,7 +12,7 @@ export default function RandomizeSeed() {
const { t } = useTranslation(); const { t } = useTranslation();
const shouldRandomizeSeed = useAppSelector( const shouldRandomizeSeed = useAppSelector(
(state: RootState) => state.options.shouldRandomizeSeed (state: RootState) => state.generation.shouldRandomizeSeed
); );
const handleChangeShouldRandomizeSeed = (e: ChangeEvent<HTMLInputElement>) => const handleChangeShouldRandomizeSeed = (e: ChangeEvent<HTMLInputElement>) =>
@ -20,7 +20,7 @@ export default function RandomizeSeed() {
return ( return (
<IAISwitch <IAISwitch
label={t('options:randomizeSeed')} label={t('parameters:randomizeSeed')}
isChecked={shouldRandomizeSeed} isChecked={shouldRandomizeSeed}
onChange={handleChangeShouldRandomizeSeed} onChange={handleChangeShouldRandomizeSeed}
/> />

View File

@ -3,16 +3,16 @@ import { NUMPY_RAND_MAX, NUMPY_RAND_MIN } from 'app/constants';
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAINumberInput from 'common/components/IAINumberInput'; import IAINumberInput from 'common/components/IAINumberInput';
import { setSeed } from 'features/options/store/optionsSlice'; import { setSeed } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
export default function Seed() { export default function Seed() {
const seed = useAppSelector((state: RootState) => state.options.seed); const seed = useAppSelector((state: RootState) => state.generation.seed);
const shouldRandomizeSeed = useAppSelector( const shouldRandomizeSeed = useAppSelector(
(state: RootState) => state.options.shouldRandomizeSeed (state: RootState) => state.generation.shouldRandomizeSeed
); );
const shouldGenerateVariations = useAppSelector( const shouldGenerateVariations = useAppSelector(
(state: RootState) => state.options.shouldGenerateVariations (state: RootState) => state.generation.shouldGenerateVariations
); );
const { t } = useTranslation(); const { t } = useTranslation();
@ -23,7 +23,7 @@ export default function Seed() {
return ( return (
<IAINumberInput <IAINumberInput
label={t('options:seed')} label={t('parameters:seed')}
step={1} step={1}
precision={0} precision={0}
flexGrow={1} flexGrow={1}

View File

@ -8,7 +8,7 @@ import Perlin from './Perlin';
/** /**
* Seed & variation options. Includes iteration, seed, seed randomization, variation options. * Seed & variation options. Includes iteration, seed, seed randomization, variation options.
*/ */
const SeedOptions = () => { const SeedSettings = () => {
return ( return (
<Flex gap={2} direction={'column'}> <Flex gap={2} direction={'column'}>
<RandomizeSeed /> <RandomizeSeed />
@ -26,4 +26,4 @@ const SeedOptions = () => {
); );
}; };
export default SeedOptions; export default SeedSettings;

View File

@ -4,13 +4,13 @@ import { NUMPY_RAND_MAX, NUMPY_RAND_MIN } from 'app/constants';
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import randomInt from 'common/util/randomInt'; import randomInt from 'common/util/randomInt';
import { setSeed } from 'features/options/store/optionsSlice'; import { setSeed } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
export default function ShuffleSeed() { export default function ShuffleSeed() {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const shouldRandomizeSeed = useAppSelector( const shouldRandomizeSeed = useAppSelector(
(state: RootState) => state.options.shouldRandomizeSeed (state: RootState) => state.generation.shouldRandomizeSeed
); );
const { t } = useTranslation(); const { t } = useTranslation();
@ -24,7 +24,7 @@ export default function ShuffleSeed() {
onClick={handleClickRandomizeSeed} onClick={handleClickRandomizeSeed}
padding="0 1.5rem" padding="0 1.5rem"
> >
<p>{t('options:shuffle')}</p> <p>{t('parameters:shuffle')}</p>
</Button> </Button>
); );
} }

View File

@ -2,13 +2,13 @@ import React from 'react';
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAINumberInput from 'common/components/IAINumberInput'; import IAINumberInput from 'common/components/IAINumberInput';
import { setThreshold } from 'features/options/store/optionsSlice'; import { setThreshold } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
export default function Threshold() { export default function Threshold() {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const threshold = useAppSelector( const threshold = useAppSelector(
(state: RootState) => state.options.threshold (state: RootState) => state.generation.threshold
); );
const { t } = useTranslation(); const { t } = useTranslation();
@ -16,7 +16,7 @@ export default function Threshold() {
return ( return (
<IAINumberInput <IAINumberInput
label={t('options:noiseThreshold')} label={t('parameters:noiseThreshold')}
min={0} min={0}
max={1000} max={1000}
step={0.1} step={0.1}

View File

@ -1,4 +1,4 @@
.upscale-options { .upscale-settings {
display: grid; display: grid;
grid-template-columns: auto 1fr; grid-template-columns: auto 1fr;
column-gap: 1rem; column-gap: 1rem;

View File

@ -1,42 +1,29 @@
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import { UpscalingLevel } from 'features/parameters/store/postprocessingSlice';
import { import {
setUpscalingLevel, setUpscalingLevel,
setUpscalingStrength, setUpscalingStrength,
UpscalingLevel, } from 'features/parameters/store/postprocessingSlice';
OptionsState,
} from 'features/options/store/optionsSlice';
import { UPSCALING_LEVELS } from 'app/constants'; import { UPSCALING_LEVELS } from 'app/constants';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
import { SystemState } from 'features/system/store/systemSlice';
import { ChangeEvent } from 'react'; import { ChangeEvent } from 'react';
import IAINumberInput from 'common/components/IAINumberInput'; import IAINumberInput from 'common/components/IAINumberInput';
import IAISelect from 'common/components/IAISelect'; import IAISelect from 'common/components/IAISelect';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { postprocessingSelector } from 'features/parameters/store/postprocessingSelectors';
import { systemSelector } from 'features/system/store/systemSelectors';
const optionsSelector = createSelector( const parametersSelector = createSelector(
(state: RootState) => state.options, [postprocessingSelector, systemSelector],
(options: OptionsState) => {
return {
upscalingLevel: options.upscalingLevel,
upscalingStrength: options.upscalingStrength,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);
const systemSelector = createSelector( ({ upscalingLevel, upscalingStrength }, { isESRGANAvailable }) => {
(state: RootState) => state.system,
(system: SystemState) => {
return { return {
isESRGANAvailable: system.isESRGANAvailable, upscalingLevel,
upscalingStrength,
isESRGANAvailable,
}; };
}, },
{ {
@ -49,11 +36,10 @@ const systemSelector = createSelector(
/** /**
* Displays upscaling/ESRGAN options (level and strength). * Displays upscaling/ESRGAN options (level and strength).
*/ */
const UpscaleOptions = () => { const UpscaleSettings = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { upscalingLevel, upscalingStrength } = useAppSelector(optionsSelector); const { upscalingLevel, upscalingStrength, isESRGANAvailable } =
useAppSelector(parametersSelector);
const { isESRGANAvailable } = useAppSelector(systemSelector);
const { t } = useTranslation(); const { t } = useTranslation();
@ -63,17 +49,17 @@ const UpscaleOptions = () => {
const handleChangeStrength = (v: number) => dispatch(setUpscalingStrength(v)); const handleChangeStrength = (v: number) => dispatch(setUpscalingStrength(v));
return ( return (
<div className="upscale-options"> <div className="upscale-settings">
<IAISelect <IAISelect
isDisabled={!isESRGANAvailable} isDisabled={!isESRGANAvailable}
label={t('options:scale')} label={t('parameters:scale')}
value={upscalingLevel} value={upscalingLevel}
onChange={handleChangeLevel} onChange={handleChangeLevel}
validValues={UPSCALING_LEVELS} validValues={UPSCALING_LEVELS}
/> />
<IAINumberInput <IAINumberInput
isDisabled={!isESRGANAvailable} isDisabled={!isESRGANAvailable}
label={t('options:strength')} label={t('parameters:strength')}
step={0.05} step={0.05}
min={0} min={0}
max={1} max={1}
@ -85,4 +71,4 @@ const UpscaleOptions = () => {
); );
}; };
export default UpscaleOptions; export default UpscaleSettings;

View File

@ -2,7 +2,7 @@ import { ChangeEvent } from 'react';
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISwitch from 'common/components/IAISwitch'; import IAISwitch from 'common/components/IAISwitch';
import { setShouldRunESRGAN } from 'features/options/store/optionsSlice'; import { setShouldRunESRGAN } from 'features/parameters/store/postprocessingSlice';
export default function UpscaleToggle() { export default function UpscaleToggle() {
const isESRGANAvailable = useAppSelector( const isESRGANAvailable = useAppSelector(
@ -10,7 +10,7 @@ export default function UpscaleToggle() {
); );
const shouldRunESRGAN = useAppSelector( const shouldRunESRGAN = useAppSelector(
(state: RootState) => state.options.shouldRunESRGAN (state: RootState) => state.postprocessing.shouldRunESRGAN
); );
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();

View File

@ -2,11 +2,11 @@ import React, { ChangeEvent } from 'react';
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISwitch from 'common/components/IAISwitch'; import IAISwitch from 'common/components/IAISwitch';
import { setShouldGenerateVariations } from 'features/options/store/optionsSlice'; import { setShouldGenerateVariations } from 'features/parameters/store/generationSlice';
export default function GenerateVariationsToggle() { export default function GenerateVariationsToggle() {
const shouldGenerateVariations = useAppSelector( const shouldGenerateVariations = useAppSelector(
(state: RootState) => state.options.shouldGenerateVariations (state: RootState) => state.generation.shouldGenerateVariations
); );
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();

View File

@ -3,16 +3,16 @@ import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAIInput from 'common/components/IAIInput'; import IAIInput from 'common/components/IAIInput';
import { validateSeedWeights } from 'common/util/seedWeightPairs'; import { validateSeedWeights } from 'common/util/seedWeightPairs';
import { setSeedWeights } from 'features/options/store/optionsSlice'; import { setSeedWeights } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
export default function SeedWeights() { export default function SeedWeights() {
const seedWeights = useAppSelector( const seedWeights = useAppSelector(
(state: RootState) => state.options.seedWeights (state: RootState) => state.generation.seedWeights
); );
const shouldGenerateVariations = useAppSelector( const shouldGenerateVariations = useAppSelector(
(state: RootState) => state.options.shouldGenerateVariations (state: RootState) => state.generation.shouldGenerateVariations
); );
const { t } = useTranslation(); const { t } = useTranslation();
@ -24,7 +24,7 @@ export default function SeedWeights() {
return ( return (
<IAIInput <IAIInput
label={t('options:seedWeights')} label={t('parameters:seedWeights')}
value={seedWeights} value={seedWeights}
isInvalid={ isInvalid={
shouldGenerateVariations && shouldGenerateVariations &&

View File

@ -2,16 +2,16 @@ import React from 'react';
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAINumberInput from 'common/components/IAINumberInput'; import IAINumberInput from 'common/components/IAINumberInput';
import { setVariationAmount } from 'features/options/store/optionsSlice'; import { setVariationAmount } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
export default function VariationAmount() { export default function VariationAmount() {
const variationAmount = useAppSelector( const variationAmount = useAppSelector(
(state: RootState) => state.options.variationAmount (state: RootState) => state.generation.variationAmount
); );
const shouldGenerateVariations = useAppSelector( const shouldGenerateVariations = useAppSelector(
(state: RootState) => state.options.shouldGenerateVariations (state: RootState) => state.generation.shouldGenerateVariations
); );
const { t } = useTranslation(); const { t } = useTranslation();
@ -22,7 +22,7 @@ export default function VariationAmount() {
return ( return (
<IAINumberInput <IAINumberInput
label={t('options:variationAmount')} label={t('parameters:variationAmount')}
value={variationAmount} value={variationAmount}
step={0.01} step={0.01}
min={0} min={0}

View File

@ -5,7 +5,7 @@ import VariationAmount from './VariationAmount';
/** /**
* Seed & variation options. Includes iteration, seed, seed randomization, variation options. * Seed & variation options. Includes iteration, seed, seed randomization, variation options.
*/ */
const VariationsOptions = () => { const VariationsSettings = () => {
return ( return (
<Flex gap={2} direction={'column'}> <Flex gap={2} direction={'column'}>
<VariationAmount /> <VariationAmount />
@ -14,4 +14,4 @@ const VariationsOptions = () => {
); );
}; };
export default VariationsOptions; export default VariationsSettings;

View File

@ -2,26 +2,26 @@ import React from 'react';
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAINumberInput from 'common/components/IAINumberInput'; import IAINumberInput from 'common/components/IAINumberInput';
import { setCfgScale } from 'features/options/store/optionsSlice'; import { setCfgScale } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
export default function MainCFGScale() { export default function MainCFGScale() {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const cfgScale = useAppSelector((state: RootState) => state.options.cfgScale); const cfgScale = useAppSelector((state: RootState) => state.generation.cfgScale);
const { t } = useTranslation(); const { t } = useTranslation();
const handleChangeCfgScale = (v: number) => dispatch(setCfgScale(v)); const handleChangeCfgScale = (v: number) => dispatch(setCfgScale(v));
return ( return (
<IAINumberInput <IAINumberInput
label={t('options:cfgScale')} label={t('parameters:cfgScale')}
step={0.5} step={0.5}
min={1.01} min={1.01}
max={200} max={200}
onChange={handleChangeCfgScale} onChange={handleChangeCfgScale}
value={cfgScale} value={cfgScale}
width="auto" width="auto"
styleClass="main-option-block" styleClass="main-settings-block"
textAlign="center" textAlign="center"
isInteger={false} isInteger={false}
/> />

View File

@ -4,12 +4,12 @@ import { HEIGHTS } from 'app/constants';
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISelect from 'common/components/IAISelect'; import IAISelect from 'common/components/IAISelect';
import { activeTabNameSelector } from 'features/options/store/optionsSelectors'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { setHeight } from 'features/options/store/optionsSlice'; import { setHeight } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
export default function MainHeight() { export default function MainHeight() {
const height = useAppSelector((state: RootState) => state.options.height); const height = useAppSelector((state: RootState) => state.generation.height);
const activeTabName = useAppSelector(activeTabNameSelector); const activeTabName = useAppSelector(activeTabNameSelector);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
@ -20,12 +20,12 @@ export default function MainHeight() {
return ( return (
<IAISelect <IAISelect
isDisabled={activeTabName === 'unifiedCanvas'} isDisabled={activeTabName === 'unifiedCanvas'}
label={t('options:height')} label={t('parameters:height')}
value={height} value={height}
flexGrow={1} flexGrow={1}
onChange={handleChangeHeight} onChange={handleChangeHeight}
validValues={HEIGHTS} validValues={HEIGHTS}
styleClass="main-option-block" styleClass="main-settings-block"
/> />
); );
} }

View File

@ -5,15 +5,15 @@ import type { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAINumberInput from 'common/components/IAINumberInput'; import IAINumberInput from 'common/components/IAINumberInput';
import { import {
OptionsState, GenerationState,
setIterations, setIterations,
} from 'features/options/store/optionsSlice'; } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const mainIterationsSelector = createSelector( const mainIterationsSelector = createSelector(
[(state: RootState) => state.options], [(state: RootState) => state.generation],
(options: OptionsState) => { (parameters: GenerationState) => {
const { iterations } = options; const { iterations } = parameters;
return { return {
iterations, iterations,
@ -35,7 +35,7 @@ export default function MainIterations() {
return ( return (
<IAINumberInput <IAINumberInput
label={t('options:images')} label={t('parameters:images')}
step={1} step={1}
min={1} min={1}
max={9999} max={9999}
@ -43,7 +43,7 @@ export default function MainIterations() {
value={iterations} value={iterations}
width="auto" width="auto"
labelFontSize={0.5} labelFontSize={0.5}
styleClass="main-option-block" styleClass="main-settings-block"
textAlign="center" textAlign="center"
/> />
); );

View File

@ -1,23 +1,23 @@
@use '../../../../styles/Mixins/' as *; @use '../../../../styles/Mixins/' as *;
.main-options { .main-settings {
display: grid; display: grid;
row-gap: 1rem; row-gap: 1rem;
} }
.main-options-list { .main-settings-list {
display: grid; display: grid;
row-gap: 1rem; row-gap: 1rem;
} }
.main-options-row { .main-settings-row {
display: grid; display: grid;
grid-template-columns: repeat(3, auto); grid-template-columns: repeat(3, auto);
column-gap: 0.5rem; column-gap: 0.5rem;
max-width: $options-bar-max-width; max-width: $options-bar-max-width;
} }
.main-option-block { .main-settings-block {
border-radius: 0.5rem; border-radius: 0.5rem;
display: grid !important; display: grid !important;
grid-template-columns: auto !important; grid-template-columns: auto !important;
@ -33,10 +33,3 @@
margin: 0; margin: 0;
} }
} }
.advanced-options-checkbox {
background-color: var(--background-color-secondary);
padding: 0.5rem 1rem;
border-radius: 0.4rem;
font-weight: bold;
}

View File

@ -7,16 +7,16 @@ import MainWidth from './MainWidth';
export const inputWidth = 'auto'; export const inputWidth = 'auto';
export default function MainOptions() { export default function MainSettings() {
return ( return (
<div className="main-options"> <div className="main-settings">
<div className="main-options-list"> <div className="main-settings-list">
<div className="main-options-row"> <div className="main-settings-row">
<MainIterations /> <MainIterations />
<MainSteps /> <MainSteps />
<MainCFGScale /> <MainCFGScale />
</div> </div>
<div className="main-options-row"> <div className="main-settings-row">
<MainWidth /> <MainWidth />
<MainHeight /> <MainHeight />
<MainSampler /> <MainSampler />

View File

@ -1,15 +1,16 @@
import React, { ChangeEvent } from 'react';
import { DIFFUSERS_SAMPLERS, SAMPLERS } from 'app/constants'; import { DIFFUSERS_SAMPLERS, SAMPLERS } from 'app/constants';
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISelect from 'common/components/IAISelect'; import IAISelect from 'common/components/IAISelect';
import { setSampler } from 'features/options/store/optionsSlice'; import { setSampler } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next';
import _ from 'lodash';
import { activeModelSelector } from 'features/system/store/systemSelectors'; import { activeModelSelector } from 'features/system/store/systemSelectors';
import { ChangeEvent } from 'react';
import { useTranslation } from 'react-i18next';
export default function MainSampler() { export default function MainSampler() {
const sampler = useAppSelector((state: RootState) => state.options.sampler); const sampler = useAppSelector(
(state: RootState) => state.generation.sampler
);
const activeModel = useAppSelector(activeModelSelector); const activeModel = useAppSelector(activeModelSelector);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
@ -19,7 +20,7 @@ export default function MainSampler() {
return ( return (
<IAISelect <IAISelect
label={t('options:sampler')} label={t('parameters:sampler')}
value={sampler} value={sampler}
onChange={handleChangeSampler} onChange={handleChangeSampler}
validValues={ validValues={

View File

@ -2,26 +2,26 @@ import React from 'react';
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAINumberInput from 'common/components/IAINumberInput'; import IAINumberInput from 'common/components/IAINumberInput';
import { setSteps } from 'features/options/store/optionsSlice'; import { setSteps } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
export default function MainSteps() { export default function MainSteps() {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const steps = useAppSelector((state: RootState) => state.options.steps); const steps = useAppSelector((state: RootState) => state.generation.steps);
const { t } = useTranslation(); const { t } = useTranslation();
const handleChangeSteps = (v: number) => dispatch(setSteps(v)); const handleChangeSteps = (v: number) => dispatch(setSteps(v));
return ( return (
<IAINumberInput <IAINumberInput
label={t('options:steps')} label={t('parameters:steps')}
min={1} min={1}
max={9999} max={9999}
step={1} step={1}
onChange={handleChangeSteps} onChange={handleChangeSteps}
value={steps} value={steps}
width="auto" width="auto"
styleClass="main-option-block" styleClass="main-settings-block"
textAlign="center" textAlign="center"
/> />
); );

View File

@ -3,12 +3,12 @@ import { WIDTHS } from 'app/constants';
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISelect from 'common/components/IAISelect'; import IAISelect from 'common/components/IAISelect';
import { activeTabNameSelector } from 'features/options/store/optionsSelectors'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { setWidth } from 'features/options/store/optionsSlice'; import { setWidth } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
export default function MainWidth() { export default function MainWidth() {
const width = useAppSelector((state: RootState) => state.options.width); const width = useAppSelector((state: RootState) => state.generation.width);
const activeTabName = useAppSelector(activeTabNameSelector); const activeTabName = useAppSelector(activeTabNameSelector);
const { t } = useTranslation(); const { t } = useTranslation();
@ -20,12 +20,12 @@ export default function MainWidth() {
return ( return (
<IAISelect <IAISelect
isDisabled={activeTabName === 'unifiedCanvas'} isDisabled={activeTabName === 'unifiedCanvas'}
label={t('options:width')} label={t('parameters:width')}
value={width} value={width}
flexGrow={1} flexGrow={1}
onChange={handleChangeWidth} onChange={handleChangeWidth}
validValues={WIDTHS} validValues={WIDTHS}
styleClass="main-option-block" styleClass="main-settings-block"
/> />
); );
} }

View File

@ -7,18 +7,18 @@ import InvokeAccordionItem, {
} from './AccordionItems/InvokeAccordionItem'; } from './AccordionItems/InvokeAccordionItem';
import { ReactElement } from 'react'; import { ReactElement } from 'react';
type OptionsAccordionType = { type ParametersAccordionType = {
[optionAccordionKey: string]: InvokeAccordionItemProps; [parametersAccordionKey: string]: InvokeAccordionItemProps;
}; };
type OptionAccordionsType = { type ParametersAccordionsType = {
accordionInfo: OptionsAccordionType; accordionInfo: ParametersAccordionType;
}; };
/** /**
* Main container for generation and processing parameters. * Main container for generation and processing parameters.
*/ */
const OptionsAccordion = (props: OptionAccordionsType) => { const ParametersAccordion = (props: ParametersAccordionsType) => {
const { accordionInfo } = props; const { accordionInfo } = props;
const openAccordions = useAppSelector( const openAccordions = useAppSelector(
@ -59,11 +59,11 @@ const OptionsAccordion = (props: OptionAccordionsType) => {
allowMultiple allowMultiple
reduceMotion reduceMotion
onChange={handleChangeAccordionState} onChange={handleChangeAccordionState}
className="advanced-settings" className="advanced-parameters"
> >
{renderAccordions()} {renderAccordions()}
</Accordion> </Accordion>
); );
}; };
export default OptionsAccordion; export default ParametersAccordion;

View File

@ -1,6 +1,5 @@
import { MdCancel } from 'react-icons/md'; import { MdCancel } from 'react-icons/md';
import { cancelProcessing } from 'app/socketio/actions'; import { cancelProcessing } from 'app/socketio/actions';
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAIIconButton, { import IAIIconButton, {
IAIIconButtonProps, IAIIconButtonProps,
@ -10,9 +9,10 @@ import { createSelector } from '@reduxjs/toolkit';
import { SystemState } from 'features/system/store/systemSlice'; import { SystemState } from 'features/system/store/systemSlice';
import _ from 'lodash'; import _ from 'lodash';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { systemSelector } from 'features/system/store/systemSelectors';
const cancelButtonSelector = createSelector( const cancelButtonSelector = createSelector(
(state: RootState) => state.system, systemSelector,
(system: SystemState) => { (system: SystemState) => {
return { return {
isProcessing: system.isProcessing, isProcessing: system.isProcessing,
@ -51,8 +51,8 @@ export default function CancelButton(
return ( return (
<IAIIconButton <IAIIconButton
icon={<MdCancel />} icon={<MdCancel />}
tooltip={t('options:cancel')} tooltip={t('parameters:cancel')}
aria-label={t('options:cancel')} aria-label={t('parameters:cancel')}
isDisabled={!isConnected || !isProcessing || !isCancelable} isDisabled={!isConnected || !isProcessing || !isCancelable}
onClick={handleClickCancel} onClick={handleClickCancel}
styleClass="cancel-btn" styleClass="cancel-btn"

View File

@ -7,7 +7,7 @@ import IAIButton, { IAIButtonProps } from 'common/components/IAIButton';
import IAIIconButton, { import IAIIconButton, {
IAIIconButtonProps, IAIIconButtonProps,
} from 'common/components/IAIIconButton'; } from 'common/components/IAIIconButton';
import { activeTabNameSelector } from 'features/options/store/optionsSelectors'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
interface InvokeButton interface InvokeButton
@ -44,19 +44,19 @@ export default function InvokeButton(props: InvokeButton) {
<div style={{ flexGrow: 4 }}> <div style={{ flexGrow: 4 }}>
{iconButton ? ( {iconButton ? (
<IAIIconButton <IAIIconButton
aria-label={t('options:invoke')} aria-label={t('parameters:invoke')}
type="submit" type="submit"
icon={<FaPlay />} icon={<FaPlay />}
isDisabled={!isReady} isDisabled={!isReady}
onClick={handleClickGenerate} onClick={handleClickGenerate}
className="invoke-btn" className="invoke-btn"
tooltip={t('options:invoke')} tooltip={t('parameters:invoke')}
tooltipProps={{ placement: 'bottom' }} tooltipProps={{ placement: 'bottom' }}
{...rest} {...rest}
/> />
) : ( ) : (
<IAIButton <IAIButton
aria-label={t('options:invoke')} aria-label={t('parameters:invoke')}
type="submit" type="submit"
isDisabled={!isReady} isDisabled={!isReady}
onClick={handleClickGenerate} onClick={handleClickGenerate}

View File

@ -1,17 +1,14 @@
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { FaRecycle } from 'react-icons/fa'; import { FaRecycle } from 'react-icons/fa';
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAIIconButton from 'common/components/IAIIconButton'; import IAIIconButton from 'common/components/IAIIconButton';
import { import { setShouldLoopback } from 'features/parameters/store/postprocessingSlice';
OptionsState,
setShouldLoopback,
} from 'features/options/store/optionsSlice';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { postprocessingSelector } from 'features/parameters/store/postprocessingSelectors';
const loopbackSelector = createSelector( const loopbackSelector = createSelector(
(state: RootState) => state.options, postprocessingSelector,
(options: OptionsState) => options.shouldLoopback ({ shouldLoopback }) => shouldLoopback
); );
const LoopbackButton = () => { const LoopbackButton = () => {
@ -22,8 +19,8 @@ const LoopbackButton = () => {
return ( return (
<IAIIconButton <IAIIconButton
aria-label={t('options:toggleLoopback')} aria-label={t('parameters:toggleLoopback')}
tooltip={t('options:toggleLoopback')} tooltip={t('parameters:toggleLoopback')}
styleClass="loopback-btn" styleClass="loopback-btn"
asCheckbox={true} asCheckbox={true}
isChecked={shouldLoopback} isChecked={shouldLoopback}

View File

@ -2,7 +2,7 @@ import InvokeButton from './InvokeButton';
import CancelButton from './CancelButton'; import CancelButton from './CancelButton';
import LoopbackButton from './Loopback'; import LoopbackButton from './Loopback';
import { useAppSelector } from 'app/storeHooks'; import { useAppSelector } from 'app/storeHooks';
import { activeTabNameSelector } from 'features/options/store/optionsSelectors'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
/** /**
* Buttons to start and cancel image generation. * Buttons to start and cancel image generation.

View File

@ -1,12 +1,12 @@
import { FormControl, Textarea } from '@chakra-ui/react'; import { FormControl, Textarea } from '@chakra-ui/react';
import type { RootState } from 'app/store'; import type { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import { setNegativePrompt } from 'features/options/store/optionsSlice'; import { setNegativePrompt } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
export function NegativePromptInput() { const NegativePromptInput = () => {
const negativePrompt = useAppSelector( const negativePrompt = useAppSelector(
(state: RootState) => state.options.negativePrompt (state: RootState) => state.generation.negativePrompt
); );
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
@ -20,7 +20,7 @@ export function NegativePromptInput() {
value={negativePrompt} value={negativePrompt}
onChange={(e) => dispatch(setNegativePrompt(e.target.value))} onChange={(e) => dispatch(setNegativePrompt(e.target.value))}
background="var(--prompt-bg-color)" background="var(--prompt-bg-color)"
placeholder={t('options:negativePrompts')} placeholder={t('parameters:negativePrompts')}
_placeholder={{ fontSize: '0.8rem' }} _placeholder={{ fontSize: '0.8rem' }}
borderColor="var(--border-color)" borderColor="var(--border-color)"
_hover={{ _hover={{
@ -35,4 +35,6 @@ export function NegativePromptInput() {
/> />
</FormControl> </FormControl>
); );
} };
export default NegativePromptInput;

Some files were not shown because too many files have changed in this diff Show More