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 blessedcoolant
parent ffe0e81ec9
commit d74c4009cb
179 changed files with 7463 additions and 1165 deletions

View File

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

View File

@@ -3,7 +3,7 @@ import { useHotkeys } from 'react-hotkeys-hook';
import { FaRedo } from 'react-icons/fa';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
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 _ from 'lodash';

View File

@@ -6,7 +6,7 @@ import IAIIconButton from 'common/components/IAIIconButton';
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
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 { systemSelector } from 'features/system/store/systemSelectors';
import { useTranslation } from 'react-i18next';

View File

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

View File

@@ -1,6 +1,6 @@
import { createSelector } from '@reduxjs/toolkit';
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 { KonvaEventObject } from 'konva/lib/Node';
import _ from 'lodash';

View File

@@ -1,6 +1,6 @@
import { createSelector } from '@reduxjs/toolkit';
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 { Vector2d } from 'konva/lib/types';
import _ from 'lodash';

View File

@@ -1,6 +1,6 @@
import { createSelector } from '@reduxjs/toolkit';
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 _ from 'lodash';
import { MutableRefObject, useCallback } from 'react';

View File

@@ -1,6 +1,6 @@
import { createSelector } from '@reduxjs/toolkit';
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 { CanvasImage, CanvasState, isCanvasBaseImage } from './canvasTypes';

View File

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

View File

