diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index 319a920025..94dff3934a 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -450,7 +450,7 @@ "cfgScale": "CFG Scale", "width": "Width", "height": "Height", - "sampler": "Sampler", + "scheduler": "Scheduler", "seed": "Seed", "imageToImage": "Image to Image", "randomizeSeed": "Randomize Seed", diff --git a/invokeai/frontend/web/src/app/components/App.tsx b/invokeai/frontend/web/src/app/components/App.tsx index e1727eee64..40554356b1 100644 --- a/invokeai/frontend/web/src/app/components/App.tsx +++ b/invokeai/frontend/web/src/app/components/App.tsx @@ -6,7 +6,7 @@ import FloatingGalleryButton from 'features/ui/components/FloatingGalleryButton' import FloatingParametersPanelButtons from 'features/ui/components/FloatingParametersPanelButtons'; import { Box, Flex, Grid, Portal } from '@chakra-ui/react'; import { APP_HEIGHT, APP_WIDTH } from 'theme/util/constants'; -import GalleryDrawer from 'features/gallery/components/ImageGalleryPanel'; +import GalleryDrawer from 'features/gallery/components/GalleryPanel'; import Lightbox from 'features/lightbox/components/Lightbox'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { memo, ReactNode, useCallback, useEffect, useState } from 'react'; @@ -22,7 +22,6 @@ import { languageSelector } from 'features/system/store/systemSelectors'; import i18n from 'i18n'; import Toaster from './Toaster'; import GlobalHotkeys from './GlobalHotkeys'; -import AuxiliaryProgressIndicator from './AuxiliaryProgressIndicator'; const DEFAULT_CONFIG = {}; diff --git a/invokeai/frontend/web/src/app/constants.ts b/invokeai/frontend/web/src/app/constants.ts index b74c67befd..d312d725ba 100644 --- a/invokeai/frontend/web/src/app/constants.ts +++ b/invokeai/frontend/web/src/app/constants.ts @@ -1,6 +1,6 @@ // TODO: use Enums? -export const SCHEDULERS: Array = [ +export const SCHEDULERS = [ 'ddim', 'lms', 'euler', @@ -17,7 +17,12 @@ export const SCHEDULERS: Array = [ 'heun', 'heun_k', 'unipc', -]; +] as const; + +export type Scheduler = (typeof SCHEDULERS)[number]; + +export const isScheduler = (x: string): x is Scheduler => + SCHEDULERS.includes(x as Scheduler); // Valid image widths export const WIDTHS: Array = Array.from(Array(64)).map( diff --git a/invokeai/frontend/web/src/features/canvas/store/canvasPersistDenylist.ts b/invokeai/frontend/web/src/features/canvas/store/canvasPersistDenylist.ts index 1f44b43021..dc0df55ad0 100644 --- a/invokeai/frontend/web/src/features/canvas/store/canvasPersistDenylist.ts +++ b/invokeai/frontend/web/src/features/canvas/store/canvasPersistDenylist.ts @@ -3,18 +3,8 @@ import { CanvasState } from './canvasTypes'; /** * Canvas slice persist denylist */ -const itemsToDenylist: (keyof CanvasState)[] = [ - 'cursorPosition', - 'isCanvasInitialized', - 'doesCanvasNeedScaling', -]; - export const canvasPersistDenylist: (keyof CanvasState)[] = [ 'cursorPosition', 'isCanvasInitialized', 'doesCanvasNeedScaling', ]; - -export const canvasDenylist = itemsToDenylist.map( - (denylistItem) => `canvas.${denylistItem}` -); diff --git a/invokeai/frontend/web/src/features/gallery/components/CurrentImageButtons.tsx b/invokeai/frontend/web/src/features/gallery/components/CurrentImageButtons.tsx index 980c317ac3..65e9dcb43c 100644 --- a/invokeai/frontend/web/src/features/gallery/components/CurrentImageButtons.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/CurrentImageButtons.tsx @@ -47,10 +47,7 @@ import { FaTrash, FaWrench, } from 'react-icons/fa'; -import { - gallerySelector, - selectedImageSelector, -} from '../store/gallerySelectors'; +import { gallerySelector } from '../store/gallerySelectors'; import DeleteImageModal from './DeleteImageModal'; import { useCallback } from 'react'; import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale'; @@ -73,15 +70,15 @@ const currentImageButtonsSelector = createSelector( uiSelector, lightboxSelector, activeTabNameSelector, - selectedImageSelector, ], - (system, gallery, postprocessing, ui, lightbox, activeTabName, image) => { + (system, gallery, postprocessing, ui, lightbox, activeTabName) => { const { isProcessing, isConnected, isGFPGANAvailable, isESRGANAvailable, shouldConfirmOnDelete, + progressImage, } = system; const { upscalingLevel, facetoolStrength } = postprocessing; @@ -90,7 +87,7 @@ const currentImageButtonsSelector = createSelector( const { shouldShowImageDetails, shouldHidePreview } = ui; - const { intermediateImage, currentImage } = gallery; + const { selectedImage } = gallery; return { canDeleteImage: isConnected && !isProcessing, @@ -101,15 +98,14 @@ const currentImageButtonsSelector = createSelector( isESRGANAvailable, upscalingLevel, facetoolStrength, - shouldDisableToolbarButtons: Boolean(intermediateImage) || !currentImage, - currentImage, + shouldDisableToolbarButtons: Boolean(progressImage) || !selectedImage, shouldShowImageDetails, activeTabName, isLightboxOpen, shouldHidePreview, - image, - seed: image?.metadata?.invokeai?.node?.seed, - prompt: image?.metadata?.invokeai?.node?.prompt, + image: selectedImage, + seed: selectedImage?.metadata?.invokeai?.node?.seed, + prompt: selectedImage?.metadata?.invokeai?.node?.prompt, }; }, { diff --git a/invokeai/frontend/web/src/features/gallery/components/CurrentImageDisplay.tsx b/invokeai/frontend/web/src/features/gallery/components/CurrentImageDisplay.tsx index 9b928d45c4..5810c599c1 100644 --- a/invokeai/frontend/web/src/features/gallery/components/CurrentImageDisplay.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/CurrentImageDisplay.tsx @@ -4,18 +4,18 @@ import { useAppSelector } from 'app/store/storeHooks'; import { systemSelector } from 'features/system/store/systemSelectors'; import { isEqual } from 'lodash-es'; -import { selectedImageSelector } from '../store/gallerySelectors'; +import { gallerySelector } from '../store/gallerySelectors'; import CurrentImageButtons from './CurrentImageButtons'; import CurrentImagePreview from './CurrentImagePreview'; import { FaImage } from 'react-icons/fa'; export const currentImageDisplaySelector = createSelector( - [systemSelector, selectedImageSelector], - (system, selectedImage) => { + [systemSelector, gallerySelector], + (system, gallery) => { const { progressImage } = system; return { - hasAnImageToDisplay: selectedImage || progressImage, + hasAnImageToDisplay: gallery.selectedImage || progressImage, }; }, { diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryPanel.tsx b/invokeai/frontend/web/src/features/gallery/components/GalleryPanel.tsx similarity index 52% rename from invokeai/frontend/web/src/features/gallery/components/ImageGalleryPanel.tsx rename to invokeai/frontend/web/src/features/gallery/components/GalleryPanel.tsx index cfb6ba0914..f4bfee3ae1 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryPanel.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/GalleryPanel.tsx @@ -1,10 +1,6 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { gallerySelector } from 'features/gallery/store/gallerySelectors'; -import { - // selectNextImage, - // selectPrevImage, - setGalleryImageMinimumWidth, -} from 'features/gallery/store/gallerySlice'; +import { setGalleryImageMinimumWidth } from 'features/gallery/store/gallerySlice'; import { clamp, isEqual } from 'lodash-es'; import { useHotkeys } from 'react-hotkeys-hook'; @@ -23,20 +19,7 @@ import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvas import { lightboxSelector } from 'features/lightbox/store/lightboxSelectors'; import { memo } from 'react'; -// const GALLERY_TAB_WIDTHS: Record< -// InvokeTabName, -// { galleryMinWidth: number; galleryMaxWidth: number } -// > = { -// txt2img: { galleryMinWidth: 200, galleryMaxWidth: 500 }, -// img2img: { galleryMinWidth: 200, galleryMaxWidth: 500 }, -// generate: { galleryMinWidth: 200, galleryMaxWidth: 500 }, -// unifiedCanvas: { galleryMinWidth: 200, galleryMaxWidth: 200 }, -// nodes: { galleryMinWidth: 200, galleryMaxWidth: 500 }, -// postprocessing: { galleryMinWidth: 200, galleryMaxWidth: 500 }, -// training: { galleryMinWidth: 200, galleryMaxWidth: 500 }, -// }; - -const galleryPanelSelector = createSelector( +const selector = createSelector( [ activeTabNameSelector, uiSelector, @@ -76,41 +59,13 @@ const GalleryDrawer = () => { // isStaging, // isResizable, // isLightboxOpen, - } = useAppSelector(galleryPanelSelector); - - // const handleSetShouldPinGallery = () => { - // dispatch(togglePinGalleryPanel()); - // dispatch(requestCanvasRescale()); - // }; - - // const handleToggleGallery = () => { - // dispatch(toggleGalleryPanel()); - // shouldPinGallery && dispatch(requestCanvasRescale()); - // }; + } = useAppSelector(selector); const handleCloseGallery = () => { dispatch(setShouldShowGallery(false)); shouldPinGallery && dispatch(requestCanvasRescale()); }; - // const resolution = useResolution(); - - // useHotkeys( - // 'g', - // () => { - // handleToggleGallery(); - // }, - // [shouldPinGallery] - // ); - - // useHotkeys( - // 'shift+g', - // () => { - // handleSetShouldPinGallery(); - // }, - // [shouldPinGallery] - // ); - useHotkeys( 'esc', () => { @@ -155,54 +110,6 @@ const GalleryDrawer = () => { [galleryImageMinimumWidth] ); - // const calcGalleryMinHeight = () => { - // if (resolution === 'desktop') return; - // return 300; - // }; - - // const imageGalleryContent = () => { - // return ( - // - // - // - // ); - // }; - - // const resizableImageGalleryContent = () => { - // return ( - // - // - // - // ); - // }; - - // const renderImageGallery = () => { - // if (['mobile', 'tablet'].includes(resolution)) return imageGalleryContent(); - // return resizableImageGalleryContent(); - // }; - if (shouldPinGallery) { return null; } @@ -218,8 +125,6 @@ const GalleryDrawer = () => { ); - - // return renderImageGallery(); }; export default memo(GalleryDrawer); diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx index b37cd5ca62..9770ed5887 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx @@ -15,10 +15,7 @@ import IAICheckbox from 'common/components/IAICheckbox'; import IAIIconButton from 'common/components/IAIIconButton'; import IAIPopover from 'common/components/IAIPopover'; import IAISlider from 'common/components/IAISlider'; -import { - gallerySelector, - imageGallerySelector, -} from 'features/gallery/store/gallerySelectors'; +import { gallerySelector } from 'features/gallery/store/gallerySelectors'; import { setCurrentCategory, setGalleryImageMinimumWidth, @@ -57,11 +54,12 @@ import { Virtuoso, VirtuosoGrid } from 'react-virtuoso'; import { Image as ImageType } from 'app/types/invokeai'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import GalleryProgressImage from './GalleryProgressImage'; +import { uiSelector } from 'features/ui/store/uiSelectors'; const GALLERY_SHOW_BUTTONS_MIN_WIDTH = 290; const PROGRESS_IMAGE_PLACEHOLDER = 'PROGRESS_IMAGE_PLACEHOLDER'; -const selector = createSelector( +const categorySelector = createSelector( [(state: RootState) => state], (state) => { const { results, uploads, system, gallery } = state; @@ -92,6 +90,33 @@ const selector = createSelector( defaultSelectorOptions ); +const mainSelector = createSelector( + [gallerySelector, uiSelector], + (gallery, ui) => { + const { + currentCategory, + galleryImageMinimumWidth, + galleryImageObjectFit, + shouldAutoSwitchToNewImages, + shouldUseSingleGalleryColumn, + selectedImage, + } = gallery; + + const { shouldPinGallery } = ui; + + return { + currentCategory, + shouldPinGallery, + galleryImageMinimumWidth, + galleryImageObjectFit, + shouldAutoSwitchToNewImages, + shouldUseSingleGalleryColumn, + selectedImage, + }; + }, + defaultSelectorOptions +); + const ImageGalleryContent = () => { const dispatch = useAppDispatch(); const { t } = useTranslation(); @@ -113,7 +138,6 @@ const ImageGalleryContent = () => { }); const { - // images, currentCategory, shouldPinGallery, galleryImageMinimumWidth, @@ -121,10 +145,10 @@ const ImageGalleryContent = () => { shouldAutoSwitchToNewImages, shouldUseSingleGalleryColumn, selectedImage, - } = useAppSelector(imageGallerySelector); + } = useAppSelector(mainSelector); const { images, areMoreImagesAvailable, isLoading } = - useAppSelector(selector); + useAppSelector(categorySelector); const handleClickLoadMore = () => { if (currentCategory === 'results') { diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageMetaDataViewer/ImageMetadataViewer.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageMetaDataViewer/ImageMetadataViewer.tsx index 4f34100fd6..c23412a87d 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageMetaDataViewer/ImageMetadataViewer.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageMetaDataViewer/ImageMetadataViewer.tsx @@ -19,7 +19,7 @@ import { setHeight, setImg2imgStrength, setPerlin, - setSampler, + setScheduler, setSeamless, setSeed, setSeedWeights, @@ -202,9 +202,9 @@ const ImageMetadataViewer = memo(({ image }: ImageMetadataViewerProps) => { )} {node.scheduler && ( dispatch(setSampler(node.scheduler))} + onClick={() => dispatch(setScheduler(node.scheduler))} /> )} {node.steps && ( diff --git a/invokeai/frontend/web/src/features/gallery/store/galleryPersistDenylist.ts b/invokeai/frontend/web/src/features/gallery/store/galleryPersistDenylist.ts index dcec4fa373..49f51d5a80 100644 --- a/invokeai/frontend/web/src/features/gallery/store/galleryPersistDenylist.ts +++ b/invokeai/frontend/web/src/features/gallery/store/galleryPersistDenylist.ts @@ -3,16 +3,7 @@ import { GalleryState } from './gallerySlice'; /** * Gallery slice persist denylist */ -const itemsToDenylist: (keyof GalleryState)[] = [ - 'currentCategory', - 'shouldAutoSwitchToNewImages', -]; - export const galleryPersistDenylist: (keyof GalleryState)[] = [ 'currentCategory', 'shouldAutoSwitchToNewImages', ]; - -export const galleryDenylist = itemsToDenylist.map( - (denylistItem) => `gallery.${denylistItem}` -); diff --git a/invokeai/frontend/web/src/features/gallery/store/gallerySelectors.ts b/invokeai/frontend/web/src/features/gallery/store/gallerySelectors.ts index 3eeb2aa933..3c7e366bf7 100644 --- a/invokeai/frontend/web/src/features/gallery/store/gallerySelectors.ts +++ b/invokeai/frontend/web/src/features/gallery/store/gallerySelectors.ts @@ -1,83 +1,3 @@ -import { createSelector } from '@reduxjs/toolkit'; import { RootState } from 'app/store/store'; -import { lightboxSelector } from 'features/lightbox/store/lightboxSelectors'; - -import { - activeTabNameSelector, - uiSelector, -} from 'features/ui/store/uiSelectors'; -import { isEqual } from 'lodash-es'; -import { selectResultsById, selectResultsEntities } from './resultsSlice'; -import { selectUploadsAll, selectUploadsById } from './uploadsSlice'; export const gallerySelector = (state: RootState) => state.gallery; - -export const imageGallerySelector = createSelector( - [ - (state: RootState) => state, - gallerySelector, - uiSelector, - lightboxSelector, - activeTabNameSelector, - ], - (state, gallery, ui, lightbox, activeTabName) => { - const { - currentCategory, - galleryImageMinimumWidth, - galleryImageObjectFit, - shouldAutoSwitchToNewImages, - galleryWidth, - shouldUseSingleGalleryColumn, - selectedImage, - } = gallery; - - const { shouldPinGallery } = ui; - - const { isLightboxOpen } = lightbox; - - const images = - currentCategory === 'results' - ? selectResultsEntities(state) - : selectUploadsAll(state); - - return { - shouldPinGallery, - galleryImageMinimumWidth, - galleryImageObjectFit, - galleryGridTemplateColumns: shouldUseSingleGalleryColumn - ? 'auto' - : `repeat(auto-fill, minmax(${galleryImageMinimumWidth}px, auto))`, - shouldAutoSwitchToNewImages, - currentCategory, - images, - galleryWidth, - shouldEnableResize: - isLightboxOpen || - (activeTabName === 'unifiedCanvas' && shouldPinGallery) - ? false - : true, - shouldUseSingleGalleryColumn, - selectedImage, - }; - }, - { - memoizeOptions: { - resultEqualityCheck: isEqual, - }, - } -); - -export const selectedImageSelector = createSelector( - [(state: RootState) => state, gallerySelector], - (state, gallery) => { - const selectedImage = gallery.selectedImage; - - if (selectedImage?.type === 'results') { - return selectResultsById(state, selectedImage.name); - } - - if (selectedImage?.type === 'uploads') { - return selectUploadsById(state, selectedImage.name); - } - } -); diff --git a/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts b/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts index 1ae5ee8aee..96c3486b50 100644 --- a/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts +++ b/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts @@ -10,14 +10,10 @@ import { type GalleryImageObjectFitType = 'contain' | 'cover'; export interface GalleryState { - /** - * The selected image - */ selectedImage?: Image; galleryImageMinimumWidth: number; galleryImageObjectFit: GalleryImageObjectFitType; shouldAutoSwitchToNewImages: boolean; - galleryWidth: number; shouldUseSingleGalleryColumn: boolean; currentCategory: 'results' | 'uploads'; } @@ -26,7 +22,6 @@ export const initialGalleryState: GalleryState = { galleryImageMinimumWidth: 64, galleryImageObjectFit: 'cover', shouldAutoSwitchToNewImages: true, - galleryWidth: 300, shouldUseSingleGalleryColumn: false, currentCategory: 'results', }; @@ -58,9 +53,6 @@ export const gallerySlice = createSlice({ ) => { state.currentCategory = action.payload; }, - setGalleryWidth: (state, action: PayloadAction) => { - state.galleryWidth = action.payload; - }, setShouldUseSingleGalleryColumn: ( state, action: PayloadAction @@ -93,24 +85,28 @@ export const gallerySlice = createSlice({ builder.addCase(receivedResultImagesPage.fulfilled, (state, action) => { // rehydrate selectedImage URL when results list comes in // solves case when outdated URL is in local storage - if (state.selectedImage) { + const selectedImage = state.selectedImage; + if (selectedImage) { const selectedImageInResults = action.payload.items.find( - (image) => image.image_name === state.selectedImage!.name + (image) => image.image_name === selectedImage.name ); if (selectedImageInResults) { - state.selectedImage.url = selectedImageInResults.image_url; + selectedImage.url = selectedImageInResults.image_url; + state.selectedImage = selectedImage; } } }); builder.addCase(receivedUploadImagesPage.fulfilled, (state, action) => { // rehydrate selectedImage URL when results list comes in // solves case when outdated URL is in local storage - if (state.selectedImage) { + const selectedImage = state.selectedImage; + if (selectedImage) { const selectedImageInResults = action.payload.items.find( - (image) => image.image_name === state.selectedImage!.name + (image) => image.image_name === selectedImage.name ); if (selectedImageInResults) { - state.selectedImage.url = selectedImageInResults.image_url; + selectedImage.url = selectedImageInResults.image_url; + state.selectedImage = selectedImage; } } }); @@ -122,7 +118,6 @@ export const { setGalleryImageMinimumWidth, setGalleryImageObjectFit, setShouldAutoSwitchToNewImages, - setGalleryWidth, setShouldUseSingleGalleryColumn, setCurrentCategory, } = gallerySlice.actions; diff --git a/invokeai/frontend/web/src/features/gallery/store/resultsPersistDenylist.ts b/invokeai/frontend/web/src/features/gallery/store/resultsPersistDenylist.ts index 4b8ccac6a7..1c3d8aaaec 100644 --- a/invokeai/frontend/web/src/features/gallery/store/resultsPersistDenylist.ts +++ b/invokeai/frontend/web/src/features/gallery/store/resultsPersistDenylist.ts @@ -3,12 +3,6 @@ import { ResultsState } from './resultsSlice'; /** * Results slice persist denylist * - * Currently denylisting results slice entirely, see persist config in store.ts + * Currently denylisting results slice entirely, see `serialize.ts` */ -const itemsToDenylist: (keyof ResultsState)[] = []; - export const resultsPersistDenylist: (keyof ResultsState)[] = []; - -export const resultsDenylist = itemsToDenylist.map( - (denylistItem) => `results.${denylistItem}` -); diff --git a/invokeai/frontend/web/src/features/gallery/store/uploadsPersistDenylist.ts b/invokeai/frontend/web/src/features/gallery/store/uploadsPersistDenylist.ts index 97e23660a9..296e8b2057 100644 --- a/invokeai/frontend/web/src/features/gallery/store/uploadsPersistDenylist.ts +++ b/invokeai/frontend/web/src/features/gallery/store/uploadsPersistDenylist.ts @@ -3,11 +3,6 @@ import { UploadsState } from './uploadsSlice'; /** * Uploads slice persist denylist * - * Currently denylisting uploads slice entirely, see persist config in store.ts + * Currently denylisting uploads slice entirely, see `serialize.ts` */ -const itemsToDenylist: (keyof UploadsState)[] = []; export const uploadsPersistDenylist: (keyof UploadsState)[] = []; - -export const uploadsDenylist = itemsToDenylist.map( - (denylistItem) => `uploads.${denylistItem}` -); diff --git a/invokeai/frontend/web/src/features/lightbox/store/lightboxPersistDenylist.ts b/invokeai/frontend/web/src/features/lightbox/store/lightboxPersistDenylist.ts index 194fe50ca3..b8a1d12f44 100644 --- a/invokeai/frontend/web/src/features/lightbox/store/lightboxPersistDenylist.ts +++ b/invokeai/frontend/web/src/features/lightbox/store/lightboxPersistDenylist.ts @@ -3,11 +3,6 @@ import { LightboxState } from './lightboxSlice'; /** * Lightbox slice persist denylist */ -const itemsToDenylist: (keyof LightboxState)[] = ['isLightboxOpen']; export const lightboxPersistDenylist: (keyof LightboxState)[] = [ 'isLightboxOpen', ]; - -export const lightboxDenylist = itemsToDenylist.map( - (denylistItem) => `lightbox.${denylistItem}` -); diff --git a/invokeai/frontend/web/src/features/nodes/store/nodesPersistDenylist.ts b/invokeai/frontend/web/src/features/nodes/store/nodesPersistDenylist.ts index 36da41f56c..ba5c120dae 100644 --- a/invokeai/frontend/web/src/features/nodes/store/nodesPersistDenylist.ts +++ b/invokeai/frontend/web/src/features/nodes/store/nodesPersistDenylist.ts @@ -3,12 +3,7 @@ import { NodesState } from './nodesSlice'; /** * Nodes slice persist denylist */ -const itemsToDenylist: (keyof NodesState)[] = ['schema', 'invocationTemplates']; export const nodesPersistDenylist: (keyof NodesState)[] = [ 'schema', 'invocationTemplates', ]; - -export const nodesDenylist = itemsToDenylist.map( - (denylistItem) => `nodes.${denylistItem}` -); diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasGraph.ts index 88ccc375c5..3615f7d298 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasGraph.ts @@ -26,16 +26,18 @@ const buildBaseNode = ( | ImageToImageInvocation | InpaintInvocation | undefined => { + const dimensionsOverride = state.canvas.boundingBoxDimensions; + if (nodeType === 'txt2img') { - return buildTxt2ImgNode(state, state.canvas.boundingBoxDimensions); + return buildTxt2ImgNode(state, dimensionsOverride); } if (nodeType === 'img2img') { - return buildImg2ImgNode(state, state.canvas.boundingBoxDimensions); + return buildImg2ImgNode(state, dimensionsOverride); } if (nodeType === 'inpaint' || nodeType === 'outpaint') { - return buildInpaintNode(state, state.canvas.boundingBoxDimensions); + return buildInpaintNode(state, dimensionsOverride); } }; diff --git a/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildImageToImageNode.ts b/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildImageToImageNode.ts index c8b328370a..02480289d4 100644 --- a/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildImageToImageNode.ts +++ b/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildImageToImageNode.ts @@ -25,7 +25,7 @@ export const buildImg2ImgNode = ( width, height, cfgScale, - sampler, + scheduler, model, img2imgStrength: strength, shouldFitToWidthHeight: fit, @@ -43,14 +43,14 @@ export const buildImg2ImgNode = ( width, height, cfg_scale: cfgScale, - scheduler: sampler as ImageToImageInvocation['scheduler'], + scheduler, model, strength, fit, }; // on Canvas tab, we do not manually specific init image - if (activeTabName === 'img2img') { + if (activeTabName !== 'unifiedCanvas') { if (!initialImage) { // TODO: handle this more better throw 'no initial image'; diff --git a/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildInpaintNode.ts b/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildInpaintNode.ts index 9cd124ba9e..36658ef58f 100644 --- a/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildInpaintNode.ts +++ b/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildInpaintNode.ts @@ -1,17 +1,16 @@ import { v4 as uuidv4 } from 'uuid'; import { RootState } from 'app/store/store'; import { InpaintInvocation } from 'services/api'; -import { initialImageSelector } from 'features/parameters/store/generationSelectors'; import { O } from 'ts-toolbelt'; +import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; export const buildInpaintNode = ( state: RootState, overrides: O.Partial = {} ): InpaintInvocation => { const nodeId = uuidv4(); - const { generation, models } = state; - - const { selectedModelName } = models; + const { generation } = state; + const activeTabName = activeTabNameSelector(state); const { prompt, @@ -21,21 +20,15 @@ export const buildInpaintNode = ( width, height, cfgScale, - sampler, - seamless, + scheduler, + model, img2imgStrength: strength, shouldFitToWidthHeight: fit, shouldRandomizeSeed, + initialImage, } = generation; - const initialImage = initialImageSelector(state); - - if (!initialImage) { - // TODO: handle this - // throw 'no initial image'; - } - - const imageToImageNode: InpaintInvocation = { + const inpaintNode: InpaintInvocation = { id: nodeId, type: 'inpaint', prompt: `${prompt} [${negativePrompt}]`, @@ -43,25 +36,30 @@ export const buildInpaintNode = ( width, height, cfg_scale: cfgScale, - scheduler: sampler as InpaintInvocation['scheduler'], - seamless, - model: selectedModelName, - progress_images: true, - image: initialImage - ? { - image_name: initialImage.name, - image_type: initialImage.type, - } - : undefined, + scheduler, + model, strength, fit, }; - if (!shouldRandomizeSeed) { - imageToImageNode.seed = seed; + // on Canvas tab, we do not manually specific init image + if (activeTabName !== 'unifiedCanvas') { + if (!initialImage) { + // TODO: handle this more better + throw 'no initial image'; + } + + inpaintNode.image = { + image_name: initialImage.name, + image_type: initialImage.type, + }; } - Object.assign(imageToImageNode, overrides); + if (!shouldRandomizeSeed) { + inpaintNode.seed = seed; + } - return imageToImageNode; + Object.assign(inpaintNode, overrides); + + return inpaintNode; }; diff --git a/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildTextToImageNode.ts b/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildTextToImageNode.ts index fe76531c59..761c909776 100644 --- a/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildTextToImageNode.ts +++ b/invokeai/frontend/web/src/features/nodes/util/nodeBuilders/buildTextToImageNode.ts @@ -18,7 +18,7 @@ export const buildTxt2ImgNode = ( width, height, cfgScale: cfg_scale, - sampler, + scheduler, shouldRandomizeSeed, model, } = generation; @@ -31,7 +31,7 @@ export const buildTxt2ImgNode = ( width, height, cfg_scale, - scheduler: sampler as TextToImageInvocation['scheduler'], + scheduler, model, }; diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamSampler.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamScheduler.tsx similarity index 70% rename from invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamSampler.tsx rename to invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamScheduler.tsx index b3dd4a0f27..2b5db18d93 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamSampler.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamScheduler.tsx @@ -1,15 +1,15 @@ +import { Scheduler } from 'app/constants'; import { RootState } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAICustomSelect from 'common/components/IAICustomSelect'; -import IAISelect from 'common/components/IAISelect'; -import { setSampler } from 'features/parameters/store/generationSlice'; +import { setScheduler } from 'features/parameters/store/generationSlice'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; -import { ChangeEvent, memo, useCallback } from 'react'; +import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -const ParamSampler = () => { - const sampler = useAppSelector( - (state: RootState) => state.generation.sampler +const ParamScheduler = () => { + const scheduler = useAppSelector( + (state: RootState) => state.generation.scheduler ); const activeTabName = useAppSelector(activeTabNameSelector); @@ -28,15 +28,15 @@ const ParamSampler = () => { if (!v) { return; } - dispatch(setSampler(v)); + dispatch(setScheduler(v as Scheduler)); }, [dispatch] ); return ( { ); }; -export default memo(ParamSampler); +export default memo(ParamScheduler); diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamSchedulerAndModel.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamSchedulerAndModel.tsx index 489d4fad55..3b53f5005c 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamSchedulerAndModel.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamSchedulerAndModel.tsx @@ -1,13 +1,13 @@ import { Box, Flex } from '@chakra-ui/react'; import { memo } from 'react'; -import ParamSampler from './ParamSampler'; import ModelSelect from 'features/system/components/ModelSelect'; +import ParamScheduler from './ParamScheduler'; const ParamSchedulerAndModel = () => { return ( - + diff --git a/invokeai/frontend/web/src/features/parameters/store/generationPersistDenylist.ts b/invokeai/frontend/web/src/features/parameters/store/generationPersistDenylist.ts index ab3e77801e..7988a581c0 100644 --- a/invokeai/frontend/web/src/features/parameters/store/generationPersistDenylist.ts +++ b/invokeai/frontend/web/src/features/parameters/store/generationPersistDenylist.ts @@ -3,9 +3,4 @@ import { GenerationState } from './generationSlice'; /** * Generation slice persist denylist */ -const itemsToDenylist: (keyof GenerationState)[] = []; export const generationPersistDenylist: (keyof GenerationState)[] = []; - -export const generationDenylist = itemsToDenylist.map( - (denylistItem) => `generation.${denylistItem}` -); diff --git a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts index 604aabbc9b..f9e857e7e3 100644 --- a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts +++ b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts @@ -5,19 +5,19 @@ import promptToString from 'common/util/promptToString'; import { clamp, sample } from 'lodash-es'; import { setAllParametersReducer } from './setAllParametersReducer'; import { receivedModels } from 'services/thunks/model'; +import { Scheduler } from 'app/constants'; export interface GenerationState { cfgScale: number; height: number; img2imgStrength: number; infillMethod: string; - initialImage?: InvokeAI.Image; // can be an Image or url + initialImage?: InvokeAI.Image; iterations: number; - maskPath: string; perlin: number; prompt: string; negativePrompt: string; - sampler: string; + scheduler: Scheduler; seamBlur: number; seamSize: number; seamSteps: number; @@ -48,11 +48,10 @@ export const initialGenerationState: GenerationState = { img2imgStrength: 0.75, infillMethod: 'patchmatch', iterations: 1, - maskPath: '', perlin: 0, prompt: '', negativePrompt: '', - sampler: 'lms', + scheduler: 'lms', seamBlur: 16, seamSize: 96, seamSteps: 30, @@ -135,8 +134,8 @@ export const generationSlice = createSlice({ setWidth: (state, action: PayloadAction) => { state.width = action.payload; }, - setSampler: (state, action: PayloadAction) => { - state.sampler = action.payload; + setScheduler: (state, action: PayloadAction) => { + state.scheduler = action.payload; }, setSeed: (state, action: PayloadAction) => { state.seed = action.payload; @@ -145,9 +144,6 @@ export const generationSlice = createSlice({ setImg2imgStrength: (state, action: PayloadAction) => { state.img2imgStrength = action.payload; }, - setMaskPath: (state, action: PayloadAction) => { - state.maskPath = action.payload; - }, setSeamless: (state, action: PayloadAction) => { state.shouldUseSeamless = action.payload; }, @@ -163,19 +159,6 @@ export const generationSlice = createSlice({ resetSeed: (state) => { state.seed = -1; }, - setParameter: ( - state, - 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') { - temp.shouldRandomizeSeed = false; - } - return temp; - }, setShouldGenerateVariations: (state, action: PayloadAction) => { state.shouldGenerateVariations = action.payload; }, @@ -258,14 +241,11 @@ export const { setHeight, setImg2imgStrength, setInfillMethod, - // setInitialImage, setIterations, - setMaskPath, - setParameter, setPerlin, setPrompt, setNegativePrompt, - setSampler, + setScheduler, setSeamBlur, setSeamSize, setSeamSteps, diff --git a/invokeai/frontend/web/src/features/parameters/store/postprocessingPersistDenylist.ts b/invokeai/frontend/web/src/features/parameters/store/postprocessingPersistDenylist.ts index a6ba084c2e..dd70361904 100644 --- a/invokeai/frontend/web/src/features/parameters/store/postprocessingPersistDenylist.ts +++ b/invokeai/frontend/web/src/features/parameters/store/postprocessingPersistDenylist.ts @@ -3,9 +3,4 @@ import { PostprocessingState } from './postprocessingSlice'; /** * Postprocessing slice persist denylist */ -const itemsToDenylist: (keyof PostprocessingState)[] = []; export const postprocessingPersistDenylist: (keyof PostprocessingState)[] = []; - -export const postprocessingDenylist = itemsToDenylist.map( - (denylistItem) => `postprocessing.${denylistItem}` -); diff --git a/invokeai/frontend/web/src/features/parameters/store/setAllParametersReducer.ts b/invokeai/frontend/web/src/features/parameters/store/setAllParametersReducer.ts index 7b02647ebc..a816d358ce 100644 --- a/invokeai/frontend/web/src/features/parameters/store/setAllParametersReducer.ts +++ b/invokeai/frontend/web/src/features/parameters/store/setAllParametersReducer.ts @@ -2,6 +2,7 @@ import { Draft, PayloadAction } from '@reduxjs/toolkit'; import { Image } from 'app/types/invokeai'; import { GenerationState } from './generationSlice'; import { ImageToImageInvocation } from 'services/api'; +import { isScheduler } from 'app/constants'; export const setAllParametersReducer = ( state: Draft, @@ -34,7 +35,10 @@ export const setAllParametersReducer = ( state.prompt = String(prompt); } if (scheduler !== undefined) { - state.sampler = String(scheduler); + const schedulerString = String(scheduler); + if (isScheduler(schedulerString)) { + state.scheduler = schedulerString; + } } if (seed !== undefined) { state.seed = Number(seed); diff --git a/invokeai/frontend/web/src/features/system/store/modelSlice.ts b/invokeai/frontend/web/src/features/system/store/modelSlice.ts index 9c7194672d..ed38425872 100644 --- a/invokeai/frontend/web/src/features/system/store/modelSlice.ts +++ b/invokeai/frontend/web/src/features/system/store/modelSlice.ts @@ -13,26 +13,15 @@ export const modelsAdapter = createEntityAdapter({ sortComparer: (a, b) => a.name.localeCompare(b.name), }); -type AdditionalModelsState = { - selectedModelName: string; -}; - -export const initialModelsState = - modelsAdapter.getInitialState({ - selectedModelName: '', - }); +export const initialModelsState = modelsAdapter.getInitialState(); export type ModelsState = typeof initialModelsState; export const modelsSlice = createSlice({ name: 'models', - initialState: modelsAdapter.getInitialState(), - // initialState: initialModelsState, + initialState: initialModelsState, reducers: { modelAdded: modelsAdapter.upsertOne, - // modelSelected: (state, action: PayloadAction) => { - // state.selectedModelName = action.payload; - // }, }, extraReducers(builder) { /** @@ -41,32 +30,10 @@ export const modelsSlice = createSlice({ builder.addCase(receivedModels.fulfilled, (state, action) => { const models = action.payload; modelsAdapter.setAll(state, models); - - // If the current selected model is `''` or isn't actually in the list of models, - // choose a random model - // if ( - // !state.selectedModelName || - // !keys(models).includes(state.selectedModelName) - // ) { - // const randomModel = sample(models); - - // if (randomModel) { - // state.selectedModelName = randomModel.name; - // } else { - // state.selectedModelName = ''; - // } - // } }); }, }); -// export const selectedModelSelector = (state: RootState) => { -// const { selectedModelName } = state.models; -// const selectedModel = selectModelsById(state, selectedModelName); - -// return selectedModel ?? null; -// }; - export const { selectAll: selectModelsAll, selectById: selectModelsById, @@ -75,9 +42,6 @@ export const { selectTotal: selectModelsTotal, } = modelsAdapter.getSelectors((state) => state.models); -export const { - modelAdded, - // modelSelected -} = modelsSlice.actions; +export const { modelAdded } = modelsSlice.actions; export default modelsSlice.reducer; diff --git a/invokeai/frontend/web/src/features/system/store/modelsPersistDenylist.ts b/invokeai/frontend/web/src/features/system/store/modelsPersistDenylist.ts index 5b36a3f196..aa9fb057e1 100644 --- a/invokeai/frontend/web/src/features/system/store/modelsPersistDenylist.ts +++ b/invokeai/frontend/web/src/features/system/store/modelsPersistDenylist.ts @@ -3,9 +3,4 @@ import { ModelsState } from './modelSlice'; /** * Models slice persist denylist */ -const itemsToDenylist: (keyof ModelsState)[] = ['entities', 'ids']; export const modelsPersistDenylist: (keyof ModelsState)[] = ['entities', 'ids']; - -export const modelsDenylist = itemsToDenylist.map( - (denylistItem) => `models.${denylistItem}` -); diff --git a/invokeai/frontend/web/src/features/system/store/systemPersistDenylist.ts b/invokeai/frontend/web/src/features/system/store/systemPersistDenylist.ts index 62fe70b83f..c2481c29df 100644 --- a/invokeai/frontend/web/src/features/system/store/systemPersistDenylist.ts +++ b/invokeai/frontend/web/src/features/system/store/systemPersistDenylist.ts @@ -5,14 +5,12 @@ import { SystemState } from './systemSlice'; */ export const systemPersistDenylist: (keyof SystemState)[] = [ 'currentIteration', - 'currentStatus', 'currentStep', 'isCancelable', 'isConnected', 'isESRGANAvailable', 'isGFPGANAvailable', 'isProcessing', - 'socketId', 'totalIterations', 'totalSteps', 'openModel', diff --git a/invokeai/frontend/web/src/features/system/store/systemSlice.ts b/invokeai/frontend/web/src/features/system/store/systemSlice.ts index 6784447af8..7331fcdba9 100644 --- a/invokeai/frontend/web/src/features/system/store/systemSlice.ts +++ b/invokeai/frontend/web/src/features/system/store/systemSlice.ts @@ -143,73 +143,15 @@ export const systemSlice = createSlice({ setCurrentStatus: (state, action: PayloadAction) => { state.statusTranslationKey = action.payload; }, - errorOccurred: (state) => { - state.isProcessing = false; - state.isCancelable = true; - state.currentStep = 0; - state.totalSteps = 0; - state.currentIteration = 0; - state.totalIterations = 0; - state.currentStatusHasSteps = false; - state.statusTranslationKey = 'common.statusError'; - }, - setIsConnected: (state, action: PayloadAction) => { - state.isConnected = action.payload; - state.isProcessing = false; - state.isCancelable = true; - state.currentStep = 0; - state.totalSteps = 0; - state.currentIteration = 0; - state.totalIterations = 0; - state.currentStatusHasSteps = false; - }, setShouldConfirmOnDelete: (state, action: PayloadAction) => { state.shouldConfirmOnDelete = action.payload; }, setShouldDisplayGuides: (state, action: PayloadAction) => { state.shouldDisplayGuides = action.payload; }, - processingCanceled: (state) => { - state.isProcessing = false; - state.isCancelable = true; - state.currentStep = 0; - state.totalSteps = 0; - state.currentIteration = 0; - state.totalIterations = 0; - state.currentStatusHasSteps = false; - state.statusTranslationKey = 'common.statusProcessingCanceled'; - }, - generationRequested: (state) => { - state.isProcessing = true; - state.isCancelable = true; - state.currentStep = 0; - state.totalSteps = 0; - state.currentIteration = 0; - state.totalIterations = 0; - state.currentStatusHasSteps = false; - state.statusTranslationKey = 'common.statusPreparing'; - }, setIsCancelable: (state, action: PayloadAction) => { state.isCancelable = action.payload; }, - modelChangeRequested: (state) => { - state.statusTranslationKey = 'common.statusLoadingModel'; - state.isCancelable = false; - state.isProcessing = true; - state.currentStatusHasSteps = false; - }, - modelConvertRequested: (state) => { - state.statusTranslationKey = 'common.statusConvertingModel'; - state.isCancelable = false; - state.isProcessing = true; - state.currentStatusHasSteps = false; - }, - modelMergingRequested: (state) => { - state.statusTranslationKey = 'common.statusMergingModels'; - state.isCancelable = false; - state.isProcessing = true; - state.currentStatusHasSteps = false; - }, setEnableImageDebugging: (state, action: PayloadAction) => { state.enableImageDebugging = action.payload; }, @@ -219,14 +161,6 @@ export const systemSlice = createSlice({ clearToastQueue: (state) => { state.toastQueue = []; }, - setProcessingIndeterminateTask: ( - state, - action: PayloadAction - ) => { - state.isProcessing = true; - state.statusTranslationKey = action.payload; - state.currentStatusHasSteps = false; - }, setSearchFolder: (state, action: PayloadAction) => { state.searchFolder = action.payload; }, @@ -485,21 +419,13 @@ export const systemSlice = createSlice({ export const { setIsProcessing, - setIsConnected, setShouldConfirmOnDelete, setCurrentStatus, setShouldDisplayGuides, - processingCanceled, - errorOccurred, setIsCancelable, - modelChangeRequested, - modelConvertRequested, - modelMergingRequested, setEnableImageDebugging, - generationRequested, addToast, clearToastQueue, - setProcessingIndeterminateTask, setSearchFolder, setFoundModels, setOpenModel, diff --git a/invokeai/frontend/web/src/features/ui/components/ParametersDrawer.tsx b/invokeai/frontend/web/src/features/ui/components/ParametersDrawer.tsx index 7a969bc396..9c19d988fe 100644 --- a/invokeai/frontend/web/src/features/ui/components/ParametersDrawer.tsx +++ b/invokeai/frontend/web/src/features/ui/components/ParametersDrawer.tsx @@ -21,11 +21,7 @@ import UnifiedCanvasParameters from './tabs/UnifiedCanvas/UnifiedCanvasParameter const selector = createSelector( [uiSelector, activeTabNameSelector, lightboxSelector], (ui, activeTabName, lightbox) => { - const { - shouldPinParametersPanel, - shouldShowParametersPanel, - shouldShowImageParameters, - } = ui; + const { shouldPinParametersPanel, shouldShowParametersPanel } = ui; const { isLightboxOpen } = lightbox; @@ -33,7 +29,6 @@ const selector = createSelector( activeTabName, shouldPinParametersPanel, shouldShowParametersPanel, - shouldShowImageParameters, }; }, defaultSelectorOptions diff --git a/invokeai/frontend/web/src/features/ui/store/uiPersistDenylist.ts b/invokeai/frontend/web/src/features/ui/store/uiPersistDenylist.ts index 7f469e1f1a..9f6bd2dd73 100644 --- a/invokeai/frontend/web/src/features/ui/store/uiPersistDenylist.ts +++ b/invokeai/frontend/web/src/features/ui/store/uiPersistDenylist.ts @@ -3,11 +3,4 @@ import { UIState } from './uiTypes'; /** * UI slice persist denylist */ -const itemsToDenylist: (keyof UIState)[] = ['floatingProgressImageRect']; -export const uiPersistDenylist: (keyof UIState)[] = [ - 'floatingProgressImageRect', -]; - -export const uiDenylist = itemsToDenylist.map( - (denylistItem) => `ui.${denylistItem}` -); +export const uiPersistDenylist: (keyof UIState)[] = []; diff --git a/invokeai/frontend/web/src/features/ui/store/uiSlice.ts b/invokeai/frontend/web/src/features/ui/store/uiSlice.ts index 6c3eb95a31..4893bb3bf6 100644 --- a/invokeai/frontend/web/src/features/ui/store/uiSlice.ts +++ b/invokeai/frontend/web/src/features/ui/store/uiSlice.ts @@ -1,16 +1,14 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; import { setActiveTabReducer } from './extraReducers'; -import { InvokeTabName, tabMap } from './tabMap'; -import { AddNewModelType, Coordinates, Rect, UIState } from './uiTypes'; -import { initialImageSelected } from 'features/parameters/store/actions'; +import { InvokeTabName } from './tabMap'; +import { AddNewModelType, UIState } from './uiTypes'; import { initialImageChanged } from 'features/parameters/store/generationSlice'; import { SCHEDULERS } from 'app/constants'; export const initialUIState: UIState = { activeTab: 0, currentTheme: 'dark', - parametersPanelScrollPosition: 0, shouldPinParametersPanel: true, shouldShowParametersPanel: true, shouldShowImageDetails: false, @@ -21,13 +19,7 @@ export const initialUIState: UIState = { shouldPinGallery: true, shouldShowGallery: true, shouldHidePreview: false, - textTabAccordionState: [], - imageTabAccordionState: [], - canvasTabAccordionState: [], - floatingProgressImageRect: { x: 0, y: 0, width: 0, height: 0 }, - shouldShowProgressImages: false, shouldShowProgressInViewer: false, - shouldShowImageParameters: false, schedulers: SCHEDULERS, }; @@ -41,12 +33,6 @@ export const uiSlice = createSlice({ setCurrentTheme: (state, action: PayloadAction) => { state.currentTheme = action.payload; }, - setParametersPanelScrollPosition: ( - state, - action: PayloadAction - ) => { - state.parametersPanelScrollPosition = action.payload; - }, setShouldPinParametersPanel: (state, action: PayloadAction) => { state.shouldPinParametersPanel = action.payload; state.shouldShowParametersPanel = true; @@ -75,9 +61,6 @@ export const uiSlice = createSlice({ setAddNewModelUIOption: (state, action: PayloadAction) => { state.addNewModelUIOption = action.payload; }, - setShouldPinGallery: (state, action: PayloadAction) => { - state.shouldPinGallery = action.payload; - }, setShouldShowGallery: (state, action: PayloadAction) => { state.shouldShowGallery = action.payload; }, @@ -108,46 +91,9 @@ export const uiSlice = createSlice({ state.shouldShowParametersPanel = true; } }, - openAccordionItemsChanged: (state, action: PayloadAction) => { - if (tabMap[state.activeTab] === 'txt2img') { - state.textTabAccordionState = action.payload; - } - - if (tabMap[state.activeTab] === 'img2img') { - state.imageTabAccordionState = action.payload; - } - - if (tabMap[state.activeTab] === 'unifiedCanvas') { - state.canvasTabAccordionState = action.payload; - } - }, - floatingProgressImageMoved: (state, action: PayloadAction) => { - state.floatingProgressImageRect = { - ...state.floatingProgressImageRect, - ...action.payload, - }; - }, - floatingProgressImageResized: ( - state, - action: PayloadAction> - ) => { - state.floatingProgressImageRect = { - ...state.floatingProgressImageRect, - ...action.payload, - }; - }, - setShouldShowProgressImages: (state, action: PayloadAction) => { - state.shouldShowProgressImages = action.payload; - }, setShouldShowProgressInViewer: (state, action: PayloadAction) => { state.shouldShowProgressInViewer = action.payload; }, - shouldShowImageParametersChanged: ( - state, - action: PayloadAction - ) => { - state.shouldShowImageParameters = action.payload; - }, setSchedulers: (state, action: PayloadAction) => { state.schedulers = []; state.schedulers = action.payload; @@ -163,7 +109,6 @@ export const uiSlice = createSlice({ export const { setActiveTab, setCurrentTheme, - setParametersPanelScrollPosition, setShouldPinParametersPanel, setShouldShowParametersPanel, setShouldShowImageDetails, @@ -172,19 +117,13 @@ export const { setShouldUseSliders, setAddNewModelUIOption, setShouldHidePreview, - setShouldPinGallery, setShouldShowGallery, togglePanels, togglePinGalleryPanel, togglePinParametersPanel, toggleParametersPanel, toggleGalleryPanel, - openAccordionItemsChanged, - floatingProgressImageMoved, - floatingProgressImageResized, - setShouldShowProgressImages, setShouldShowProgressInViewer, - shouldShowImageParametersChanged, setSchedulers, } = uiSlice.actions; diff --git a/invokeai/frontend/web/src/features/ui/store/uiTypes.ts b/invokeai/frontend/web/src/features/ui/store/uiTypes.ts index db9c60e292..18a758cdd6 100644 --- a/invokeai/frontend/web/src/features/ui/store/uiTypes.ts +++ b/invokeai/frontend/web/src/features/ui/store/uiTypes.ts @@ -15,7 +15,6 @@ export type Rect = Coordinates & Dimensions; export interface UIState { activeTab: number; currentTheme: string; - parametersPanelScrollPosition: number; shouldPinParametersPanel: boolean; shouldShowParametersPanel: boolean; shouldShowImageDetails: boolean; @@ -26,12 +25,6 @@ export interface UIState { shouldHidePreview: boolean; shouldPinGallery: boolean; shouldShowGallery: boolean; - textTabAccordionState: number[]; - imageTabAccordionState: number[]; - canvasTabAccordionState: number[]; - floatingProgressImageRect: Rect; - shouldShowProgressImages: boolean; shouldShowProgressInViewer: boolean; - shouldShowImageParameters: boolean; schedulers: string[]; } diff --git a/invokeai/frontend/web/src/services/api/core/request.ts b/invokeai/frontend/web/src/services/api/core/request.ts index 745f687743..51c5f0f708 100644 --- a/invokeai/frontend/web/src/services/api/core/request.ts +++ b/invokeai/frontend/web/src/services/api/core/request.ts @@ -3,12 +3,14 @@ /* eslint-disable */ /** + * DO NOT DELETE EVEN THOUGH IT IS NOT USED! + * * Custom `request.ts` file for OpenAPI code generator. * * Patches the request logic in such a way that we can extract headers from requests. * * Copied from https://github.com/ferdikoomen/openapi-typescript-codegen/issues/829#issuecomment-1228224477 - * + * * This file should be excluded in `tsconfig.json` and ignored by prettier/eslint! */ diff --git a/invokeai/frontend/web/src/services/api/index.ts b/invokeai/frontend/web/src/services/api/index.ts index 0671808245..3b89d2a40c 100644 --- a/invokeai/frontend/web/src/services/api/index.ts +++ b/invokeai/frontend/web/src/services/api/index.ts @@ -65,6 +65,7 @@ export type { PaginatedResults_ImageResponse_ } from './models/PaginatedResults_ export type { ParamIntInvocation } from './models/ParamIntInvocation'; export type { PasteImageInvocation } from './models/PasteImageInvocation'; export type { PromptOutput } from './models/PromptOutput'; +export type { RandomIntInvocation } from './models/RandomIntInvocation'; export type { RandomRangeInvocation } from './models/RandomRangeInvocation'; export type { RangeInvocation } from './models/RangeInvocation'; export type { ResizeLatentsInvocation } from './models/ResizeLatentsInvocation'; @@ -137,6 +138,7 @@ export { $PaginatedResults_ImageResponse_ } from './schemas/$PaginatedResults_Im export { $ParamIntInvocation } from './schemas/$ParamIntInvocation'; export { $PasteImageInvocation } from './schemas/$PasteImageInvocation'; export { $PromptOutput } from './schemas/$PromptOutput'; +export { $RandomIntInvocation } from './schemas/$RandomIntInvocation'; export { $RandomRangeInvocation } from './schemas/$RandomRangeInvocation'; export { $RangeInvocation } from './schemas/$RangeInvocation'; export { $ResizeLatentsInvocation } from './schemas/$ResizeLatentsInvocation'; diff --git a/invokeai/frontend/web/src/services/api/models/Graph.ts b/invokeai/frontend/web/src/services/api/models/Graph.ts index 08d993e477..4e4f92e6f4 100644 --- a/invokeai/frontend/web/src/services/api/models/Graph.ts +++ b/invokeai/frontend/web/src/services/api/models/Graph.ts @@ -28,6 +28,7 @@ import type { MultiplyInvocation } from './MultiplyInvocation'; import type { NoiseInvocation } from './NoiseInvocation'; import type { ParamIntInvocation } from './ParamIntInvocation'; import type { PasteImageInvocation } from './PasteImageInvocation'; +import type { RandomIntInvocation } from './RandomIntInvocation'; import type { RandomRangeInvocation } from './RandomRangeInvocation'; import type { RangeInvocation } from './RangeInvocation'; import type { ResizeLatentsInvocation } from './ResizeLatentsInvocation'; @@ -47,7 +48,7 @@ export type Graph = { /** * The nodes in this graph */ - nodes?: Record; + nodes?: Record; /** * The connections between nodes and their fields in this graph */ diff --git a/invokeai/frontend/web/src/services/api/models/ImageToImageInvocation.ts b/invokeai/frontend/web/src/services/api/models/ImageToImageInvocation.ts index 09479388a7..fb43c76921 100644 --- a/invokeai/frontend/web/src/services/api/models/ImageToImageInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/ImageToImageInvocation.ts @@ -40,7 +40,7 @@ export type ImageToImageInvocation = { /** * The scheduler to use */ - scheduler?: 'ddim' | 'ddpm' | 'deis' | 'lms' | 'pndm' | 'heun' | 'euler' | 'euler_k' | 'euler_a' | 'kdpm_2' | 'kdpm_2_a' | 'dpmpp_2s' | 'dpmpp_2m' | 'dpmpp_2m_k' | 'unipc'; + scheduler?: 'ddim' | 'ddpm' | 'deis' | 'lms' | 'pndm' | 'heun' | 'heun_k' | 'euler' | 'euler_k' | 'euler_a' | 'kdpm_2' | 'kdpm_2_a' | 'dpmpp_2s' | 'dpmpp_2m' | 'dpmpp_2m_k' | 'unipc'; /** * The model to use (currently ignored) */ diff --git a/invokeai/frontend/web/src/services/api/models/InpaintInvocation.ts b/invokeai/frontend/web/src/services/api/models/InpaintInvocation.ts index 9ef7fcb36e..88ead9907c 100644 --- a/invokeai/frontend/web/src/services/api/models/InpaintInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/InpaintInvocation.ts @@ -41,7 +41,7 @@ export type InpaintInvocation = { /** * The scheduler to use */ - scheduler?: 'ddim' | 'ddpm' | 'deis' | 'lms' | 'pndm' | 'heun' | 'euler' | 'euler_k' | 'euler_a' | 'kdpm_2' | 'kdpm_2_a' | 'dpmpp_2s' | 'dpmpp_2m' | 'dpmpp_2m_k' | 'unipc'; + scheduler?: 'ddim' | 'ddpm' | 'deis' | 'lms' | 'pndm' | 'heun' | 'heun_k' | 'euler' | 'euler_k' | 'euler_a' | 'kdpm_2' | 'kdpm_2_a' | 'dpmpp_2s' | 'dpmpp_2m' | 'dpmpp_2m_k' | 'unipc'; /** * The model to use (currently ignored) */ diff --git a/invokeai/frontend/web/src/services/api/models/LatentsOutput.ts b/invokeai/frontend/web/src/services/api/models/LatentsOutput.ts index 0d417c3db2..3e9c2f60e4 100644 --- a/invokeai/frontend/web/src/services/api/models/LatentsOutput.ts +++ b/invokeai/frontend/web/src/services/api/models/LatentsOutput.ts @@ -8,10 +8,18 @@ import type { LatentsField } from './LatentsField'; * Base class for invocations that output latents */ export type LatentsOutput = { - type?: 'latent_output'; + type?: 'latents_output'; /** * The output latents */ latents?: LatentsField; + /** + * The width of the latents in pixels + */ + width: number; + /** + * The height of the latents in pixels + */ + height: number; }; diff --git a/invokeai/frontend/web/src/services/api/models/LatentsToLatentsInvocation.ts b/invokeai/frontend/web/src/services/api/models/LatentsToLatentsInvocation.ts index 425eeaf3d5..29995c6ad9 100644 --- a/invokeai/frontend/web/src/services/api/models/LatentsToLatentsInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/LatentsToLatentsInvocation.ts @@ -37,7 +37,7 @@ export type LatentsToLatentsInvocation = { /** * The scheduler to use */ - scheduler?: 'ddim' | 'ddpm' | 'deis' | 'lms' | 'pndm' | 'heun' | 'euler' | 'euler_k' | 'euler_a' | 'kdpm_2' | 'kdpm_2_a' | 'dpmpp_2s' | 'dpmpp_2m' | 'dpmpp_2m_k' | 'unipc'; + scheduler?: 'ddim' | 'ddpm' | 'deis' | 'lms' | 'pndm' | 'heun' | 'heun_k' | 'euler' | 'euler_k' | 'euler_a' | 'kdpm_2' | 'kdpm_2_a' | 'dpmpp_2s' | 'dpmpp_2m' | 'dpmpp_2m_k' | 'unipc'; /** * The model to use (currently ignored) */ diff --git a/invokeai/frontend/web/src/services/api/models/NoiseOutput.ts b/invokeai/frontend/web/src/services/api/models/NoiseOutput.ts index ff87cb7277..f1832d7aa2 100644 --- a/invokeai/frontend/web/src/services/api/models/NoiseOutput.ts +++ b/invokeai/frontend/web/src/services/api/models/NoiseOutput.ts @@ -13,5 +13,13 @@ export type NoiseOutput = { * The output noise */ noise?: LatentsField; + /** + * The width of the noise in pixels + */ + width: number; + /** + * The height of the noise in pixels + */ + height: number; }; diff --git a/invokeai/frontend/web/src/services/api/models/RandomIntInvocation.ts b/invokeai/frontend/web/src/services/api/models/RandomIntInvocation.ts new file mode 100644 index 0000000000..af7cf85666 --- /dev/null +++ b/invokeai/frontend/web/src/services/api/models/RandomIntInvocation.ts @@ -0,0 +1,15 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +/** + * Outputs a single random integer. + */ +export type RandomIntInvocation = { + /** + * The id of this node. Must be unique among all nodes. + */ + id: string; + type?: 'rand_int'; +}; + diff --git a/invokeai/frontend/web/src/services/api/models/TextToImageInvocation.ts b/invokeai/frontend/web/src/services/api/models/TextToImageInvocation.ts index 64e072bf4a..184e35693b 100644 --- a/invokeai/frontend/web/src/services/api/models/TextToImageInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/TextToImageInvocation.ts @@ -38,7 +38,7 @@ export type TextToImageInvocation = { /** * The scheduler to use */ - scheduler?: 'ddim' | 'ddpm' | 'deis' | 'lms' | 'pndm' | 'heun' | 'euler' | 'euler_k' | 'euler_a' | 'kdpm_2' | 'kdpm_2_a' | 'dpmpp_2s' | 'dpmpp_2m' | 'dpmpp_2m_k' | 'unipc'; + scheduler?: 'ddim' | 'ddpm' | 'deis' | 'lms' | 'pndm' | 'heun' | 'heun_k' | 'euler' | 'euler_k' | 'euler_a' | 'kdpm_2' | 'kdpm_2_a' | 'dpmpp_2s' | 'dpmpp_2m' | 'dpmpp_2m_k' | 'unipc'; /** * The model to use (currently ignored) */ diff --git a/invokeai/frontend/web/src/services/api/models/TextToLatentsInvocation.ts b/invokeai/frontend/web/src/services/api/models/TextToLatentsInvocation.ts index f939070763..d1ec5ed08c 100644 --- a/invokeai/frontend/web/src/services/api/models/TextToLatentsInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/TextToLatentsInvocation.ts @@ -37,7 +37,7 @@ export type TextToLatentsInvocation = { /** * The scheduler to use */ - scheduler?: 'ddim' | 'ddpm' | 'deis' | 'lms' | 'pndm' | 'heun' | 'euler' | 'euler_k' | 'euler_a' | 'kdpm_2' | 'kdpm_2_a' | 'dpmpp_2s' | 'dpmpp_2m' | 'dpmpp_2m_k' | 'unipc'; + scheduler?: 'ddim' | 'ddpm' | 'deis' | 'lms' | 'pndm' | 'heun' | 'heun_k' | 'euler' | 'euler_k' | 'euler_a' | 'kdpm_2' | 'kdpm_2_a' | 'dpmpp_2s' | 'dpmpp_2m' | 'dpmpp_2m_k' | 'unipc'; /** * The model to use (currently ignored) */ diff --git a/invokeai/frontend/web/src/services/api/schemas/$Graph.ts b/invokeai/frontend/web/src/services/api/schemas/$Graph.ts index b526def648..397d753a52 100644 --- a/invokeai/frontend/web/src/services/api/schemas/$Graph.ts +++ b/invokeai/frontend/web/src/services/api/schemas/$Graph.ts @@ -49,6 +49,8 @@ export const $Graph = { type: 'MultiplyInvocation', }, { type: 'DivideInvocation', + }, { + type: 'RandomIntInvocation', }, { type: 'ParamIntInvocation', }, { diff --git a/invokeai/frontend/web/src/services/api/schemas/$LatentsOutput.ts b/invokeai/frontend/web/src/services/api/schemas/$LatentsOutput.ts index 2b44f5a438..41a670c3aa 100644 --- a/invokeai/frontend/web/src/services/api/schemas/$LatentsOutput.ts +++ b/invokeai/frontend/web/src/services/api/schemas/$LatentsOutput.ts @@ -14,5 +14,15 @@ export const $LatentsOutput = { type: 'LatentsField', }], }, + width: { + type: 'number', + description: `The width of the latents in pixels`, + isRequired: true, + }, + height: { + type: 'number', + description: `The height of the latents in pixels`, + isRequired: true, + }, }, } as const; diff --git a/invokeai/frontend/web/src/services/api/schemas/$NoiseOutput.ts b/invokeai/frontend/web/src/services/api/schemas/$NoiseOutput.ts index b0c3cc1d02..8112240add 100644 --- a/invokeai/frontend/web/src/services/api/schemas/$NoiseOutput.ts +++ b/invokeai/frontend/web/src/services/api/schemas/$NoiseOutput.ts @@ -14,5 +14,15 @@ export const $NoiseOutput = { type: 'LatentsField', }], }, + width: { + type: 'number', + description: `The width of the noise in pixels`, + isRequired: true, + }, + height: { + type: 'number', + description: `The height of the noise in pixels`, + isRequired: true, + }, }, } as const; diff --git a/invokeai/frontend/web/src/services/api/schemas/$RandomIntInvocation.ts b/invokeai/frontend/web/src/services/api/schemas/$RandomIntInvocation.ts new file mode 100644 index 0000000000..e5b0387d5a --- /dev/null +++ b/invokeai/frontend/web/src/services/api/schemas/$RandomIntInvocation.ts @@ -0,0 +1,16 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $RandomIntInvocation = { + description: `Outputs a single random integer.`, + properties: { + id: { + type: 'string', + description: `The id of this node. Must be unique among all nodes.`, + isRequired: true, + }, + type: { + type: 'Enum', + }, + }, +} as const; diff --git a/invokeai/frontend/web/src/services/api/services/SessionsService.ts b/invokeai/frontend/web/src/services/api/services/SessionsService.ts index c16e7ecd3d..c8c53e0bd7 100644 --- a/invokeai/frontend/web/src/services/api/services/SessionsService.ts +++ b/invokeai/frontend/web/src/services/api/services/SessionsService.ts @@ -30,6 +30,7 @@ import type { NoiseInvocation } from '../models/NoiseInvocation'; import type { PaginatedResults_GraphExecutionState_ } from '../models/PaginatedResults_GraphExecutionState_'; import type { ParamIntInvocation } from '../models/ParamIntInvocation'; import type { PasteImageInvocation } from '../models/PasteImageInvocation'; +import type { RandomIntInvocation } from '../models/RandomIntInvocation'; import type { RandomRangeInvocation } from '../models/RandomRangeInvocation'; import type { RangeInvocation } from '../models/RangeInvocation'; import type { ResizeLatentsInvocation } from '../models/ResizeLatentsInvocation'; @@ -149,7 +150,7 @@ export class SessionsService { * The id of the session */ sessionId: string, - requestBody: (LoadImageInvocation | ShowImageInvocation | CropImageInvocation | PasteImageInvocation | MaskFromAlphaInvocation | BlurInvocation | LerpInvocation | InverseLerpInvocation | CompelInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | ImageToLatentsInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | ParamIntInvocation | CvInpaintInvocation | RangeInvocation | RandomRangeInvocation | UpscaleInvocation | RestoreFaceInvocation | TextToImageInvocation | InfillColorInvocation | InfillTileInvocation | InfillPatchMatchInvocation | GraphInvocation | IterateInvocation | CollectInvocation | LatentsToLatentsInvocation | ImageToImageInvocation | InpaintInvocation), + requestBody: (LoadImageInvocation | ShowImageInvocation | CropImageInvocation | PasteImageInvocation | MaskFromAlphaInvocation | BlurInvocation | LerpInvocation | InverseLerpInvocation | CompelInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | ImageToLatentsInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | RandomIntInvocation | ParamIntInvocation | CvInpaintInvocation | RangeInvocation | RandomRangeInvocation | UpscaleInvocation | RestoreFaceInvocation | TextToImageInvocation | InfillColorInvocation | InfillTileInvocation | InfillPatchMatchInvocation | GraphInvocation | IterateInvocation | CollectInvocation | LatentsToLatentsInvocation | ImageToImageInvocation | InpaintInvocation), }): CancelablePromise { return __request(OpenAPI, { method: 'POST', @@ -186,7 +187,7 @@ export class SessionsService { * The path to the node in the graph */ nodePath: string, - requestBody: (LoadImageInvocation | ShowImageInvocation | CropImageInvocation | PasteImageInvocation | MaskFromAlphaInvocation | BlurInvocation | LerpInvocation | InverseLerpInvocation | CompelInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | ImageToLatentsInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | ParamIntInvocation | CvInpaintInvocation | RangeInvocation | RandomRangeInvocation | UpscaleInvocation | RestoreFaceInvocation | TextToImageInvocation | InfillColorInvocation | InfillTileInvocation | InfillPatchMatchInvocation | GraphInvocation | IterateInvocation | CollectInvocation | LatentsToLatentsInvocation | ImageToImageInvocation | InpaintInvocation), + requestBody: (LoadImageInvocation | ShowImageInvocation | CropImageInvocation | PasteImageInvocation | MaskFromAlphaInvocation | BlurInvocation | LerpInvocation | InverseLerpInvocation | CompelInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | ImageToLatentsInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | RandomIntInvocation | ParamIntInvocation | CvInpaintInvocation | RangeInvocation | RandomRangeInvocation | UpscaleInvocation | RestoreFaceInvocation | TextToImageInvocation | InfillColorInvocation | InfillTileInvocation | InfillPatchMatchInvocation | GraphInvocation | IterateInvocation | CollectInvocation | LatentsToLatentsInvocation | ImageToImageInvocation | InpaintInvocation), }): CancelablePromise { return __request(OpenAPI, { method: 'PUT',