From 4fe93e521ea12a85d26573294f3e20f9a7d9c399 Mon Sep 17 00:00:00 2001 From: Paul Curry Date: Fri, 24 Nov 2023 19:58:11 -0800 Subject: [PATCH] feat(ui): add recall Height/Width button to img2img initial image and current image displays in linear flow (#5161) * working on recall height/width * working on adding resize * working on feature * fix(ui): move added translation from dist/ to public/ * fix(ui): use `metadata` as hotkey cb dependency Using `imageDTO` may result in stale data being used --------- Co-authored-by: psychedelicious <4822129+psychedelicious@users.noreply.github.com> --- invokeai/frontend/web/public/locales/en.json | 1 + .../CurrentImage/CurrentImageButtons.tsx | 30 +++++++++++++++++-- .../ImageToImage/InitialImageDisplay.tsx | 28 +++++++++++++---- .../parameters/hooks/useRecallParameters.ts | 21 +++++++++++++ 4 files changed, 72 insertions(+), 8 deletions(-) diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index b210ff6b4f..9951b21cd8 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -1111,6 +1111,7 @@ "upscaling": "Upscaling", "unmasked": "Unmasked", "useAll": "Use All", + "useSize": "Use Size", "useCpuNoise": "Use CPU Noise", "cpuNoise": "CPU Noise", "gpuNoise": "GPU Noise", diff --git a/invokeai/frontend/web/src/features/gallery/components/CurrentImage/CurrentImageButtons.tsx b/invokeai/frontend/web/src/features/gallery/components/CurrentImage/CurrentImageButtons.tsx index 10f1be9153..c371e2aee5 100644 --- a/invokeai/frontend/web/src/features/gallery/components/CurrentImage/CurrentImageButtons.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/CurrentImage/CurrentImageButtons.tsx @@ -35,6 +35,7 @@ import { FaCode, FaHourglassHalf, FaQuoteRight, + FaRulerVertical, FaSeedling, } from 'react-icons/fa'; import { FaCircleNodes, FaEllipsis } from 'react-icons/fa6'; @@ -95,8 +96,12 @@ const CurrentImageButtons = () => { const toaster = useAppToaster(); const { t } = useTranslation(); - const { recallBothPrompts, recallSeed, recallAllParameters } = - useRecallParameters(); + const { + recallBothPrompts, + recallSeed, + recallWidthAndHeight, + recallAllParameters, + } = useRecallParameters(); const { currentData: imageDTO } = useGetImageDTOQuery( lastSelectedImage?.image_name ?? skipToken @@ -117,6 +122,8 @@ const CurrentImageButtons = () => { dispatch(workflowLoadRequested(workflow)); }, [dispatch, workflow]); + useHotkeys('w', handleLoadWorkflow, [workflow]); + const handleClickUseAllParameters = useCallback(() => { recallAllParameters(metadata); }, [metadata, recallAllParameters]); @@ -146,7 +153,11 @@ const CurrentImageButtons = () => { useHotkeys('p', handleUsePrompt, [metadata]); - useHotkeys('w', handleLoadWorkflow, [workflow]); + const handleUseSize = useCallback(() => { + recallWidthAndHeight(metadata?.width, metadata?.height); + }, [metadata?.width, metadata?.height, recallWidthAndHeight]); + + useHotkeys('d', handleUseSize, [metadata]); const handleSendToImageToImage = useCallback(() => { dispatch(sentImageToImg2Img()); @@ -267,6 +278,19 @@ const CurrentImageButtons = () => { isDisabled={metadata?.seed === null || metadata?.seed === undefined} onClick={handleUseSeed} /> + } + tooltip={`${t('parameters.useSize')} (D)`} + aria-label={`${t('parameters.useSize')} (D)`} + isDisabled={ + metadata?.height === null || + metadata?.height === undefined || + metadata?.width === null || + metadata?.width === undefined + } + onClick={handleUseSize} + /> } diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/InitialImageDisplay.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/InitialImageDisplay.tsx index 56054e3505..17eeccf306 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/InitialImageDisplay.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/InitialImageDisplay.tsx @@ -5,12 +5,14 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import IAIIconButton from 'common/components/IAIIconButton'; import { useImageUploadButton } from 'common/hooks/useImageUploadButton'; +import { useRecallParameters } from 'features/parameters/hooks/useRecallParameters'; import { clearInitialImage } from 'features/parameters/store/generationSlice'; import { memo, useCallback } from 'react'; -import { FaUndo, FaUpload } from 'react-icons/fa'; -import InitialImage from './InitialImage'; -import { PostUploadAction } from 'services/api/types'; +import { useHotkeys } from 'react-hotkeys-hook'; import { useTranslation } from 'react-i18next'; +import { FaRulerVertical, FaUndo, FaUpload } from 'react-icons/fa'; +import { PostUploadAction } from 'services/api/types'; +import InitialImage from './InitialImage'; const selector = createSelector( [stateSelector], @@ -18,6 +20,7 @@ const selector = createSelector( const { initialImage } = state.generation; return { isResetButtonDisabled: !initialImage, + initialImage, }; }, defaultSelectorOptions @@ -28,7 +31,9 @@ const postUploadAction: PostUploadAction = { }; const InitialImageDisplay = () => { - const { isResetButtonDisabled } = useAppSelector(selector); + const { recallWidthAndHeight } = useRecallParameters(); + const { t } = useTranslation(); + const { isResetButtonDisabled, initialImage } = useAppSelector(selector); const dispatch = useAppDispatch(); const { getUploadButtonProps, getUploadInputProps } = useImageUploadButton({ @@ -39,7 +44,13 @@ const InitialImageDisplay = () => { dispatch(clearInitialImage()); }, [dispatch]); - const { t } = useTranslation(); + const handleUseSizeInitialImage = useCallback(() => { + if (initialImage) { + recallWidthAndHeight(initialImage.width, initialImage.height); + } + }, [initialImage, recallWidthAndHeight]); + + useHotkeys('shift+d', handleUseSizeInitialImage, [initialImage]); return ( { icon={} {...getUploadButtonProps()} /> + } + onClick={handleUseSizeInitialImage} + isDisabled={isResetButtonDisabled} + /> { [dispatch, parameterSetToast, parameterNotSetToast] ); + /** + * Recall width and height with toast + */ + const recallWidthAndHeight = useCallback( + (width: unknown, height: unknown) => { + if (!isValidWidth(width)) { + allParameterNotSetToast(); + return; + } + if (!isValidHeight(height)) { + allParameterNotSetToast(); + return; + } + dispatch(setHeight(height)); + dispatch(setWidth(width)); + allParameterSetToast(); + }, + [dispatch, allParameterSetToast, allParameterNotSetToast] + ); + /** * Recall strength with toast */ @@ -966,6 +986,7 @@ export const useRecallParameters = () => { recallSteps, recallWidth, recallHeight, + recallWidthAndHeight, recallStrength, recallHrfEnabled, recallHrfStrength,