diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index c476411d3f..6fd46aafcf 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -384,7 +384,9 @@ "selectAnImageToCompare": "Select an Image to Compare", "slider": "Slider", "sideBySide": "Side-by-Side", - "swapImages": "Swap" + "swapImages": "Swap Images", + "compareOptions": "Comparison Options", + "sliderFitLabel": "Stretch second image to fit" }, "hotkeys": { "searchHotkeys": "Search Hotkeys", diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonSlider.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonSlider.tsx index c7a411c07c..43c97a69b5 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonSlider.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonSlider.tsx @@ -1,5 +1,6 @@ import { Box, Flex, Icon, Image, Text } from '@invoke-ai/ui-library'; import { useMeasure } from '@reactuses/core'; +import { useAppSelector } from 'app/store/storeHooks'; import type { Dimensions } from 'features/canvas/store/canvasTypes'; import { STAGE_BG_DATAURL } from 'features/controlLayers/util/renderers'; import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; @@ -29,6 +30,7 @@ type Props = { export const ImageComparisonSlider = memo(({ firstImage, secondImage }: Props) => { const { t } = useTranslation(); + const sliderFit = useAppSelector((s) => s.gallery.sliderFit); // How far the handle is from the left - this will be a CSS calculation that takes into account the handle width const [left, setLeft] = useState(HANDLE_LEFT_INITIAL_PX); // How wide the first image is @@ -165,11 +167,11 @@ export const ImageComparisonSlider = memo(({ firstImage, secondImage }: Props) = { const { t } = useTranslation(); const dispatch = useAppDispatch(); const comparisonMode = useAppSelector((s) => s.gallery.comparisonMode); + const sliderFit = useAppSelector((s) => s.gallery.sliderFit); const setComparisonModeSlider = useCallback(() => { dispatch(comparisonModeChanged('slider')); }, [dispatch]); @@ -17,6 +32,12 @@ export const ImageComparisonToolbarButtons = memo(() => { const swapImages = useCallback(() => { dispatch(comparedImagesSwapped()); }, [dispatch]); + const onSliderFitChanged = useCallback( + (e: ChangeEvent) => { + dispatch(sliderFitChanged(e.target.checked ? 'fill' : 'contain')); + }, + [dispatch] + ); return ( <> @@ -31,6 +52,30 @@ export const ImageComparisonToolbarButtons = memo(() => { {t('gallery.sideBySide')} + + + } + /> + + + + + + {t('gallery.sliderFitLabel')} + + + + + + + ); diff --git a/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts b/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts index d8618ea200..4a49acafc5 100644 --- a/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts +++ b/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts @@ -24,6 +24,7 @@ const initialGalleryState: GalleryState = { viewerMode: 'view', imageToCompare: null, comparisonMode: 'slider', + sliderFit: 'fill', }; export const gallerySlice = createSlice({ @@ -97,6 +98,9 @@ export const gallerySlice = createSlice({ state.imageToCompare = oldSelection[0] ?? null; } }, + sliderFitChanged: (state, action: PayloadAction<'contain' | 'fill'>) => { + state.sliderFit = action.payload; + }, }, extraReducers: (builder) => { builder.addMatcher(isAnyBoardDeleted, (state, action) => { @@ -138,6 +142,7 @@ export const { imageToCompareChanged, comparisonModeChanged, comparedImagesSwapped, + sliderFitChanged, } = gallerySlice.actions; const isAnyBoardDeleted = isAnyOf( diff --git a/invokeai/frontend/web/src/features/gallery/store/types.ts b/invokeai/frontend/web/src/features/gallery/store/types.ts index f406c37303..1388c792c3 100644 --- a/invokeai/frontend/web/src/features/gallery/store/types.ts +++ b/invokeai/frontend/web/src/features/gallery/store/types.ts @@ -24,5 +24,6 @@ export type GalleryState = { alwaysShowImageSizeBadge: boolean; imageToCompare: ImageDTO | null; comparisonMode: ComparisonMode; + sliderFit: 'contain' | 'fill'; viewerMode: ViewerMode; };