From ca8313e8059d8e26790efc99029040f86b0443d8 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 28 Nov 2024 13:55:19 +1100 Subject: [PATCH] feat(ui): add new layer from image menu items for staging area The layers are disabled when created so as to not interfere with the canvas state. --- .../StagingArea/StagingAreaToolbar.tsx | 2 + .../StagingAreaToolbarSaveAsMenu.tsx | 136 ++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarSaveAsMenu.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbar.tsx b/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbar.tsx index 2c044278198..14046163804 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbar.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbar.tsx @@ -5,6 +5,7 @@ import { StagingAreaToolbarDiscardSelectedButton } from 'features/controlLayers/ import { StagingAreaToolbarImageCountButton } from 'features/controlLayers/components/StagingArea/StagingAreaToolbarImageCountButton'; import { StagingAreaToolbarNextButton } from 'features/controlLayers/components/StagingArea/StagingAreaToolbarNextButton'; import { StagingAreaToolbarPrevButton } from 'features/controlLayers/components/StagingArea/StagingAreaToolbarPrevButton'; +import { StagingAreaToolbarSaveAsMenu } from 'features/controlLayers/components/StagingArea/StagingAreaToolbarSaveAsMenu'; import { StagingAreaToolbarSaveSelectedToGalleryButton } from 'features/controlLayers/components/StagingArea/StagingAreaToolbarSaveSelectedToGalleryButton'; import { StagingAreaToolbarToggleShowResultsButton } from 'features/controlLayers/components/StagingArea/StagingAreaToolbarToggleShowResultsButton'; import { memo } from 'react'; @@ -21,6 +22,7 @@ export const StagingAreaToolbar = memo(() => { + diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarSaveAsMenu.tsx b/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarSaveAsMenu.tsx new file mode 100644 index 00000000000..2555000c9c5 --- /dev/null +++ b/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarSaveAsMenu.tsx @@ -0,0 +1,136 @@ +import { IconButton, Menu, MenuButton, MenuItem, MenuList } from '@invoke-ai/ui-library'; +import { useAppStore } from 'app/store/nanostores/store'; +import { useAppSelector } from 'app/store/storeHooks'; +import { NewLayerIcon } from 'features/controlLayers/components/common/icons'; +import { selectSelectedImage } from 'features/controlLayers/store/canvasStagingAreaSlice'; +import { createNewCanvasEntityFromImage } from 'features/imageActions/actions'; +import { toast } from 'features/toast/toast'; +import { memo, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { PiDotsThreeBold } from 'react-icons/pi'; +import { imageDTOToFile, uploadImage } from 'services/api/endpoints/images'; + +const uploadImageArg = { image_category: 'general', is_intermediate: true, silent: true } as const; + +export const StagingAreaToolbarSaveAsMenu = memo(() => { + const { t } = useTranslation(); + const selectedImage = useAppSelector(selectSelectedImage); + const store = useAppStore(); + + const onClickNewRasterLayerFromImage = useCallback(async () => { + if (!selectedImage) { + return; + } + const { dispatch, getState } = store; + const file = await imageDTOToFile(selectedImage.imageDTO); + const imageDTO = await uploadImage({ file, ...uploadImageArg }); + createNewCanvasEntityFromImage({ + imageDTO, + type: 'raster_layer', + dispatch, + getState, + overrides: { isEnabled: false }, // We are adding the layer while staging, it should be disabled by default + }); + toast({ + id: 'SENT_TO_CANVAS', + title: t('toast.sentToCanvas'), + status: 'success', + }); + }, [selectedImage, store, t]); + + const onClickNewControlLayerFromImage = useCallback(async () => { + if (!selectedImage) { + return; + } + const { dispatch, getState } = store; + const file = await imageDTOToFile(selectedImage.imageDTO); + const imageDTO = await uploadImage({ file, ...uploadImageArg }); + createNewCanvasEntityFromImage({ + imageDTO, + type: 'control_layer', + dispatch, + getState, + overrides: { isEnabled: false }, // We are adding the layer while staging, it should be disabled by default + }); + toast({ + id: 'SENT_TO_CANVAS', + title: t('toast.sentToCanvas'), + status: 'success', + }); + }, [selectedImage, store, t]); + + const onClickNewInpaintMaskFromImage = useCallback(async () => { + if (!selectedImage) { + return; + } + const { dispatch, getState } = store; + const file = await imageDTOToFile(selectedImage.imageDTO); + const imageDTO = await uploadImage({ file, ...uploadImageArg }); + createNewCanvasEntityFromImage({ + imageDTO, + type: 'inpaint_mask', + dispatch, + getState, + overrides: { isEnabled: false }, // We are adding the layer while staging, it should be disabled by default + }); + toast({ + id: 'SENT_TO_CANVAS', + title: t('toast.sentToCanvas'), + status: 'success', + }); + }, [selectedImage, store, t]); + + const onClickNewRegionalGuidanceFromImage = useCallback(async () => { + if (!selectedImage) { + return; + } + const { dispatch, getState } = store; + const file = await imageDTOToFile(selectedImage.imageDTO); + const imageDTO = await uploadImage({ file, ...uploadImageArg }); + createNewCanvasEntityFromImage({ + imageDTO, + type: 'regional_guidance', + dispatch, + getState, + overrides: { isEnabled: false }, // We are adding the layer while staging, it should be disabled by default + }); + toast({ + id: 'SENT_TO_CANVAS', + title: t('toast.sentToCanvas'), + status: 'success', + }); + }, [selectedImage, store, t]); + + return ( + + } + colorScheme="invokeBlue" + isDisabled={!selectedImage} + /> + + } onClickCapture={onClickNewInpaintMaskFromImage} isDisabled={!selectedImage}> + {t('controlLayers.inpaintMask')} + + } + onClickCapture={onClickNewRegionalGuidanceFromImage} + isDisabled={!selectedImage} + > + {t('controlLayers.regionalGuidance')} + + } onClickCapture={onClickNewControlLayerFromImage} isDisabled={!selectedImage}> + {t('controlLayers.controlLayer')} + + } onClickCapture={onClickNewRasterLayerFromImage} isDisabled={!selectedImage}> + {t('controlLayers.rasterLayer')} + + + + ); +}); + +StagingAreaToolbarSaveAsMenu.displayName = 'StagingAreaToolbarSaveAsMenu';