@@ -1,23 +1,21 @@
import { RootState } from 'app/store';
import { useAppSelector } from 'app/storeHooks';
import CurrentImageButtons from './CurrentImageButtons';
import { MdPhoto } from 'react-icons/md';
import CurrentImagePreview from './CurrentImagePreview';
import { GalleryState } from 'features/gallery/store/gallerySlice';
import { OptionsState } from 'features/options/store/optionsSlice';
import _ from 'lodash';
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(
[
(state: RootState) => state.gallery,
(state: RootState) => state.options,
activeTabNameSelector,
],
(gallery: GalleryState, options: OptionsState, activeTabName) => {
[gallerySelector, uiSelector, activeTabNameSelector],
(gallery: GalleryState, ui, activeTabName) => {
const { currentImage, intermediateImage } = gallery;
const { shouldShowImageDetails } = options;
const { shouldShowImageDetails } = ui;
return {
activeTabName,

View File

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

View File

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

View File

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

View File

@@ -32,7 +32,7 @@ import { setShouldShowGallery } from 'features/gallery/store/gallerySlice';
import { ButtonGroup } from '@chakra-ui/react';
import { CSSTransition } from 'react-transition-group';
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 IAIPopover from 'common/components/IAIPopover';
import IAISlider from 'common/components/IAISlider';
@@ -41,7 +41,7 @@ import IAICheckbox from 'common/components/IAICheckbox';
import { setDoesCanvasNeedScaling } from 'features/canvas/store/canvasSlice';
import _ from 'lodash';
import IAIButton from 'common/components/IAIButton';
import { InvokeTabName } from 'features/tabs/tabMap';
import { InvokeTabName } from 'features/ui/store/tabMap';
import { useTranslation } from 'react-i18next';
const GALLERY_SHOW_BUTTONS_MIN_WIDTH = 320;
@@ -81,13 +81,13 @@ export default function ImageGallery() {
shouldAutoSwitchToNewImages,
areMoreImagesAvailable,
galleryWidth,
isLightBoxOpen,
isLightboxOpen,
isStaging,
shouldEnableResize,
shouldUseSingleGalleryColumn,
} = useAppSelector(imageGallerySelector);
const { galleryMinWidth, galleryMaxWidth } = isLightBoxOpen
const { galleryMinWidth, galleryMaxWidth } = isLightboxOpen
? {
galleryMinWidth: 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 {
setCfgScale,
setFacetoolStrength,
setCodeformerFidelity,
setFacetoolType,
setHeight,
setHiresFix,
setImg2imgStrength,
setMaskPath,
setPrompt,
@@ -28,14 +24,20 @@ import {
setSeedWeights,
setShouldFitToWidthHeight,
setSteps,
setUpscalingLevel,
setUpscalingStrength,
setWidth,
setInitialImage,
setShouldShowImageDetails,
setThreshold,
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 { seedWeightsToString } from 'common/util/seedWeightPairs';
import { FaCopy } from 'react-icons/fa';

View File

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

View File

@@ -1,20 +1,17 @@
import { createSelector } from '@reduxjs/toolkit';
import { RootState } from 'app/store';
import { activeTabNameSelector } from 'features/options/store/optionsSelectors';
import { OptionsState } from 'features/options/store/optionsSlice';
import { SystemState } from 'features/system/store/systemSlice';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { GalleryState } from './gallerySlice';
import _ from 'lodash';
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(
[
(state: RootState) => state.gallery,
(state: RootState) => state.options,
isStagingSelector,
activeTabNameSelector,
],
(gallery: GalleryState, options: OptionsState, isStaging, activeTabName) => {
[gallerySelector, lightboxSelector, isStagingSelector, activeTabNameSelector],
(gallery: GalleryState, lightbox, isStaging, activeTabName) => {
const {
categories,
currentCategory,
@@ -30,7 +27,7 @@ export const imageGallerySelector = createSelector(
shouldUseSingleGalleryColumn,
} = gallery;
const { isLightBoxOpen } = options;
const { isLightboxOpen } = lightbox;
return {
currentImageUuid,
@@ -50,10 +47,10 @@ export const imageGallerySelector = createSelector(
categories[currentCategory].areMoreImagesAvailable,
currentCategory,
galleryWidth,
isLightBoxOpen,
isLightboxOpen,
isStaging,
shouldEnableResize:
isLightBoxOpen ||
isLightboxOpen ||
(activeTabName === 'unifiedCanvas' && shouldPinGallery)
? false
: true,
@@ -68,25 +65,15 @@ export const imageGallerySelector = createSelector(
);
export const hoverableImageSelector = createSelector(
[
(state: RootState) => state.options,
(state: RootState) => state.gallery,
(state: RootState) => state.system,
activeTabNameSelector,
],
(
options: OptionsState,
gallery: GalleryState,
system: SystemState,
activeTabName
) => {
[gallerySelector, systemSelector, lightboxSelector, activeTabNameSelector],
(gallery: GalleryState, system, lightbox, activeTabName) => {
return {
mayDeleteImage: system.isConnected && !system.isProcessing,
galleryImageObjectFit: gallery.galleryImageObjectFit,
galleryImageMinimumWidth: gallery.galleryImageMinimumWidth,
shouldUseSingleGalleryColumn: gallery.shouldUseSingleGalleryColumn,
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 * as InvokeAI from 'app/invokeai';
import { IRect } from 'konva/lib/types';
import { InvokeTabName } from 'features/tabs/tabMap';
import { InvokeTabName } from 'features/ui/store/tabMap';
export type GalleryCategory = 'user' | 'result';
@@ -241,6 +241,7 @@ export const gallerySlice = createSlice({
setShouldShowGallery: (state, action: PayloadAction<boolean>) => {
state.shouldShowGallery = action.payload;
},
setGalleryScrollPosition: (state, action: PayloadAction<number>) => {
state.galleryScrollPosition = action.payload;
},

View File

@@ -2,9 +2,9 @@ import { AnyAction, ThunkAction } from '@reduxjs/toolkit';
import { RootState } from 'app/store';
import * as InvokeAI from 'app/invokeai';
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 { setInitialImage } from 'features/options/store/optionsSlice';
import { setInitialImage } from 'features/parameters/store/generationSlice';
import { addImage } from '../gallerySlice';
type UploadImageConfig = {

View File

@@ -10,7 +10,7 @@ import {
} from 'features/gallery/store/gallerySlice';
import ImageGallery from 'features/gallery/components/ImageGallery';
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 { useHotkeys } from 'react-hotkeys-hook';
import { BiExit } from 'react-icons/bi';
@@ -20,7 +20,7 @@ import ReactPanZoom from './ReactPanZoom';
export default function Lightbox() {
const dispatch = useAppDispatch();
const isLightBoxOpen = useAppSelector(
(state: RootState) => state.options.isLightBoxOpen
(state: RootState) => state.lightbox.isLightboxOpen
);
const {
@@ -52,7 +52,7 @@ export default function Lightbox() {
useHotkeys(
'Esc',
() => {
if (isLightBoxOpen) dispatch(setIsLightBoxOpen(false));
if (isLightBoxOpen) dispatch(setIsLightboxOpen(false));
},
[isLightBoxOpen]
);
@@ -64,7 +64,7 @@ export default function Lightbox() {
aria-label="Exit Viewer"
className="lightbox-close-btn"
onClick={() => {
dispatch(setIsLightBoxOpen(false));
dispatch(setIsLightboxOpen(false));
}}
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 *;
.advanced-settings {
.advanced-parameters {
padding-top: 0.5rem;
display: grid;
row-gap: 0.5rem;
}
.advanced-settings-item {
.advanced-parameters-item {
display: grid;
max-width: $options-bar-max-width;
border: none;
@@ -20,7 +20,7 @@
}
}
.advanced-settings-panel {
.advanced-parameters-panel {
background-color: var(--tab-panel-bg);
border-radius: 0 0 0.4rem 0.4rem;
padding: 1rem;
@@ -39,7 +39,7 @@
}
}
.advanced-settings-header {
.advanced-parameters-header {
border-radius: 0.4rem;
font-weight: bold;

View File

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

View File

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

View File

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

View File

@@ -4,7 +4,7 @@ import SeamSize from './SeamSize';
import SeamSteps from './SeamSteps';
import SeamStrength from './SeamStrength';
const SeamCorrectionOptions = () => {
const SeamCorrectionSettings = () => {
return (
<Flex direction="column" gap="1rem">
<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 { useAppDispatch, useAppSelector } from 'app/storeHooks';
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 { useTranslation } from 'react-i18next';
@@ -9,12 +9,12 @@ export default function SeamSize() {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const seamSize = useAppSelector((state: RootState) => state.options.seamSize);
const seamSize = useAppSelector((state: RootState) => state.generation.seamSize);
return (
<IAISlider
sliderMarkRightOffset={-6}
label={t('options:seamSize')}
label={t('parameters:seamSize')}
min={1}
max={256}
sliderNumberInputProps={{ max: 512 }}

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,7 +2,7 @@ import React from 'react';
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
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';
interface ImageToImageStrengthProps {
@@ -12,9 +12,9 @@ interface ImageToImageStrengthProps {
export default function ImageToImageStrength(props: ImageToImageStrengthProps) {
const { t } = useTranslation();
const { label = `${t('options:strength')}`, styleClass } = props;
const { label = `${t('parameters:strength')}`, styleClass } = props;
const img2imgStrength = useAppSelector(
(state: RootState) => state.options.img2imgStrength
(state: RootState) => state.generation.img2imgStrength
);
const dispatch = useAppDispatch();

View File

@@ -1,20 +1,30 @@
import { Flex } from '@chakra-ui/react';
import { ChangeEvent } from 'react';
import { createSelector } from '@reduxjs/toolkit';
import type { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISlider from 'common/components/IAISlider';
import IAISwitch from 'common/components/IAISwitch';
import { postprocessingSelector } from 'features/parameters/store/postprocessingSelectors';
import {
setHiresFix,
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 IAISlider from 'common/components/IAISlider';
function HighResStrength() {
const hiresFix = useAppSelector((state: RootState) => state.options.hiresFix);
const hiresStrength = useAppSelector(
(state: RootState) => state.options.hiresStrength
);
const hiresStrengthSelector = createSelector(
[postprocessingSelector],
({ hiresFix, hiresStrength }) => ({ hiresFix, hiresStrength }),
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);
const HiresStrength = () => {
const { hiresFix, hiresStrength } = useAppSelector(hiresStrengthSelector);
const dispatch = useAppDispatch();
@@ -30,7 +40,7 @@ function HighResStrength() {
return (
<IAISlider
label={t('options:hiresStrength')}
label={t('parameters:hiresStrength')}
step={0.01}
min={0.01}
max={0.99}
@@ -47,15 +57,17 @@ function HighResStrength() {
isResetDisabled={!hiresFix}
/>
);
}
};
/**
* Hires Fix Toggle
*/
const HiresOptions = () => {
const HiresSettings = () => {
const dispatch = useAppDispatch();
const hiresFix = useAppSelector((state: RootState) => state.options.hiresFix);
const hiresFix = useAppSelector(
(state: RootState) => state.postprocessing.hiresFix
);
const { t } = useTranslation();
@@ -65,14 +77,14 @@ const HiresOptions = () => {
return (
<Flex gap={2} direction={'column'}>
<IAISwitch
label={t('options:hiresOptim')}
label={t('parameters:hiresOptim')}
fontSize={'md'}
isChecked={hiresFix}
onChange={handleChangeHiresFix}
/>
<HighResStrength />
<HiresStrength />
</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 { useAppDispatch, useAppSelector } from 'app/storeHooks';
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';
/**
* Seamless tiling toggle
*/
const SeamlessOptions = () => {
const SeamlessSettings = () => {
const dispatch = useAppDispatch();
const seamless = useAppSelector((state: RootState) => state.options.seamless);
const seamless = useAppSelector((state: RootState) => state.generation.seamless);
const handleChangeSeamless = (e: ChangeEvent<HTMLInputElement>) =>
dispatch(setSeamless(e.target.checked));
@@ -22,7 +22,7 @@ const SeamlessOptions = () => {
return (
<Flex gap={2} direction={'column'}>
<IAISwitch
label={t('options:seamlessTiling')}
label={t('parameters:seamlessTiling')}
fontSize={'md'}
isChecked={seamless}
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 { useAppDispatch, useAppSelector } from 'app/storeHooks';
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';
export default function Perlin() {
const dispatch = useAppDispatch();
const perlin = useAppSelector((state: RootState) => state.options.perlin);
const perlin = useAppSelector((state: RootState) => state.generation.perlin);
const { t } = useTranslation();
const handleChangePerlin = (v: number) => dispatch(setPerlin(v));
return (
<IAINumberInput
label={t('options:perlinNoise')}
label={t('parameters:perlinNoise')}
min={0}
max={1}
step={0.05}

View File

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

View File

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

View File

@@ -8,7 +8,7 @@ import Perlin from './Perlin';
/**
* Seed & variation options. Includes iteration, seed, seed randomization, variation options.
*/
const SeedOptions = () => {
const SeedSettings = () => {
return (
<Flex gap={2} direction={'column'}>
<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 { useAppDispatch, useAppSelector } from 'app/storeHooks';
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';
export default function ShuffleSeed() {
const dispatch = useAppDispatch();
const shouldRandomizeSeed = useAppSelector(
(state: RootState) => state.options.shouldRandomizeSeed
(state: RootState) => state.generation.shouldRandomizeSeed
);
const { t } = useTranslation();
@@ -24,7 +24,7 @@ export default function ShuffleSeed() {
onClick={handleClickRandomizeSeed}
padding="0 1.5rem"
>
<p>{t('options:shuffle')}</p>
<p>{t('parameters:shuffle')}</p>
</Button>
);
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,16 +3,16 @@ import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAIInput from 'common/components/IAIInput';
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';
export default function SeedWeights() {
const seedWeights = useAppSelector(
(state: RootState) => state.options.seedWeights
(state: RootState) => state.generation.seedWeights
);
const shouldGenerateVariations = useAppSelector(
(state: RootState) => state.options.shouldGenerateVariations
(state: RootState) => state.generation.shouldGenerateVariations
);
const { t } = useTranslation();
@@ -24,7 +24,7 @@ export default function SeedWeights() {
return (
<IAIInput
label={t('options:seedWeights')}
label={t('parameters:seedWeights')}
value={seedWeights}
isInvalid={
shouldGenerateVariations &&

View File

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

View File

@@ -5,7 +5,7 @@ import VariationAmount from './VariationAmount';
/**
* Seed & variation options. Includes iteration, seed, seed randomization, variation options.
*/
const VariationsOptions = () => {
const VariationsSettings = () => {
return (
<Flex gap={2} direction={'column'}>
<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 { useAppDispatch, useAppSelector } from 'app/storeHooks';
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';
export default function MainCFGScale() {
const dispatch = useAppDispatch();
const cfgScale = useAppSelector((state: RootState) => state.options.cfgScale);
const cfgScale = useAppSelector((state: RootState) => state.generation.cfgScale);
const { t } = useTranslation();
const handleChangeCfgScale = (v: number) => dispatch(setCfgScale(v));
return (
<IAINumberInput
label={t('options:cfgScale')}
label={t('parameters:cfgScale')}
step={0.5}
min={1.01}
max={200}
onChange={handleChangeCfgScale}
value={cfgScale}
width="auto"
styleClass="main-option-block"
styleClass="main-settings-block"
textAlign="center"
isInteger={false}
/>

View File

@@ -4,12 +4,12 @@ import { HEIGHTS } from 'app/constants';
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISelect from 'common/components/IAISelect';
import { activeTabNameSelector } from 'features/options/store/optionsSelectors';
import { setHeight } from 'features/options/store/optionsSlice';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { setHeight } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next';
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 dispatch = useAppDispatch();
const { t } = useTranslation();
@@ -20,12 +20,12 @@ export default function MainHeight() {
return (
<IAISelect
isDisabled={activeTabName === 'unifiedCanvas'}
label={t('options:height')}
label={t('parameters:height')}
value={height}
flexGrow={1}
onChange={handleChangeHeight}
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 IAINumberInput from 'common/components/IAINumberInput';
import {
OptionsState,
GenerationState,
setIterations,
} from 'features/options/store/optionsSlice';
} from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next';
const mainIterationsSelector = createSelector(
[(state: RootState) => state.options],
(options: OptionsState) => {
const { iterations } = options;
[(state: RootState) => state.generation],
(parameters: GenerationState) => {
const { iterations } = parameters;
return {
iterations,
@@ -35,7 +35,7 @@ export default function MainIterations() {
return (
<IAINumberInput
label={t('options:images')}
label={t('parameters:images')}
step={1}
min={1}
max={9999}
@@ -43,7 +43,7 @@ export default function MainIterations() {
value={iterations}
width="auto"
labelFontSize={0.5}
styleClass="main-option-block"
styleClass="main-settings-block"
textAlign="center"
/>
);

View File

@@ -1,23 +1,23 @@
@use '../../../../styles/Mixins/' as *;
.main-options {
.main-settings {
display: grid;
row-gap: 1rem;
}
.main-options-list {
.main-settings-list {
display: grid;
row-gap: 1rem;
}
.main-options-row {
.main-settings-row {
display: grid;
grid-template-columns: repeat(3, auto);
column-gap: 0.5rem;
max-width: $options-bar-max-width;
}
.main-option-block {
.main-settings-block {
border-radius: 0.5rem;
display: grid !important;
grid-template-columns: auto !important;
@@ -33,10 +33,3 @@
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 default function MainOptions() {
export default function MainSettings() {
return (
<div className="main-options">
<div className="main-options-list">
<div className="main-options-row">
<div className="main-settings">
<div className="main-settings-list">
<div className="main-settings-row">
<MainIterations />
<MainSteps />
<MainCFGScale />
</div>
<div className="main-options-row">
<div className="main-settings-row">
<MainWidth />
<MainHeight />
<MainSampler />

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,7 +2,7 @@ import InvokeButton from './InvokeButton';
import CancelButton from './CancelButton';
import LoopbackButton from './Loopback';
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.

View File

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

View File

@@ -4,19 +4,19 @@ import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import { generateImage } from 'app/socketio/actions';
import { OptionsState, setPrompt } from 'features/options/store/optionsSlice';
import { GenerationState, setPrompt } from 'features/parameters/store/generationSlice';
import { createSelector } from '@reduxjs/toolkit';
import _ from 'lodash';
import { useHotkeys } from 'react-hotkeys-hook';
import { activeTabNameSelector } from 'features/options/store/optionsSelectors';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { readinessSelector } from 'app/selectors/readinessSelector';
import { useTranslation } from 'react-i18next';
const promptInputSelector = createSelector(
[(state: RootState) => state.options, activeTabNameSelector],
(options: OptionsState, activeTabName) => {
[(state: RootState) => state.generation, activeTabNameSelector],
(parameters: GenerationState, activeTabName) => {
return {
prompt: options.prompt,
prompt: parameters.prompt,
activeTabName,
};
},
@@ -66,7 +66,7 @@ const PromptInput = () => {
<Textarea
id="prompt"
name="prompt"
placeholder={t('options:promptPlaceholder')}
placeholder={t('parameters:promptPlaceholder')}
size={'lg'}
value={prompt}
onChange={handleChangePrompt}

View File

@@ -0,0 +1,17 @@
import { createSelector } from '@reduxjs/toolkit';
import _ from 'lodash';
import { RootState } from 'app/store';
export const generationSelector = (state: RootState) => state.generation;
export const mayGenerateMultipleImagesSelector = createSelector(
generationSelector,
({ shouldRandomizeSeed, shouldGenerateVariations }) => {
return shouldRandomizeSeed || shouldGenerateVariations;
},
{
memoizeOptions: {
resultEqualityCheck: _.isEqual,
},
}
);

View File

@@ -3,31 +3,16 @@ import type { PayloadAction } from '@reduxjs/toolkit';
import * as InvokeAI from 'app/invokeai';
import promptToString from 'common/util/promptToString';
import { seedWeightsToString } from 'common/util/seedWeightPairs';
import { FACETOOL_TYPES } from 'app/constants';
import { InvokeTabName, tabMap } from 'features/tabs/tabMap';
import { getPromptAndNegative } from 'common/util/getPromptAndNegative';
export type UpscalingLevel = 2 | 4;
export type FacetoolType = typeof FACETOOL_TYPES[number];
export interface OptionsState {
activeTab: number;
export interface GenerationState {
cfgScale: number;
codeformerFidelity: number;
currentTheme: string;
facetoolStrength: number;
facetoolType: FacetoolType;
height: number;
hiresFix: boolean;
hiresStrength: number;
img2imgStrength: number;
infillMethod: string;
initialImage?: InvokeAI.Image | string; // can be an Image or url
isLightBoxOpen: boolean;
iterations: number;
maskPath: string;
optionsPanelScrollPosition: number;
perlin: number;
prompt: string;
negativePrompt: string;
@@ -41,44 +26,21 @@ export interface OptionsState {
seedWeights: string;
shouldFitToWidthHeight: boolean;
shouldGenerateVariations: boolean;
shouldHoldOptionsPanelOpen: boolean;
shouldLoopback: boolean;
shouldPinOptionsPanel: boolean;
shouldRandomizeSeed: boolean;
shouldRunESRGAN: boolean;
shouldRunFacetool: boolean;
shouldShowImageDetails: boolean;
shouldShowOptionsPanel: boolean;
showAdvancedOptions: boolean;
showDualDisplay: boolean;
steps: number;
threshold: number;
tileSize: number;
upscalingLevel: UpscalingLevel;
upscalingStrength: number;
variationAmount: number;
width: number;
shouldUseCanvasBetaLayout: boolean;
shouldShowExistingModelsInSearch: boolean;
addNewModelUIOption: 'ckpt' | 'diffusers' | null;
}
const initialOptionsState: OptionsState = {
activeTab: 0,
const initialGenerationState: GenerationState = {
cfgScale: 7.5,
codeformerFidelity: 0.75,
currentTheme: 'dark',
facetoolStrength: 0.8,
facetoolType: 'gfpgan',
height: 512,
hiresFix: false,
hiresStrength: 0.75,
img2imgStrength: 0.75,
infillMethod: 'patchmatch',
isLightBoxOpen: false,
iterations: 1,
maskPath: '',
optionsPanelScrollPosition: 0,
perlin: 0,
prompt: '',
negativePrompt: '',
@@ -92,32 +54,18 @@ const initialOptionsState: OptionsState = {
seedWeights: '',
shouldFitToWidthHeight: true,
shouldGenerateVariations: false,
shouldHoldOptionsPanelOpen: false,
shouldLoopback: false,
shouldPinOptionsPanel: true,
shouldRandomizeSeed: true,
shouldRunESRGAN: false,
shouldRunFacetool: false,
shouldShowImageDetails: false,
shouldShowOptionsPanel: true,
showAdvancedOptions: true,
showDualDisplay: true,
steps: 50,
threshold: 0,
tileSize: 32,
upscalingLevel: 4,
upscalingStrength: 0.75,
variationAmount: 0.1,
width: 512,
shouldUseCanvasBetaLayout: false,
shouldShowExistingModelsInSearch: false,
addNewModelUIOption: null,
};
const initialState: OptionsState = initialOptionsState;
const initialState: GenerationState = initialGenerationState;
export const optionsSlice = createSlice({
name: 'options',
export const generationSlice = createSlice({
name: 'generation',
initialState,
reducers: {
setPrompt: (state, action: PayloadAction<string | InvokeAI.Prompt>) => {
@@ -170,30 +118,12 @@ export const optionsSlice = createSlice({
setImg2imgStrength: (state, action: PayloadAction<number>) => {
state.img2imgStrength = action.payload;
},
setFacetoolStrength: (state, action: PayloadAction<number>) => {
state.facetoolStrength = action.payload;
},
setCodeformerFidelity: (state, action: PayloadAction<number>) => {
state.codeformerFidelity = action.payload;
},
setUpscalingLevel: (state, action: PayloadAction<UpscalingLevel>) => {
state.upscalingLevel = action.payload;
},
setUpscalingStrength: (state, action: PayloadAction<number>) => {
state.upscalingStrength = action.payload;
},
setMaskPath: (state, action: PayloadAction<string>) => {
state.maskPath = action.payload;
},
setSeamless: (state, action: PayloadAction<boolean>) => {
state.seamless = action.payload;
},
setHiresFix: (state, action: PayloadAction<boolean>) => {
state.hiresFix = action.payload;
},
setHiresStrength: (state, action: PayloadAction<number>) => {
state.hiresStrength = action.payload;
},
setShouldFitToWidthHeight: (state, action: PayloadAction<boolean>) => {
state.shouldFitToWidthHeight = action.payload;
},
@@ -205,6 +135,7 @@ export const optionsSlice = createSlice({
action: PayloadAction<{ key: string; value: string | number | boolean }>
) => {
// TODO: This probably needs to be refactored.
// TODO: This probably also needs to be fixed after the reorg.
const { key, value } = action.payload;
const temp = { ...state, [key]: value };
if (key === 'seed') {
@@ -267,7 +198,7 @@ export const optionsSlice = createSlice({
if (perlin) state.perlin = perlin;
if (typeof perlin === 'undefined') state.perlin = 0;
if (typeof seamless === 'boolean') state.seamless = seamless;
if (typeof hires_fix === 'boolean') state.hiresFix = hires_fix;
// if (typeof hires_fix === 'boolean') state.hiresFix = hires_fix; // TODO: Needs to be fixed after reorg
if (width) state.width = width;
if (height) state.height = height;
},
@@ -342,47 +273,22 @@ export const optionsSlice = createSlice({
if (perlin) state.perlin = perlin;
if (typeof perlin === 'undefined') state.perlin = 0;
if (typeof seamless === 'boolean') state.seamless = seamless;
if (typeof hires_fix === 'boolean') state.hiresFix = hires_fix;
// if (typeof hires_fix === 'boolean') state.hiresFix = hires_fix; // TODO: Needs to be fixed after reorg
if (width) state.width = width;
if (height) state.height = height;
state.shouldRunESRGAN = false;
state.shouldRunFacetool = false;
// state.shouldRunESRGAN = false; // TODO: Needs to be fixed after reorg
// state.shouldRunFacetool = false; // TODO: Needs to be fixed after reorg
},
resetOptionsState: (state) => {
resetParametersState: (state) => {
return {
...state,
...initialOptionsState,
...initialGenerationState,
};
},
setShouldRunFacetool: (state, action: PayloadAction<boolean>) => {
state.shouldRunFacetool = action.payload;
},
setFacetoolType: (state, action: PayloadAction<FacetoolType>) => {
state.facetoolType = action.payload;
},
setShouldRunESRGAN: (state, action: PayloadAction<boolean>) => {
state.shouldRunESRGAN = action.payload;
},
setShouldRandomizeSeed: (state, action: PayloadAction<boolean>) => {
state.shouldRandomizeSeed = action.payload;
},
setShowAdvancedOptions: (state, action: PayloadAction<boolean>) => {
state.showAdvancedOptions = action.payload;
},
setActiveTab: (state, action: PayloadAction<number | InvokeTabName>) => {
if (typeof action.payload === 'number') {
state.activeTab = action.payload;
} else {
state.activeTab = tabMap.indexOf(action.payload);
}
},
setShouldShowImageDetails: (state, action: PayloadAction<boolean>) => {
state.shouldShowImageDetails = action.payload;
},
setShowDualDisplay: (state, action: PayloadAction<boolean>) => {
state.showDualDisplay = action.payload;
},
setInitialImage: (
state,
action: PayloadAction<InvokeAI.Image | string>
@@ -392,27 +298,6 @@ export const optionsSlice = createSlice({
clearInitialImage: (state) => {
state.initialImage = undefined;
},
setShouldPinOptionsPanel: (state, action: PayloadAction<boolean>) => {
state.shouldPinOptionsPanel = action.payload;
},
setShouldShowOptionsPanel: (state, action: PayloadAction<boolean>) => {
state.shouldShowOptionsPanel = action.payload;
},
setOptionsPanelScrollPosition: (state, action: PayloadAction<number>) => {
state.optionsPanelScrollPosition = action.payload;
},
setShouldHoldOptionsPanelOpen: (state, action: PayloadAction<boolean>) => {
state.shouldHoldOptionsPanelOpen = action.payload;
},
setShouldLoopback: (state, action: PayloadAction<boolean>) => {
state.shouldLoopback = action.payload;
},
setCurrentTheme: (state, action: PayloadAction<string>) => {
state.currentTheme = action.payload;
},
setIsLightBoxOpen: (state, action: PayloadAction<boolean>) => {
state.isLightBoxOpen = action.payload;
},
setSeamSize: (state, action: PayloadAction<number>) => {
state.seamSize = action.payload;
},
@@ -431,47 +316,23 @@ export const optionsSlice = createSlice({
setInfillMethod: (state, action: PayloadAction<string>) => {
state.infillMethod = action.payload;
},
setShouldUseCanvasBetaLayout: (state, action: PayloadAction<boolean>) => {
state.shouldUseCanvasBetaLayout = action.payload;
},
setShouldShowExistingModelsInSearch: (
state,
action: PayloadAction<boolean>
) => {
state.shouldShowExistingModelsInSearch = action.payload;
},
setAddNewModelUIOption: (
state,
action: PayloadAction<'ckpt' | 'diffusers' | null>
) => {
state.addNewModelUIOption = action.payload;
},
},
});
export const {
clearInitialImage,
resetOptionsState,
resetParametersState,
resetSeed,
setActiveTab,
setAllImageToImageParameters,
setAllParameters,
setAllTextToImageParameters,
setCfgScale,
setCodeformerFidelity,
setCurrentTheme,
setFacetoolStrength,
setFacetoolType,
setHeight,
setHiresFix,
setHiresStrength,
setImg2imgStrength,
setInfillMethod,
setInitialImage,
setIsLightBoxOpen,
setIterations,
setMaskPath,
setOptionsPanelScrollPosition,
setParameter,
setPerlin,
setPrompt,
@@ -486,26 +347,12 @@ export const {
setSeedWeights,
setShouldFitToWidthHeight,
setShouldGenerateVariations,
setShouldHoldOptionsPanelOpen,
setShouldLoopback,
setShouldPinOptionsPanel,
setShouldRandomizeSeed,
setShouldRunESRGAN,
setShouldRunFacetool,
setShouldShowImageDetails,
setShouldShowOptionsPanel,
setShowAdvancedOptions,
setShowDualDisplay,
setSteps,
setThreshold,
setTileSize,
setUpscalingLevel,
setUpscalingStrength,
setVariationAmount,
setWidth,
setShouldUseCanvasBetaLayout,
setShouldShowExistingModelsInSearch,
setAddNewModelUIOption,
} = optionsSlice.actions;
} = generationSlice.actions;
export default optionsSlice.reducer;
export default generationSlice.reducer;

View File

@@ -0,0 +1,3 @@
import { RootState } from 'app/store';
export const postprocessingSelector = (state: RootState) => state.postprocessing;

View File

@@ -0,0 +1,94 @@
import { createSlice } from '@reduxjs/toolkit';
import type { PayloadAction } from '@reduxjs/toolkit';
import { FACETOOL_TYPES } from 'app/constants';
export type UpscalingLevel = 2 | 4;
export type FacetoolType = typeof FACETOOL_TYPES[number];
export interface PostprocessingState {
codeformerFidelity: number;
facetoolStrength: number;
facetoolType: FacetoolType;
hiresFix: boolean;
hiresStrength: number;
shouldLoopback: boolean;
shouldRunESRGAN: boolean;
shouldRunFacetool: boolean;
upscalingLevel: UpscalingLevel;
upscalingStrength: number;
}
const initialPostprocessingState: PostprocessingState = {
codeformerFidelity: 0.75,
facetoolStrength: 0.8,
facetoolType: 'gfpgan',
hiresFix: false,
hiresStrength: 0.75,
shouldLoopback: false,
shouldRunESRGAN: false,
shouldRunFacetool: false,
upscalingLevel: 4,
upscalingStrength: 0.75,
};
const initialState: PostprocessingState = initialPostprocessingState;
export const postprocessingSlice = createSlice({
name: 'postprocessing',
initialState,
reducers: {
setFacetoolStrength: (state, action: PayloadAction<number>) => {
state.facetoolStrength = action.payload;
},
setCodeformerFidelity: (state, action: PayloadAction<number>) => {
state.codeformerFidelity = action.payload;
},
setUpscalingLevel: (state, action: PayloadAction<UpscalingLevel>) => {
state.upscalingLevel = action.payload;
},
setUpscalingStrength: (state, action: PayloadAction<number>) => {
state.upscalingStrength = action.payload;
},
setHiresFix: (state, action: PayloadAction<boolean>) => {
state.hiresFix = action.payload;
},
setHiresStrength: (state, action: PayloadAction<number>) => {
state.hiresStrength = action.payload;
},
resetPostprocessingState: (state) => {
return {
...state,
...initialPostprocessingState,
};
},
setShouldRunFacetool: (state, action: PayloadAction<boolean>) => {
state.shouldRunFacetool = action.payload;
},
setFacetoolType: (state, action: PayloadAction<FacetoolType>) => {
state.facetoolType = action.payload;
},
setShouldRunESRGAN: (state, action: PayloadAction<boolean>) => {
state.shouldRunESRGAN = action.payload;
},
setShouldLoopback: (state, action: PayloadAction<boolean>) => {
state.shouldLoopback = action.payload;
},
},
});
export const {
resetPostprocessingState,
setCodeformerFidelity,
setFacetoolStrength,
setFacetoolType,
setHiresFix,
setHiresStrength,
setShouldLoopback,
setShouldRunESRGAN,
setShouldRunFacetool,
setUpscalingLevel,
setUpscalingStrength,
} = postprocessingSlice.actions;
export default postprocessingSlice.reducer;

View File

@@ -1,6 +1,5 @@
import { IconButton, Tooltip } from '@chakra-ui/react';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import { RootState } from 'app/store';
import {
errorSeen,
setShouldShowLogViewer,
@@ -12,9 +11,10 @@ import { createSelector } from '@reduxjs/toolkit';
import { isEqual } from 'lodash';
import { Resizable } from 're-resizable';
import { useHotkeys } from 'react-hotkeys-hook';
import { systemSelector } from '../store/systemSelectors';
const logSelector = createSelector(
(state: RootState) => state.system,
systemSelector,
(system: SystemState) => system.log,
{
memoizeOptions: {
@@ -24,8 +24,8 @@ const logSelector = createSelector(
}
);
const systemSelector = createSelector(
(state: RootState) => state.system,
const consoleSelector = createSelector(
systemSelector,
(system: SystemState) => {
return {
shouldShowLogViewer: system.shouldShowLogViewer,
@@ -47,7 +47,7 @@ const Console = () => {
const dispatch = useAppDispatch();
const log = useAppSelector(logSelector);
const { shouldShowLogViewer, hasError, wasErrorSeen } =
useAppSelector(systemSelector);
useAppSelector(consoleSelector);
// Rudimentary autoscroll
const [shouldAutoscroll, setShouldAutoscroll] = useState<boolean>(true);

View File

@@ -26,7 +26,7 @@ import { useTranslation } from 'react-i18next';
import type { FieldInputProps, FormikProps } from 'formik';
import type { RootState } from 'app/store';
import type { InvokeModelConfigProps } from 'app/invokeai';
import { setAddNewModelUIOption } from 'features/options/store/optionsSlice';
import { setAddNewModelUIOption } from 'features/ui/store/uiSlice';
import IAIIconButton from 'common/components/IAIIconButton';
import { BiArrowBack } from 'react-icons/bi';

View File

@@ -11,7 +11,7 @@ import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAIButton from 'common/components/IAIButton';
import IAIIconButton from 'common/components/IAIIconButton';
import IAIInput from 'common/components/IAIInput';
import { setAddNewModelUIOption } from 'features/options/store/optionsSlice';
import { setAddNewModelUIOption } from 'features/ui/store/uiSlice';
import { Field, Formik } from 'formik';
import React from 'react';
import { useTranslation } from 'react-i18next';

View File

@@ -19,7 +19,7 @@ import { useTranslation } from 'react-i18next';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import type { RootState } from 'app/store';
import { setAddNewModelUIOption } from 'features/options/store/optionsSlice';
import { setAddNewModelUIOption } from 'features/ui/store/uiSlice';
import AddCheckpointModel from './AddCheckpointModel';
import AddDiffusersModel from './AddDiffusersModel';
@@ -54,7 +54,7 @@ export default function AddModel() {
const { isOpen, onOpen, onClose } = useDisclosure();
const addNewModelUIOption = useAppSelector(
(state: RootState) => state.options.addNewModelUIOption
(state: RootState) => state.ui.addNewModelUIOption
);
const dispatch = useAppDispatch();

View File

@@ -12,12 +12,12 @@ import { useTranslation } from 'react-i18next';
import _ from 'lodash';
import type { ChangeEvent, ReactNode } from 'react';
import type { RootState } from 'app/store';
import type { SystemState } from 'features/system/store/systemSlice';
import IAIButton from 'common/components/IAIButton';
import { systemSelector } from 'features/system/store/systemSelectors';
const modelListSelector = createSelector(
(state: RootState) => state.system,
systemSelector,
(system: SystemState) => {
const models = _.map(system.model_list, (model, key) => {
return { name: key, ...model };

View File

@@ -17,7 +17,7 @@ import {
setFoundModels,
setSearchFolder,
} from 'features/system/store/systemSlice';
import { setShouldShowExistingModelsInSearch } from 'features/options/store/optionsSlice';
import { setShouldShowExistingModelsInSearch } from 'features/ui/store/uiSlice';
import _ from 'lodash';
@@ -128,7 +128,7 @@ export default function SearchModels() {
const existingModels = useAppSelector(existingModelsSelector);
const shouldShowExistingModelsInSearch = useAppSelector(
(state: RootState) => state.options.shouldShowExistingModelsInSearch
(state: RootState) => state.ui.shouldShowExistingModelsInSearch
);
const isProcessing = useAppSelector(

View File

@@ -2,11 +2,11 @@ import { Progress } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { isEqual } from 'lodash';
import { useAppSelector } from 'app/storeHooks';
import { RootState } from 'app/store';
import { SystemState } from 'features/system/store/systemSlice';
import { systemSelector } from '../store/systemSelectors';
const systemSelector = createSelector(
(state: RootState) => state.system,
const progressBarSelector = createSelector(
systemSelector,
(system: SystemState) => {
return {
isProcessing: system.isProcessing,
@@ -22,7 +22,7 @@ const systemSelector = createSelector(
const ProgressBar = () => {
const { isProcessing, currentStep, totalSteps, currentStatusHasSteps } =
useAppSelector(systemSelector);
useAppSelector(progressBarSelector);
const value = currentStep ? Math.round((currentStep * 100) / totalSteps) : 0;

View File

@@ -31,13 +31,13 @@ import IAISwitch from 'common/components/IAISwitch';
import IAISelect from 'common/components/IAISelect';
import IAINumberInput from 'common/components/IAINumberInput';
import { systemSelector } from 'features/system/store/systemSelectors';
import { optionsSelector } from 'features/options/store/optionsSelectors';
import { setShouldUseCanvasBetaLayout } from 'features/options/store/optionsSlice';
import { uiSelector } from 'features/ui/store/uiSelectors';
import { setShouldUseCanvasBetaLayout } from 'features/ui/store/uiSlice';
import { useTranslation } from 'react-i18next';
const selector = createSelector(
[systemSelector, optionsSelector],
(system, options) => {
[systemSelector, uiSelector],
(system, ui) => {
const {
shouldDisplayInProgressType,
shouldConfirmOnDelete,
@@ -47,7 +47,7 @@ const selector = createSelector(
enableImageDebugging,
} = system;
const { shouldUseCanvasBetaLayout } = options;
const { shouldUseCanvasBetaLayout } = ui;
return {
shouldDisplayInProgressType,
@@ -79,7 +79,7 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const steps = useAppSelector((state: RootState) => state.options.steps);
const steps = useAppSelector((state: RootState) => state.generation.steps);
const {
isOpen: isSettingsModalOpen,

View File

@@ -1,13 +1,13 @@
import { Text, Tooltip } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { isEqual } from 'lodash';
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import { errorSeen, SystemState } from 'features/system/store/systemSlice';
import { useTranslation } from 'react-i18next';
import { systemSelector } from '../store/systemSelectors';
const systemSelector = createSelector(
(state: RootState) => state.system,
const statusIndicatorSelector = createSelector(
systemSelector,
(system: SystemState) => {
return {
isConnected: system.isConnected,
@@ -33,7 +33,7 @@ const StatusIndicator = () => {
currentStatus,
hasError,
wasErrorSeen,
} = useAppSelector(systemSelector);
} = useAppSelector(statusIndicatorSelector);
const dispatch = useAppDispatch();
const { t } = useTranslation();

View File

@@ -2,7 +2,7 @@ import { useEffect } from 'react';
import { useColorMode, VStack } from '@chakra-ui/react';
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import { setCurrentTheme } from 'features/options/store/optionsSlice';
import { setCurrentTheme } from 'features/ui/store/uiSlice';
import IAIPopover from 'common/components/IAIPopover';
import IAIIconButton from 'common/components/IAIIconButton';
import { FaCheck, FaPalette } from 'react-icons/fa';
@@ -15,7 +15,7 @@ export default function ThemeChanger() {
const { setColorMode, colorMode } = useColorMode();
const dispatch = useAppDispatch();
const currentTheme = useAppSelector(
(state: RootState) => state.options.currentTheme
(state: RootState) => state.ui.currentTheme
);
const THEMES = {

View File

@@ -1,9 +1,8 @@
import { createSelector } from '@reduxjs/toolkit';
import { RootState } from 'app/store';
import { SystemState } from './systemSlice';
import _ from 'lodash';
import { isEqual, reduce } from 'lodash';
export const systemSelector = (state: RootState): SystemState => state.system;
export const systemSelector = (state: RootState) => state.system;
export const toastQueueSelector = (state: RootState) => state.system.toastQueue;
@@ -11,7 +10,7 @@ export const activeModelSelector = createSelector(
systemSelector,
(system) => {
const { model_list } = system;
const activeModel = _.reduce(
const activeModel = reduce(
model_list,
(acc, model, key) => {
if (model.status === 'active') {
@@ -25,7 +24,7 @@ export const activeModelSelector = createSelector(
},
{
memoizeOptions: {
resultEqualityCheck: _.isEqual,
resultEqualityCheck: isEqual,
},
}
);

View File

@@ -1,86 +0,0 @@
import { useEffect } from 'react';
import { Feature } from 'app/features';
import FaceRestoreOptions from 'features/options/components/AdvancedOptions/FaceRestore/FaceRestoreOptions';
import FaceRestoreToggle from 'features/options/components/AdvancedOptions/FaceRestore/FaceRestoreToggle';
import ImageFit from 'features/options/components/AdvancedOptions/ImageToImage/ImageFit';
import ImageToImageStrength from 'features/options/components/AdvancedOptions/ImageToImage/ImageToImageStrength';
import ImageToImageOutputOptions from 'features/options/components/AdvancedOptions/Output/ImageToImageOutputOptions';
import SeedOptions from 'features/options/components/AdvancedOptions/Seed/SeedOptions';
import UpscaleOptions from 'features/options/components/AdvancedOptions/Upscale/UpscaleOptions';
import UpscaleToggle from 'features/options/components/AdvancedOptions/Upscale/UpscaleToggle';
import GenerateVariationsToggle from 'features/options/components/AdvancedOptions/Variations/GenerateVariations';
import VariationsOptions from 'features/options/components/AdvancedOptions/Variations/VariationsOptions';
import MainOptions from 'features/options/components/MainOptions/MainOptions';
import OptionsAccordion from 'features/options/components/OptionsAccordion';
import ProcessButtons from 'features/options/components/ProcessButtons/ProcessButtons';
import PromptInput from 'features/options/components/PromptInput/PromptInput';
import { setHiresFix } from 'features/options/store/optionsSlice';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import InvokeOptionsPanel from 'features/tabs/components/InvokeOptionsPanel';
import { activeTabNameSelector } from 'features/options/store/optionsSelectors';
import { useTranslation } from 'react-i18next';
import { Flex } from '@chakra-ui/react';
import { NegativePromptInput } from 'features/options/components/PromptInput/NegativePromptInput';
export default function ImageToImagePanel() {
const { t } = useTranslation();
const imageToImageAccordions = {
seed: {
header: `${t('options:seed')}`,
feature: Feature.SEED,
content: <SeedOptions />,
},
variations: {
header: `${t('options:variations')}`,
feature: Feature.VARIATIONS,
content: <VariationsOptions />,
additionalHeaderComponents: <GenerateVariationsToggle />,
},
face_restore: {
header: `${t('options:faceRestoration')}`,
feature: Feature.FACE_CORRECTION,
content: <FaceRestoreOptions />,
additionalHeaderComponents: <FaceRestoreToggle />,
},
upscale: {
header: `${t('options:upscaling')}`,
feature: Feature.UPSCALE,
content: <UpscaleOptions />,
additionalHeaderComponents: <UpscaleToggle />,
},
other: {
header: `${t('options:otherOptions')}`,
feature: Feature.OTHER,
content: <ImageToImageOutputOptions />,
},
};
const dispatch = useAppDispatch();
const activeTabName = useAppSelector(activeTabNameSelector);
useEffect(() => {
if (activeTabName === 'img2img') {
const handleChangeHiresFix = () => dispatch(setHiresFix(false));
handleChangeHiresFix();
}
}, [activeTabName, dispatch]);
return (
<InvokeOptionsPanel>
<Flex flexDir="column" rowGap="0.5rem">
<PromptInput />
<NegativePromptInput />
</Flex>
<ProcessButtons />
<MainOptions />
<ImageToImageStrength
label={t('options:img2imgStrength')}
styleClass="main-option-block image-to-image-strength-main-option"
/>
<ImageFit />
<OptionsAccordion accordionInfo={imageToImageAccordions} />
</InvokeOptionsPanel>
);
}

View File

@@ -1,64 +0,0 @@
import { Flex } from '@chakra-ui/react';
import { Feature } from 'app/features';
import FaceRestoreOptions from 'features/options/components/AdvancedOptions/FaceRestore/FaceRestoreOptions';
import FaceRestoreToggle from 'features/options/components/AdvancedOptions/FaceRestore/FaceRestoreToggle';
import OutputOptions from 'features/options/components/AdvancedOptions/Output/OutputOptions';
import SeedOptions from 'features/options/components/AdvancedOptions/Seed/SeedOptions';
import UpscaleOptions from 'features/options/components/AdvancedOptions/Upscale/UpscaleOptions';
import UpscaleToggle from 'features/options/components/AdvancedOptions/Upscale/UpscaleToggle';
import GenerateVariationsToggle from 'features/options/components/AdvancedOptions/Variations/GenerateVariations';
import VariationsOptions from 'features/options/components/AdvancedOptions/Variations/VariationsOptions';
import MainOptions from 'features/options/components/MainOptions/MainOptions';
import OptionsAccordion from 'features/options/components/OptionsAccordion';
import ProcessButtons from 'features/options/components/ProcessButtons/ProcessButtons';
import { NegativePromptInput } from 'features/options/components/PromptInput/NegativePromptInput';
import PromptInput from 'features/options/components/PromptInput/PromptInput';
import InvokeOptionsPanel from 'features/tabs/components/InvokeOptionsPanel';
import { useTranslation } from 'react-i18next';
export default function TextToImagePanel() {
const { t } = useTranslation();
const textToImageAccordions = {
seed: {
header: `${t('options:seed')}`,
feature: Feature.SEED,
content: <SeedOptions />,
},
variations: {
header: `${t('options:variations')}`,
feature: Feature.VARIATIONS,
content: <VariationsOptions />,
additionalHeaderComponents: <GenerateVariationsToggle />,
},
face_restore: {
header: `${t('options:faceRestoration')}`,
feature: Feature.FACE_CORRECTION,
content: <FaceRestoreOptions />,
additionalHeaderComponents: <FaceRestoreToggle />,
},
upscale: {
header: `${t('options:upscaling')}`,
feature: Feature.UPSCALE,
content: <UpscaleOptions />,
additionalHeaderComponents: <UpscaleToggle />,
},
other: {
header: `${t('options:otherOptions')}`,
feature: Feature.OTHER,
content: <OutputOptions />,
},
};
return (
<InvokeOptionsPanel>
<Flex flexDir="column" rowGap="0.5rem">
<PromptInput />
<NegativePromptInput />
</Flex>
<ProcessButtons />
<MainOptions />
<OptionsAccordion accordionInfo={textToImageAccordions} />
</InvokeOptionsPanel>
);
}

View File

@@ -1,66 +0,0 @@
// import { Feature } from 'app/features';
import { Feature } from 'app/features';
import ImageToImageStrength from 'features/options/components/AdvancedOptions/ImageToImage/ImageToImageStrength';
import SeamCorrectionOptions from 'features/options/components/AdvancedOptions/Canvas/SeamCorrectionOptions/SeamCorrectionOptions';
import SeedOptions from 'features/options/components/AdvancedOptions/Seed/SeedOptions';
import GenerateVariationsToggle from 'features/options/components/AdvancedOptions/Variations/GenerateVariations';
import VariationsOptions from 'features/options/components/AdvancedOptions/Variations/VariationsOptions';
import MainOptions from 'features/options/components/MainOptions/MainOptions';
import OptionsAccordion from 'features/options/components/OptionsAccordion';
import ProcessButtons from 'features/options/components/ProcessButtons/ProcessButtons';
import PromptInput from 'features/options/components/PromptInput/PromptInput';
import InvokeOptionsPanel from 'features/tabs/components/InvokeOptionsPanel';
import BoundingBoxSettings from 'features/options/components/AdvancedOptions/Canvas/BoundingBoxSettings/BoundingBoxSettings';
import InfillAndScalingOptions from 'features/options/components/AdvancedOptions/Canvas/InfillAndScalingOptions';
import { useTranslation } from 'react-i18next';
import { Flex } from '@chakra-ui/react';
import { NegativePromptInput } from 'features/options/components/PromptInput/NegativePromptInput';
export default function UnifiedCanvasPanel() {
const { t } = useTranslation();
const unifiedCanvasAccordions = {
boundingBox: {
header: `${t('options:boundingBoxHeader')}`,
feature: Feature.BOUNDING_BOX,
content: <BoundingBoxSettings />,
},
seamCorrection: {
header: `${t('options:seamCorrectionHeader')}`,
feature: Feature.SEAM_CORRECTION,
content: <SeamCorrectionOptions />,
},
infillAndScaling: {
header: `${t('options:infillScalingHeader')}`,
feature: Feature.INFILL_AND_SCALING,
content: <InfillAndScalingOptions />,
},
seed: {
header: `${t('options:seed')}`,
feature: Feature.SEED,
content: <SeedOptions />,
},
variations: {
header: `${t('options:variations')}`,
feature: Feature.VARIATIONS,
content: <VariationsOptions />,
additionalHeaderComponents: <GenerateVariationsToggle />,
},
};
return (
<InvokeOptionsPanel>
<Flex flexDir="column" rowGap="0.5rem">
<PromptInput />
<NegativePromptInput />
</Flex>
<ProcessButtons />
<MainOptions />
<ImageToImageStrength
label={t('options:img2imgStrength')}
styleClass="main-option-block image-to-image-strength-main-option"
/>
<OptionsAccordion accordionInfo={unifiedCanvasAccordions} />
</InvokeOptionsPanel>
);
}

View File

@@ -4,8 +4,8 @@ import IAIIconButton from 'common/components/IAIIconButton';
import { setShouldShowGallery } from 'features/gallery/store/gallerySlice';
import { setDoesCanvasNeedScaling } from 'features/canvas/store/canvasSlice';
import { useHotkeys } from 'react-hotkeys-hook';
import { floatingSelector } from './FloatingOptionsPanelButtons';
import { setShouldShowOptionsPanel } from 'features/options/store/optionsSlice';
import { floatingSelector } from './FloatingParametersPanelButtons';
import { setShouldShowParametersPanel } from 'features/ui/store/uiSlice';
const FloatingGalleryButton = () => {
const dispatch = useAppDispatch();
@@ -13,8 +13,8 @@ const FloatingGalleryButton = () => {
shouldShowGallery,
shouldShowGalleryButton,
shouldPinGallery,
shouldShowOptionsPanel,
shouldPinOptionsPanel,
shouldShowParametersPanel,
shouldPinParametersPanel,
} = useAppSelector(floatingSelector);
const handleShowGallery = () => {
@@ -27,17 +27,17 @@ const FloatingGalleryButton = () => {
useHotkeys(
'f',
() => {
if (shouldShowGallery || shouldShowOptionsPanel) {
dispatch(setShouldShowOptionsPanel(false));
if (shouldShowGallery || shouldShowParametersPanel) {
dispatch(setShouldShowParametersPanel(false));
dispatch(setShouldShowGallery(false));
} else {
dispatch(setShouldShowOptionsPanel(true));
dispatch(setShouldShowParametersPanel(true));
dispatch(setShouldShowGallery(true));
}
if (shouldPinGallery || shouldPinOptionsPanel)
if (shouldPinGallery || shouldPinParametersPanel)
setTimeout(() => dispatch(setDoesCanvasNeedScaling(true)), 400);
},
[shouldShowGallery, shouldShowOptionsPanel]
[shouldShowGallery, shouldShowParametersPanel]
);
return shouldShowGalleryButton ? (

View File

@@ -1,36 +1,32 @@
import { createSelector } from '@reduxjs/toolkit';
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAIIconButton from 'common/components/IAIIconButton';
import {
OptionsState,
setShouldShowOptionsPanel,
} from 'features/options/store/optionsSlice';
import CancelButton from 'features/options/components/ProcessButtons/CancelButton';
import InvokeButton from 'features/options/components/ProcessButtons/InvokeButton';
import { setShouldShowParametersPanel } from 'features/ui/store/uiSlice';
import CancelButton from 'features/parameters/components/ProcessButtons/CancelButton';
import InvokeButton from 'features/parameters/components/ProcessButtons/InvokeButton';
import _ from 'lodash';
import { setDoesCanvasNeedScaling } from 'features/canvas/store/canvasSlice';
import { FaSlidersH } from 'react-icons/fa';
import { activeTabNameSelector } from 'features/options/store/optionsSelectors';
import {
activeTabNameSelector,
uiSelector,
} from 'features/ui/store/uiSelectors';
import { useHotkeys } from 'react-hotkeys-hook';
import {
GalleryState,
setShouldShowGallery,
} from 'features/gallery/store/gallerySlice';
import { gallerySelector } from 'features/gallery/store/gallerySelectors';
export const floatingSelector = createSelector(
[
(state: RootState) => state.options,
(state: RootState) => state.gallery,
activeTabNameSelector,
],
(options: OptionsState, gallery: GalleryState, activeTabName) => {
[gallerySelector, uiSelector, activeTabNameSelector],
(gallery: GalleryState, ui, activeTabName) => {
const {
shouldPinOptionsPanel,
shouldShowOptionsPanel,
shouldHoldOptionsPanelOpen,
shouldPinParametersPanel,
shouldShowParametersPanel,
shouldHoldParametersPanelOpen,
shouldUseCanvasBetaLayout,
} = options;
} = ui;
const { shouldShowGallery, shouldPinGallery, shouldHoldGalleryOpen } =
gallery;
@@ -38,11 +34,11 @@ export const floatingSelector = createSelector(
const canvasBetaLayoutCheck =
shouldUseCanvasBetaLayout && activeTabName === 'unifiedCanvas';
const shouldShowOptionsPanelButton =
const shouldShowParametersPanelButton =
!canvasBetaLayoutCheck &&
!(
shouldShowOptionsPanel ||
(shouldHoldOptionsPanelOpen && !shouldPinOptionsPanel)
shouldShowParametersPanel ||
(shouldHoldParametersPanelOpen && !shouldPinParametersPanel)
) &&
['txt2img', 'img2img', 'unifiedCanvas'].includes(activeTabName);
@@ -52,13 +48,13 @@ export const floatingSelector = createSelector(
const shouldShowProcessButtons =
!canvasBetaLayoutCheck &&
(!shouldPinOptionsPanel || !shouldShowOptionsPanel);
(!shouldPinParametersPanel || !shouldShowParametersPanel);
return {
shouldPinOptionsPanel,
shouldPinParametersPanel,
shouldShowProcessButtons,
shouldShowOptionsPanelButton,
shouldShowOptionsPanel,
shouldShowParametersPanelButton,
shouldShowParametersPanel,
shouldShowGallery,
shouldPinGallery,
shouldShowGalleryButton,
@@ -67,20 +63,20 @@ export const floatingSelector = createSelector(
{ memoizeOptions: { resultEqualityCheck: _.isEqual } }
);
const FloatingOptionsPanelButtons = () => {
const FloatingParametersPanelButtons = () => {
const dispatch = useAppDispatch();
const {
shouldShowOptionsPanel,
shouldShowOptionsPanelButton,
shouldShowParametersPanel,
shouldShowParametersPanelButton,
shouldShowProcessButtons,
shouldPinOptionsPanel,
shouldPinParametersPanel,
shouldShowGallery,
shouldPinGallery,
} = useAppSelector(floatingSelector);
const handleShowOptionsPanel = () => {
dispatch(setShouldShowOptionsPanel(true));
if (shouldPinOptionsPanel) {
dispatch(setShouldShowParametersPanel(true));
if (shouldPinParametersPanel) {
setTimeout(() => dispatch(setDoesCanvasNeedScaling(true)), 400);
}
};
@@ -88,20 +84,20 @@ const FloatingOptionsPanelButtons = () => {
useHotkeys(
'f',
() => {
if (shouldShowGallery || shouldShowOptionsPanel) {
dispatch(setShouldShowOptionsPanel(false));
if (shouldShowGallery || shouldShowParametersPanel) {
dispatch(setShouldShowParametersPanel(false));
dispatch(setShouldShowGallery(false));
} else {
dispatch(setShouldShowOptionsPanel(true));
dispatch(setShouldShowParametersPanel(true));
dispatch(setShouldShowGallery(true));
}
if (shouldPinGallery || shouldPinOptionsPanel)
if (shouldPinGallery || shouldPinParametersPanel)
setTimeout(() => dispatch(setDoesCanvasNeedScaling(true)), 400);
},
[shouldShowGallery, shouldShowOptionsPanel]
[shouldShowGallery, shouldShowParametersPanel]
);
return shouldShowOptionsPanelButton ? (
return shouldShowParametersPanelButton ? (
<div className="show-hide-button-options">
<IAIIconButton
tooltip="Show Options Panel (O)"
@@ -121,4 +117,4 @@ const FloatingOptionsPanelButtons = () => {
) : null;
};
export default FloatingOptionsPanelButtons;
export default FloatingParametersPanelButtons;

View File

@@ -6,7 +6,7 @@ import InitImagePreview from './InitImagePreview';
const ImageToImageDisplay = () => {
const initialImage = useAppSelector(
(state: RootState) => state.options.initialImage
(state: RootState) => state.generation.initialImage
);
const { currentImage } = useAppSelector((state: RootState) => state.gallery);

View File

@@ -0,0 +1,86 @@
import { Flex } from '@chakra-ui/react';
import { Feature } from 'app/features';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import FaceRestoreSettings from 'features/parameters/components/AdvancedParameters/FaceRestore/FaceRestoreSettings';
import FaceRestoreToggle from 'features/parameters/components/AdvancedParameters/FaceRestore/FaceRestoreToggle';
import ImageFit from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageFit';
import ImageToImageStrength from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageStrength';
import ImageToImageOutputSettings from 'features/parameters/components/AdvancedParameters/Output/ImageToImageOutputSettings';
import SeedSettings from 'features/parameters/components/AdvancedParameters/Seed/SeedSettings';
import UpscaleSettings from 'features/parameters/components/AdvancedParameters/Upscale/UpscaleSettings';
import UpscaleToggle from 'features/parameters/components/AdvancedParameters/Upscale/UpscaleToggle';
import GenerateVariationsToggle from 'features/parameters/components/AdvancedParameters/Variations/GenerateVariations';
import VariationsSettings from 'features/parameters/components/AdvancedParameters/Variations/VariationsSettings';
import MainSettings from 'features/parameters/components/MainParameters/MainParameters';
import ParametersAccordion from 'features/parameters/components/ParametersAccordion';
import ProcessButtons from 'features/parameters/components/ProcessButtons/ProcessButtons';
import NegativePromptInput from 'features/parameters/components/PromptInput/NegativePromptInput';
import PromptInput from 'features/parameters/components/PromptInput/PromptInput';
import { setHiresFix } from 'features/parameters/store/postprocessingSlice';
import InvokeOptionsPanel from 'features/ui/components/InvokeParametersPanel';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
export default function ImageToImagePanel() {
const { t } = useTranslation();
const imageToImageAccordions = {
seed: {
header: `${t('parameters:seed')}`,
feature: Feature.SEED,
content: <SeedSettings />,
},
variations: {
header: `${t('parameters:variations')}`,
feature: Feature.VARIATIONS,
content: <VariationsSettings />,
additionalHeaderComponents: <GenerateVariationsToggle />,
},
face_restore: {
header: `${t('parameters:faceRestoration')}`,
feature: Feature.FACE_CORRECTION,
content: <FaceRestoreSettings />,
additionalHeaderComponents: <FaceRestoreToggle />,
},
upscale: {
header: `${t('parameters:upscaling')}`,
feature: Feature.UPSCALE,
content: <UpscaleSettings />,
additionalHeaderComponents: <UpscaleToggle />,
},
other: {
header: `${t('parameters:otherOptions')}`,
feature: Feature.OTHER,
content: <ImageToImageOutputSettings />,
},
};
const dispatch = useAppDispatch();
const activeTabName = useAppSelector(activeTabNameSelector);
useEffect(() => {
if (activeTabName === 'img2img') {
const handleChangeHiresFix = () => dispatch(setHiresFix(false));
handleChangeHiresFix();
}
}, [activeTabName, dispatch]);
return (
<InvokeOptionsPanel>
<Flex flexDir="column" rowGap="0.5rem">
<PromptInput />
<NegativePromptInput />
</Flex>
<ProcessButtons />
<MainSettings />
<ImageToImageStrength
label={t('parameters:img2imgStrength')}
styleClass="main-settings-block image-to-image-strength-main-option"
/>
<ImageFit />
<ParametersAccordion accordionInfo={imageToImageAccordions} />
</InvokeOptionsPanel>
);
}

View File

@@ -2,12 +2,12 @@ import { Image, useToast } from '@chakra-ui/react';
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import ImageUploaderIconButton from 'common/components/ImageUploaderIconButton';
import { clearInitialImage } from 'features/options/store/optionsSlice';
import { clearInitialImage } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next';
export default function InitImagePreview() {
const initialImage = useAppSelector(
(state: RootState) => state.options.initialImage
(state: RootState) => state.generation.initialImage
);
const { t } = useTranslation();
@@ -29,7 +29,7 @@ export default function InitImagePreview() {
return (
<>
<div className="init-image-preview-header">
<h2>{t('options:initialImage')}</h2>
<h2>{t('parameters:initialImage')}</h2>
<ImageUploaderIconButton />
</div>
{initialImage && (

View File

@@ -5,7 +5,7 @@ import { useAppSelector } from 'app/storeHooks';
export default function InitialImageOverlay() {
const initialImage = useAppSelector(
(state: RootState) => state.options.initialImage
(state: RootState) => state.generation.initialImage
);
return initialImage ? (

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