From ee6df5852ada9122dc809ac1edd5b74cb78c435b Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Sun, 12 Mar 2023 19:16:11 +1100 Subject: [PATCH] fix(ui): fix lightbox --- invokeai/frontend/web/src/app/App.tsx | 2 +- invokeai/frontend/web/src/app/store.ts | 11 +- .../components/CurrentImagePreview.tsx | 23 +- .../gallery/components/ImageGalleryPanel.tsx | 16 +- .../ImageMetadataViewer.tsx | 540 +++++++++--------- .../features/lightbox/components/Lightbox.tsx | 173 +++--- .../lightbox/components/ReactPanZoomImage.tsx | 5 +- .../src/features/ui/components/InvokeTabs.tsx | 2 +- .../ui/components/ParametersPanel.tsx | 20 +- 9 files changed, 409 insertions(+), 383 deletions(-) diff --git a/invokeai/frontend/web/src/app/App.tsx b/invokeai/frontend/web/src/app/App.tsx index c8faa2724e..4be90f0892 100644 --- a/invokeai/frontend/web/src/app/App.tsx +++ b/invokeai/frontend/web/src/app/App.tsx @@ -30,6 +30,7 @@ const App = () => { return ( + { - ); }; diff --git a/invokeai/frontend/web/src/app/store.ts b/invokeai/frontend/web/src/app/store.ts index ac6400a8c4..29dbff3fba 100644 --- a/invokeai/frontend/web/src/app/store.ts +++ b/invokeai/frontend/web/src/app/store.ts @@ -60,6 +60,10 @@ const galleryBlacklist = [ 'intermediateImage', ].map((blacklistItem) => `gallery.${blacklistItem}`); +const lightboxBlacklist = ['isLightboxOpen'].map( + (blacklistItem) => `lightbox.${blacklistItem}` +); + const rootReducer = combineReducers({ generation: generationReducer, postprocessing: postprocessingReducer, @@ -74,7 +78,12 @@ const rootPersistConfig = getPersistConfig({ key: 'root', storage, rootReducer, - blacklist: [...canvasBlacklist, ...systemBlacklist, ...galleryBlacklist], + blacklist: [ + ...canvasBlacklist, + ...systemBlacklist, + ...galleryBlacklist, + ...lightboxBlacklist, + ], debounce: 300, }); diff --git a/invokeai/frontend/web/src/features/gallery/components/CurrentImagePreview.tsx b/invokeai/frontend/web/src/features/gallery/components/CurrentImagePreview.tsx index 74468febd1..84f92da53b 100644 --- a/invokeai/frontend/web/src/features/gallery/components/CurrentImagePreview.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/CurrentImagePreview.tsx @@ -1,9 +1,10 @@ -import { Flex, Image } from '@chakra-ui/react'; +import { Box, Flex, Image } from '@chakra-ui/react'; import { createSelector } from '@reduxjs/toolkit'; import { useAppSelector } from 'app/storeHooks'; import { GalleryState } from 'features/gallery/store/gallerySlice'; import { uiSelector } from 'features/ui/store/uiSelectors'; import { isEqual } from 'lodash'; +import { APP_METADATA_HEIGHT } from 'theme/util/constants'; import { gallerySelector } from '../store/gallerySelectors'; import ImageMetadataViewer from './ImageMetaDataViewer/ImageMetadataViewer'; @@ -60,10 +61,22 @@ export default function CurrentImagePreview() { )} {!shouldShowImageDetails && } {shouldShowImageDetails && imageToDisplay && ( - + + + )} ); diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryPanel.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryPanel.tsx index 6c574f2be3..da97dea9bf 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryPanel.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryPanel.tsx @@ -25,6 +25,7 @@ import { } from 'features/ui/store/uiSelectors'; import { isStagingSelector } from 'features/canvas/store/canvasSelectors'; import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale'; +import { lightboxSelector } from 'features/lightbox/store/lightboxSelectors'; const GALLERY_TAB_WIDTHS: Record< InvokeTabName, @@ -39,10 +40,17 @@ const GALLERY_TAB_WIDTHS: Record< }; const galleryPanelSelector = createSelector( - [activeTabNameSelector, uiSelector, gallerySelector, isStagingSelector], - (activeTabName, ui, gallery, isStaging) => { + [ + activeTabNameSelector, + uiSelector, + gallerySelector, + isStagingSelector, + lightboxSelector, + ], + (activeTabName, ui, gallery, isStaging, lightbox) => { const { shouldPinGallery, shouldShowGallery } = ui; const { galleryImageMinimumWidth } = gallery; + const { isLightboxOpen } = lightbox; return { activeTabName, @@ -51,6 +59,7 @@ const galleryPanelSelector = createSelector( shouldShowGallery, galleryImageMinimumWidth, isResizable: activeTabName !== 'unifiedCanvas', + isLightboxOpen, }; }, { @@ -69,6 +78,7 @@ export default function ImageGalleryPanel() { activeTabName, isStaging, isResizable, + isLightboxOpen, } = useAppSelector(galleryPanelSelector); const handleSetShouldPinGallery = () => { @@ -174,7 +184,7 @@ export default function ImageGalleryPanel() { isResizable={isResizable || !shouldPinGallery} isOpen={shouldShowGallery} onClose={handleCloseGallery} - isPinned={shouldPinGallery} + isPinned={shouldPinGallery && !isLightboxOpen} minWidth={ shouldPinGallery ? GALLERY_TAB_WIDTHS[activeTabName].galleryMinWidth 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 3e747a1235..4d8e870f34 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageMetaDataViewer/ImageMetadataViewer.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageMetaDataViewer/ImageMetadataViewer.tsx @@ -170,304 +170,290 @@ const ImageMetadataViewer = memo( const metadataJSON = JSON.stringify(image.metadata, null, 2); return ( - - - - File: - - {image.url.length > 64 - ? image.url.substring(0, 64).concat('...') - : image.url} - - - - {Object.keys(metadata).length > 0 ? ( - <> - {type && } - {image.metadata?.model_weights && ( - - )} - {['esrgan', 'gfpgan'].includes(type) && ( - - )} - {prompt && ( - setBothPrompts(prompt)} - /> - )} - {seed !== undefined && ( - dispatch(setSeed(seed))} - /> - )} - {threshold !== undefined && ( - dispatch(setThreshold(threshold))} - /> - )} - {perlin !== undefined && ( - dispatch(setPerlin(perlin))} - /> - )} - {sampler && ( - dispatch(setSampler(sampler))} - /> - )} - {steps && ( - dispatch(setSteps(steps))} - /> - )} - {cfg_scale !== undefined && ( - dispatch(setCfgScale(cfg_scale))} - /> - )} - {variations && variations.length > 0 && ( - - dispatch(setSeedWeights(seedWeightsToString(variations))) - } - /> - )} - {seamless && ( - dispatch(setSeamless(seamless))} - /> - )} - {hires_fix && ( - dispatch(setHiresFix(hires_fix))} - /> - )} - {width && ( - dispatch(setWidth(width))} - /> - )} - {height && ( - dispatch(setHeight(height))} - /> - )} - {init_image_path && ( - dispatch(setInitialImage(init_image_path))} - /> - )} - {mask_image_path && ( - dispatch(setMaskPath(mask_image_path))} - /> - )} - {type === 'img2img' && strength && ( - dispatch(setImg2imgStrength(strength))} - /> - )} - {fit && ( - dispatch(setShouldFitToWidthHeight(fit))} - /> - )} - {postprocessing && postprocessing.length > 0 && ( - <> - Postprocessing - {postprocessing.map( - ( - postprocess: InvokeAI.PostProcessedImageMetadata, - i: number - ) => { - if (postprocess.type === 'esrgan') { - const { scale, strength, denoise_str } = postprocess; - return ( - - {`${ - i + 1 - }: Upscale (ESRGAN)`} + + File: + + {image.url.length > 64 + ? image.url.substring(0, 64).concat('...') + : image.url} + + + + {Object.keys(metadata).length > 0 ? ( + <> + {type && } + {image.metadata?.model_weights && ( + + )} + {['esrgan', 'gfpgan'].includes(type) && ( + + )} + {prompt && ( + setBothPrompts(prompt)} + /> + )} + {seed !== undefined && ( + dispatch(setSeed(seed))} + /> + )} + {threshold !== undefined && ( + dispatch(setThreshold(threshold))} + /> + )} + {perlin !== undefined && ( + dispatch(setPerlin(perlin))} + /> + )} + {sampler && ( + dispatch(setSampler(sampler))} + /> + )} + {steps && ( + dispatch(setSteps(steps))} + /> + )} + {cfg_scale !== undefined && ( + dispatch(setCfgScale(cfg_scale))} + /> + )} + {variations && variations.length > 0 && ( + + dispatch(setSeedWeights(seedWeightsToString(variations))) + } + /> + )} + {seamless && ( + dispatch(setSeamless(seamless))} + /> + )} + {hires_fix && ( + dispatch(setHiresFix(hires_fix))} + /> + )} + {width && ( + dispatch(setWidth(width))} + /> + )} + {height && ( + dispatch(setHeight(height))} + /> + )} + {init_image_path && ( + dispatch(setInitialImage(init_image_path))} + /> + )} + {mask_image_path && ( + dispatch(setMaskPath(mask_image_path))} + /> + )} + {type === 'img2img' && strength && ( + dispatch(setImg2imgStrength(strength))} + /> + )} + {fit && ( + dispatch(setShouldFitToWidthHeight(fit))} + /> + )} + {postprocessing && postprocessing.length > 0 && ( + <> + Postprocessing + {postprocessing.map( + ( + postprocess: InvokeAI.PostProcessedImageMetadata, + i: number + ) => { + if (postprocess.type === 'esrgan') { + const { scale, strength, denoise_str } = postprocess; + return ( + + {`${i + 1}: Upscale (ESRGAN)`} + dispatch(setUpscalingLevel(scale))} + /> + + dispatch(setUpscalingStrength(strength)) + } + /> + {denoise_str !== undefined && ( dispatch(setUpscalingLevel(scale))} - /> - - dispatch(setUpscalingStrength(strength)) + dispatch(setUpscalingDenoising(denoise_str)) } /> - {denoise_str !== undefined && ( - - dispatch(setUpscalingDenoising(denoise_str)) - } - /> - )} - - ); - } else if (postprocess.type === 'gfpgan') { - const { strength } = postprocess; - return ( - - {`${ - i + 1 - }: Face restoration (GFPGAN)`} + )} + + ); + } else if (postprocess.type === 'gfpgan') { + const { strength } = postprocess; + return ( + + {`${ + i + 1 + }: Face restoration (GFPGAN)`} - { - dispatch(setFacetoolStrength(strength)); - dispatch(setFacetoolType('gfpgan')); - }} - /> - - ); - } else if (postprocess.type === 'codeformer') { - const { strength, fidelity } = postprocess; - return ( - - {`${ - i + 1 - }: Face restoration (Codeformer)`} + { + dispatch(setFacetoolStrength(strength)); + dispatch(setFacetoolType('gfpgan')); + }} + /> + + ); + } else if (postprocess.type === 'codeformer') { + const { strength, fidelity } = postprocess; + return ( + + {`${ + i + 1 + }: Face restoration (Codeformer)`} + { + dispatch(setFacetoolStrength(strength)); + dispatch(setFacetoolType('codeformer')); + }} + /> + {fidelity && ( { - dispatch(setFacetoolStrength(strength)); + dispatch(setCodeformerFidelity(fidelity)); dispatch(setFacetoolType('codeformer')); }} /> - {fidelity && ( - { - dispatch(setCodeformerFidelity(fidelity)); - dispatch(setFacetoolType('codeformer')); - }} - /> - )} - - ); - } + )} + + ); } - )} - - )} - {dreamPrompt && ( - - )} - - - - } - size="xs" - variant="ghost" - fontSize={14} - onClick={() => - navigator.clipboard.writeText(metadataJSON) - } - /> - - Metadata JSON: - - -
{metadataJSON}
-
+ } + )} + + )} + {dreamPrompt && ( + + )} + + + + } + size="xs" + variant="ghost" + fontSize={14} + onClick={() => navigator.clipboard.writeText(metadataJSON)} + /> + + Metadata JSON: - - ) : ( -
- - No metadata available - -
- )} -
-
+ +
{metadataJSON}
+
+ + + ) : ( +
+ + No metadata available + +
+ )} + ); }, memoEqualityCheck diff --git a/invokeai/frontend/web/src/features/lightbox/components/Lightbox.tsx b/invokeai/frontend/web/src/features/lightbox/components/Lightbox.tsx index 473906185e..30ccbf318e 100644 --- a/invokeai/frontend/web/src/features/lightbox/components/Lightbox.tsx +++ b/invokeai/frontend/web/src/features/lightbox/components/Lightbox.tsx @@ -1,20 +1,21 @@ -import { Box, Flex, Grid } from '@chakra-ui/react'; +import { Box, chakra, Flex } from '@chakra-ui/react'; import { createSelector } from '@reduxjs/toolkit'; import { RootState } from 'app/store'; import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import IAIIconButton from 'common/components/IAIIconButton'; import CurrentImageButtons from 'features/gallery/components/CurrentImageButtons'; -import ImageGalleryPanel from 'features/gallery/components/ImageGalleryPanel'; import ImageMetadataViewer from 'features/gallery/components/ImageMetaDataViewer/ImageMetadataViewer'; import NextPrevImageButtons from 'features/gallery/components/NextPrevImageButtons'; import { gallerySelector } from 'features/gallery/store/gallerySelectors'; import { setIsLightboxOpen } from 'features/lightbox/store/lightboxSlice'; import { uiSelector } from 'features/ui/store/uiSelectors'; +import { AnimatePresence, motion } from 'framer-motion'; import { isEqual } from 'lodash'; import { useHotkeys } from 'react-hotkeys-hook'; import { useTranslation } from 'react-i18next'; import { BiExit } from 'react-icons/bi'; import { TransformWrapper } from 'react-zoom-pan-pinch'; +import { PROGRESS_BAR_THICKNESS } from 'theme/util/constants'; import useImageTransform from '../hooks/useImageTransform'; import ReactPanZoomButtons from './ReactPanZoomButtons'; import ReactPanZoomImage from './ReactPanZoomImage'; @@ -37,6 +38,8 @@ export const lightboxSelector = createSelector( } ); +const ChakraMotionBox = chakra(motion.div); + export default function Lightbox() { const dispatch = useAppDispatch(); const { t } = useTranslation(); @@ -67,106 +70,102 @@ export default function Lightbox() { ); return ( - - - + {isLightBoxOpen && ( + - } - aria-label={t('accessibility.exitViewer')} - onClick={() => { - dispatch(setIsLightboxOpen(false)); - }} - fontSize={20} - /> - - - - - + + } + aria-label="Exit Viewer" + className="lightbox-close-btn" + onClick={() => { + dispatch(setIsLightboxOpen(false)); + }} + fontSize={20} + /> + + + + + + {viewerImageToDisplay && ( <> {shouldShowImageDetails && ( )} + + {!shouldShowImageDetails && ( + + + + )} )} - - {!shouldShowImageDetails && ( - - - - )} - - - - - - - - - + + + )} + ); } diff --git a/invokeai/frontend/web/src/features/lightbox/components/ReactPanZoomImage.tsx b/invokeai/frontend/web/src/features/lightbox/components/ReactPanZoomImage.tsx index db6a02ae71..0658fd1756 100644 --- a/invokeai/frontend/web/src/features/lightbox/components/ReactPanZoomImage.tsx +++ b/invokeai/frontend/web/src/features/lightbox/components/ReactPanZoomImage.tsx @@ -1,8 +1,9 @@ import * as React from 'react'; import { TransformComponent, useTransformContext } from 'react-zoom-pan-pinch'; +import * as InvokeAI from 'app/invokeai'; type ReactPanZoomProps = { - image: string; + image: InvokeAI.Image; styleClass?: string; alt?: string; ref?: React.Ref; @@ -34,7 +35,7 @@ export default function ReactPanZoomImage({ transform: `rotate(${rotation}deg) scaleX(${scaleX}) scaleY(${scaleY})`, width: '100%', }} - src={image} + src={image.url} alt={alt} ref={ref} className={styleClass ? styleClass : ''} diff --git a/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx b/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx index 74a64ba405..4c011c124e 100644 --- a/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx +++ b/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx @@ -189,7 +189,7 @@ export default function InvokeTabs() { flexGrow={1} > {tabs} - {isLightBoxOpen ? : tabPanels} + {tabPanels} ); } diff --git a/invokeai/frontend/web/src/features/ui/components/ParametersPanel.tsx b/invokeai/frontend/web/src/features/ui/components/ParametersPanel.tsx index 8efccc9472..1f0bcaead3 100644 --- a/invokeai/frontend/web/src/features/ui/components/ParametersPanel.tsx +++ b/invokeai/frontend/web/src/features/ui/components/ParametersPanel.tsx @@ -18,16 +18,19 @@ import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvas import { createSelector } from '@reduxjs/toolkit'; import { activeTabNameSelector, uiSelector } from '../store/uiSelectors'; import { isEqual } from 'lodash'; +import { lightboxSelector } from 'features/lightbox/store/lightboxSelectors'; const parametersPanelSelector = createSelector( - [uiSelector, activeTabNameSelector], - (ui, activeTabName) => { + [uiSelector, activeTabNameSelector, lightboxSelector], + (ui, activeTabName, lightbox) => { const { shouldPinParametersPanel, shouldShowParametersPanel } = ui; + const { isLightboxOpen } = lightbox; return { shouldPinParametersPanel, shouldShowParametersPanel, isResizable: activeTabName !== 'unifiedCanvas', + isLightboxOpen, }; }, { @@ -44,8 +47,12 @@ type ParametersPanelProps = { const ParametersPanel = ({ children }: ParametersPanelProps) => { const dispatch = useAppDispatch(); - const { shouldPinParametersPanel, shouldShowParametersPanel, isResizable } = - useAppSelector(parametersPanelSelector); + const { + shouldPinParametersPanel, + shouldShowParametersPanel, + isResizable, + isLightboxOpen, + } = useAppSelector(parametersPanelSelector); const closeParametersPanel = () => { dispatch(setShouldShowParametersPanel(false)); @@ -57,7 +64,8 @@ const ParametersPanel = ({ children }: ParametersPanelProps) => { dispatch(toggleParametersPanel()); shouldPinParametersPanel && dispatch(requestCanvasRescale()); }, - [shouldPinParametersPanel] + { enabled: () => !isLightboxOpen }, + [shouldPinParametersPanel, isLightboxOpen] ); useHotkeys( @@ -86,7 +94,7 @@ const ParametersPanel = ({ children }: ParametersPanelProps) => { isResizable={isResizable || !shouldPinParametersPanel} isOpen={shouldShowParametersPanel} onClose={closeParametersPanel} - isPinned={shouldPinParametersPanel} + isPinned={shouldPinParametersPanel || isLightboxOpen} sx={{ borderColor: 'base.700', p: shouldPinParametersPanel ? 0 : 4,