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 (
+
+ );
+});
+
+StagingAreaToolbarSaveAsMenu.displayName = 'StagingAreaToolbarSaveAsMenu';