From a0f823a3cfe17b3dc47510ec21ee40c2d8d5b444 Mon Sep 17 00:00:00 2001
From: psychedelicious <4822129+psychedelicious@users.noreply.github.com>
Date: Sat, 26 Oct 2024 07:45:29 +1000
Subject: [PATCH 1/8] feat(ui): reset `shouldShowStagedImage` flag when
starting staging
---
.../controlLayers/konva/CanvasStagingAreaModule.ts | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasStagingAreaModule.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasStagingAreaModule.ts
index e092788f80..c100a9d8a3 100644
--- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasStagingAreaModule.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasStagingAreaModule.ts
@@ -51,10 +51,16 @@ export class CanvasStagingAreaModule extends CanvasModuleBase {
/**
* Sync the $isStaging flag with the redux state. $isStaging is used by the manager to determine the global busy
* state of the canvas.
+ *
+ * We also set the $shouldShowStagedImage flag when we enter staging mode, so that the staged images are shown,
+ * even if the user disabled this in the last staging session.
*/
this.subscriptions.add(
- this.manager.stateApi.createStoreSubscription(selectIsStaging, (isStaging) => {
+ this.manager.stateApi.createStoreSubscription(selectIsStaging, (isStaging, oldIsStaging) => {
this.$isStaging.set(isStaging);
+ if (isStaging && !oldIsStaging) {
+ this.$shouldShowStagedImage.set(true);
+ }
})
);
}
From c95b151dafaa7fa0c68ba95bc5183910b9f1e04c Mon Sep 17 00:00:00 2001
From: psychedelicious <4822129+psychedelicious@users.noreply.github.com>
Date: Sat, 26 Oct 2024 09:59:32 +1000
Subject: [PATCH 2/8] feat(ui): add layer title heading for canvas ctx menu
---
.../CanvasContextMenuSelectedEntityMenuItems.tsx | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasContextMenu/CanvasContextMenuSelectedEntityMenuItems.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasContextMenu/CanvasContextMenuSelectedEntityMenuItems.tsx
index 6f70f5b799..57dac5cbb5 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasContextMenu/CanvasContextMenuSelectedEntityMenuItems.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasContextMenu/CanvasContextMenuSelectedEntityMenuItems.tsx
@@ -1,3 +1,4 @@
+import { MenuGroup } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks';
import { ControlLayerMenuItems } from 'features/controlLayers/components/ControlLayer/ControlLayerMenuItems';
import { InpaintMaskMenuItems } from 'features/controlLayers/components/InpaintMask/InpaintMaskMenuItems';
@@ -8,7 +9,9 @@ import {
EntityIdentifierContext,
useEntityIdentifierContext,
} from 'features/controlLayers/contexts/EntityIdentifierContext';
+import { useEntityTypeString } from 'features/controlLayers/hooks/useEntityTypeString';
import { selectSelectedEntityIdentifier } from 'features/controlLayers/store/selectors';
+import type { PropsWithChildren } from 'react';
import { memo } from 'react';
import type { Equals } from 'tsafe';
import { assert } from 'tsafe';
@@ -46,9 +49,20 @@ export const CanvasContextMenuSelectedEntityMenuItems = memo(() => {
return (
-
+
+
+
);
});
CanvasContextMenuSelectedEntityMenuItems.displayName = 'CanvasContextMenuSelectedEntityMenuItems';
+
+const CanvasContextMenuSelectedEntityMenuGroup = memo((props: PropsWithChildren) => {
+ const entityIdentifier = useEntityIdentifierContext();
+ const title = useEntityTypeString(entityIdentifier.type);
+
+ return {props.children};
+});
+
+CanvasContextMenuSelectedEntityMenuGroup.displayName = 'CanvasContextMenuSelectedEntityMenuGroup';
From 813cf87920e0295e06dcf0bf8a3d9e413e655cc7 Mon Sep 17 00:00:00 2001
From: psychedelicious <4822129+psychedelicious@users.noreply.github.com>
Date: Sat, 26 Oct 2024 10:06:10 +1000
Subject: [PATCH 3/8] feat(ui): move canvas alerts to top-left corner
---
.../CanvasAlerts/CanvasAlertsPreserveMask.tsx | 2 +-
.../CanvasAlertsSelectedEntityStatus.tsx | 2 +-
.../CanvasAlerts/CanvasAlertsSendingTo.tsx | 1 -
.../components/CanvasMainPanelContent.tsx | 16 ++++++++++------
.../components/ImageViewer/ImageViewer.tsx | 2 +-
5 files changed, 13 insertions(+), 10 deletions(-)
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasAlerts/CanvasAlertsPreserveMask.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasAlerts/CanvasAlertsPreserveMask.tsx
index bdfeabd1c7..7178c3d123 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasAlerts/CanvasAlertsPreserveMask.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasAlerts/CanvasAlertsPreserveMask.tsx
@@ -13,7 +13,7 @@ export const CanvasAlertsPreserveMask = memo(() => {
}
return (
-
+
{t('controlLayers.settings.preserveMask.alert')}
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasAlerts/CanvasAlertsSelectedEntityStatus.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasAlerts/CanvasAlertsSelectedEntityStatus.tsx
index 6ba087af8e..47bbf36ebd 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasAlerts/CanvasAlertsSelectedEntityStatus.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasAlerts/CanvasAlertsSelectedEntityStatus.tsx
@@ -98,7 +98,7 @@ const CanvasAlertsSelectedEntityStatusContent = memo(({ entityIdentifier, adapte
}
return (
-
+
{alert.title}
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasAlerts/CanvasAlertsSendingTo.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasAlerts/CanvasAlertsSendingTo.tsx
index 16791ee7e6..96d388c70a 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasAlerts/CanvasAlertsSendingTo.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasAlerts/CanvasAlertsSendingTo.tsx
@@ -132,7 +132,6 @@ const AlertWrapper = ({
fontSize="sm"
shadow="md"
w="fit-content"
- alignSelf="flex-end"
>
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasMainPanelContent.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasMainPanelContent.tsx
index 1e8dccfd39..b66c0d8367 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasMainPanelContent.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasMainPanelContent.tsx
@@ -71,12 +71,16 @@ export const CanvasMainPanelContent = memo(() => {
>
- {showHUD && (
-
-
-
- )}
-
+
+ {showHUD && }
diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewer.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewer.tsx
index 47c82ca3d7..30a1afb76b 100644
--- a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewer.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewer.tsx
@@ -57,7 +57,7 @@ export const ImageViewer = memo(({ closeButton }: Props) => {
{hasImageToCompare && }
-
+
From 5739799e2ebffc3e0a74975398ebfe8225c6c584 Mon Sep 17 00:00:00 2001
From: psychedelicious <4822129+psychedelicious@users.noreply.github.com>
Date: Sat, 26 Oct 2024 10:14:30 +1000
Subject: [PATCH 4/8] fix(ui): close viewer when transforming
---
.../web/src/features/controlLayers/hooks/useEntityTransform.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTransform.ts b/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTransform.ts
index 74fa52a8a1..08346f243c 100644
--- a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTransform.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTransform.ts
@@ -52,8 +52,9 @@ export const useEntityTransform = (entityIdentifier: CanvasEntityIdentifier | nu
if (!adapter) {
return;
}
+ imageViewer.close();
await adapter.transformer.startTransform();
- }, [isDisabled, entityIdentifier, canvasManager]);
+ }, [isDisabled, entityIdentifier, canvasManager, imageViewer]);
const fitToBbox = useCallback(async () => {
if (isDisabled) {
From b7510ce709119916fd827225c7d0021a608ea74e Mon Sep 17 00:00:00 2001
From: psychedelicious <4822129+psychedelicious@users.noreply.github.com>
Date: Sat, 26 Oct 2024 10:30:54 +1000
Subject: [PATCH 5/8] feat(ui): filter, select object and transform UI buttons
- Restore dedicated `Apply` buttons
- Remove icons from the buttons, too much noise when the words are short and clear
- Update loading state to show a spinner next to the `Process` button instead of on _every_ button
---
invokeai/frontend/web/public/locales/en.json | 1 +
.../components/Filters/Filter.tsx | 34 +++++++--------
.../components/SelectObject/SelectObject.tsx | 42 +++++++++----------
.../components/Transform/Transform.tsx | 15 +++----
.../Transform/TransformFitToBboxButtons.tsx | 4 +-
5 files changed, 44 insertions(+), 52 deletions(-)
diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json
index cf1b9aedef..6e06557027 100644
--- a/invokeai/frontend/web/public/locales/en.json
+++ b/invokeai/frontend/web/public/locales/en.json
@@ -1894,6 +1894,7 @@
"include": "Include",
"exclude": "Exclude",
"neutral": "Neutral",
+ "apply": "Apply",
"reset": "Reset",
"saveAs": "Save As",
"cancel": "Cancel",
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Filters/Filter.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Filters/Filter.tsx
index 4143b113ca..6aace17412 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/Filters/Filter.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/Filters/Filter.tsx
@@ -8,6 +8,7 @@ import {
MenuItem,
MenuList,
Spacer,
+ Spinner,
} from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { useAppSelector } from 'app/store/storeHooks';
@@ -25,7 +26,7 @@ import { IMAGE_FILTERS } from 'features/controlLayers/store/filters';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { memo, useCallback, useMemo, useRef } from 'react';
import { useTranslation } from 'react-i18next';
-import { PiArrowsCounterClockwiseBold, PiFloppyDiskBold, PiPlayFill, PiXBold } from 'react-icons/pi';
+import { PiCaretDownBold } from 'react-icons/pi';
const FilterContent = memo(
({ adapter }: { adapter: CanvasEntityAdapterRasterLayer | CanvasEntityAdapterControlLayer }) => {
@@ -115,39 +116,41 @@ const FilterContent = memo(
}
onClick={adapter.filterer.processImmediate}
- isLoading={isProcessing}
loadingText={t('controlLayers.filter.process')}
- isDisabled={!isValid || autoProcess}
+ isDisabled={isProcessing || !isValid || autoProcess}
>
{t('controlLayers.filter.process')}
+ {isProcessing && }
}
onClick={adapter.filterer.reset}
- isLoading={isProcessing}
+ isDisabled={isProcessing}
loadingText={t('controlLayers.filter.reset')}
variant="ghost"
>
{t('controlLayers.filter.reset')}
+
- }
- onClick={adapter.filterer.cancel}
- loadingText={t('controlLayers.filter.cancel')}
- >
+
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObject.tsx b/invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObject.tsx
index 72be4daccf..5fb3cc7dca 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObject.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObject.tsx
@@ -10,6 +10,7 @@ import {
MenuItem,
MenuList,
Spacer,
+ Spinner,
Text,
Tooltip,
UnorderedList,
@@ -29,7 +30,7 @@ import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/us
import type { PropsWithChildren } from 'react';
import { memo, useCallback, useRef } from 'react';
import { Trans, useTranslation } from 'react-i18next';
-import { PiArrowsCounterClockwiseBold, PiFloppyDiskBold, PiInfoBold, PiPlayFill, PiXBold } from 'react-icons/pi';
+import { PiCaretDownBold, PiInfoBold } from 'react-icons/pi';
const SelectObjectContent = memo(
({ adapter }: { adapter: CanvasEntityAdapterRasterLayer | CanvasEntityAdapterControlLayer }) => {
@@ -42,10 +43,6 @@ const SelectObjectContent = memo(
const hasImageState = useStore(adapter.segmentAnything.$hasImageState);
const autoProcess = useAppSelector(selectAutoProcess);
- const replaceCurrent = useCallback(() => {
- adapter.segmentAnything.apply();
- }, [adapter.segmentAnything]);
-
const saveAsInpaintMask = useCallback(() => {
adapter.segmentAnything.saveAs('inpaint_mask');
}, [adapter.segmentAnything]);
@@ -115,58 +112,59 @@ const SelectObjectContent = memo(
}
onClick={adapter.segmentAnything.processImmediate}
- isLoading={isProcessing}
loadingText={t('controlLayers.selectObject.process')}
variant="ghost"
- isDisabled={!hasPoints || autoProcess}
+ isDisabled={isProcessing || !hasPoints || autoProcess}
>
{t('controlLayers.selectObject.process')}
+ {isProcessing && }
}
onClick={adapter.segmentAnything.reset}
- isLoading={isProcessing}
+ isDisabled={isProcessing || !hasPoints}
loadingText={t('controlLayers.selectObject.reset')}
variant="ghost"
>
{t('controlLayers.selectObject.reset')}
+
}
onClick={adapter.segmentAnything.cancel}
- isLoading={isProcessing}
+ isDisabled={isProcessing}
loadingText={t('common.cancel')}
variant="ghost"
>
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Transform/Transform.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Transform/Transform.tsx
index d2b4247227..ed0bc6c4d4 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/Transform/Transform.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/Transform/Transform.tsx
@@ -1,4 +1,4 @@
-import { Button, ButtonGroup, Flex, Heading, Spacer } from '@invoke-ai/ui-library';
+import { Button, ButtonGroup, Flex, Heading, Spacer, Spinner } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { useFocusRegion, useIsRegionFocused } from 'common/hooks/focus';
import { CanvasOperationIsolatedLayerPreviewSwitch } from 'features/controlLayers/components/CanvasOperationIsolatedLayerPreviewSwitch';
@@ -8,7 +8,6 @@ import type { CanvasEntityAdapter } from 'features/controlLayers/konva/CanvasEnt
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { memo, useRef } from 'react';
import { useTranslation } from 'react-i18next';
-import { PiArrowsCounterClockwiseBold, PiCheckBold, PiXBold } from 'react-icons/pi';
const TransformContent = memo(({ adapter }: { adapter: CanvasEntityAdapter }) => {
const { t } = useTranslation();
@@ -62,30 +61,28 @@ const TransformContent = memo(({ adapter }: { adapter: CanvasEntityAdapter }) =>
-
+
+ {isProcessing && }
}
onClick={adapter.transformer.resetTransform}
- isLoading={isProcessing}
+ isDisabled={isProcessing}
loadingText={t('controlLayers.transform.reset')}
variant="ghost"
>
{t('controlLayers.transform.reset')}
}
onClick={adapter.transformer.applyTransform}
- isLoading={isProcessing}
+ isDisabled={isProcessing}
loadingText={t('controlLayers.transform.apply')}
variant="ghost"
>
{t('controlLayers.transform.apply')}
}
onClick={adapter.transformer.stopTransform}
- isLoading={isProcessing}
+ isDisabled={isProcessing}
loadingText={t('common.cancel')}
variant="ghost"
>
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Transform/TransformFitToBboxButtons.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Transform/TransformFitToBboxButtons.tsx
index ebe299808b..b76e7632f1 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/Transform/TransformFitToBboxButtons.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/Transform/TransformFitToBboxButtons.tsx
@@ -4,7 +4,6 @@ import { useStore } from '@nanostores/react';
import type { CanvasEntityAdapter } from 'features/controlLayers/konva/CanvasEntity/types';
import { memo, useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
-import { PiArrowsOutBold } from 'react-icons/pi';
import type { Equals } from 'tsafe';
import { assert } from 'tsafe';
import { z } from 'zod';
@@ -60,10 +59,9 @@ export const TransformFitToBboxButtons = memo(({ adapter }: { adapter: CanvasEnt
}
size="sm"
onClick={onClick}
- isLoading={isProcessing}
+ isDisabled={isProcessing}
loadingText={t('controlLayers.transform.fitToBbox')}
variant="ghost"
>
From 56222a849301466647d7d49e638d756ef8187251 Mon Sep 17 00:00:00 2001
From: psychedelicious <4822129+psychedelicious@users.noreply.github.com>
Date: Sat, 26 Oct 2024 10:38:28 +1000
Subject: [PATCH 6/8] feat(ui): organize layer context menu items
---
.../components/ControlLayer/ControlLayerMenuItems.tsx | 5 ++---
.../components/InpaintMask/InpaintMaskMenuItems.tsx | 5 ++---
.../components/RasterLayer/RasterLayerMenuItems.tsx | 5 ++---
.../RegionalGuidance/RegionalGuidanceMenuItems.tsx | 5 ++---
4 files changed, 8 insertions(+), 12 deletions(-)
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerMenuItems.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerMenuItems.tsx
index ab77f238bc..e7e5a45d71 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerMenuItems.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerMenuItems.tsx
@@ -27,11 +27,10 @@ export const ControlLayerMenuItems = memo(() => {
+
+
-
-
-
>
);
});
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/InpaintMask/InpaintMaskMenuItems.tsx b/invokeai/frontend/web/src/features/controlLayers/components/InpaintMask/InpaintMaskMenuItems.tsx
index d8dc481d72..0e0005df80 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/InpaintMask/InpaintMaskMenuItems.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/InpaintMask/InpaintMaskMenuItems.tsx
@@ -20,10 +20,9 @@ export const InpaintMaskMenuItems = memo(() => {
-
-
-
+
+
>
);
});
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerMenuItems.tsx b/invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerMenuItems.tsx
index 212720e064..30a96866f4 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerMenuItems.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerMenuItems.tsx
@@ -25,11 +25,10 @@ export const RasterLayerMenuItems = memo(() => {
+
+
-
-
-
>
);
});
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItems.tsx b/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItems.tsx
index 745730a420..79b3f6dffe 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItems.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItems.tsx
@@ -25,10 +25,9 @@ export const RegionalGuidanceMenuItems = memo(() => {
-
-
-
+
+
>
);
});
From db1c5a94f758448cfba6418fda230f5e7a630d8b Mon Sep 17 00:00:00 2001
From: psychedelicious <4822129+psychedelicious@users.noreply.github.com>
Date: Sat, 26 Oct 2024 10:52:06 +1000
Subject: [PATCH 7/8] feat(ui): image ctx -> New from Image -> Canvas as
Raster/Control Layer
---
invokeai/frontend/web/public/locales/en.json | 2 +
.../controlLayers/hooks/addLayerHooks.ts | 38 +++++++++++++++----
.../ImageMenuItemNewFromImageSubMenu.tsx | 26 +++++++++++--
3 files changed, 55 insertions(+), 11 deletions(-)
diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json
index 6e06557027..726ce2e008 100644
--- a/invokeai/frontend/web/public/locales/en.json
+++ b/invokeai/frontend/web/public/locales/en.json
@@ -1682,6 +1682,8 @@
"controlLayer": "Control Layer",
"inpaintMask": "Inpaint Mask",
"regionalGuidance": "Regional Guidance",
+ "canvasAsRasterLayer": "$t(controlLayers.canvas) as $t(controlLayers.rasterLayer)",
+ "canvasAsControlLayer": "$t(controlLayers.canvas) as $t(controlLayers.controlLayer)",
"referenceImage": "Reference Image",
"regionalReferenceImage": "Regional Reference Image",
"globalReferenceImage": "Global Reference Image",
diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/addLayerHooks.ts b/invokeai/frontend/web/src/features/controlLayers/hooks/addLayerHooks.ts
index 2d19fb8265..6e22c1b626 100644
--- a/invokeai/frontend/web/src/features/controlLayers/hooks/addLayerHooks.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/hooks/addLayerHooks.ts
@@ -46,6 +46,8 @@ import { useCallback } from 'react';
import { modelConfigsAdapterSelectors, selectModelConfigsQuery } from 'services/api/endpoints/models';
import type { ControlNetModelConfig, ImageDTO, IPAdapterModelConfig, T2IAdapterModelConfig } from 'services/api/types';
import { isControlNetOrT2IAdapterModelConfig, isIPAdapterModelConfig } from 'services/api/types';
+import type { Equals } from 'tsafe';
+import { assert } from 'tsafe';
export const selectDefaultControlAdapter = createSelector(
selectModelConfigsQuery,
@@ -194,18 +196,31 @@ export const useNewCanvasFromImage = () => {
const bboxRect = useAppSelector(selectBboxRect);
const base = useAppSelector(selectBboxModelBase);
const func = useCallback(
- (imageDTO: ImageDTO) => {
+ (imageDTO: ImageDTO, type: CanvasRasterLayerState['type'] | CanvasControlLayerState['type']) => {
// Calculate the new bbox dimensions to fit the image's aspect ratio at the optimal size
const ratio = imageDTO.width / imageDTO.height;
const optimalDimension = getOptimalDimension(base);
const { width, height } = calculateNewSize(ratio, optimalDimension ** 2, base);
// The overrides need to include the layer's ID so we can transform the layer it is initialized
- const overrides = {
- id: getPrefixedId('raster_layer'),
- position: { x: bboxRect.x, y: bboxRect.y },
- objects: [imageDTOToImageObject(imageDTO)],
- } satisfies Partial;
+ let overrides: Partial | Partial;
+
+ if (type === 'raster_layer') {
+ overrides = {
+ id: getPrefixedId('raster_layer'),
+ position: { x: bboxRect.x, y: bboxRect.y },
+ objects: [imageDTOToImageObject(imageDTO)],
+ } satisfies Partial;
+ } else if (type === 'control_layer') {
+ overrides = {
+ id: getPrefixedId('control_layer'),
+ position: { x: bboxRect.x, y: bboxRect.y },
+ objects: [imageDTOToImageObject(imageDTO)],
+ } satisfies Partial;
+ } else {
+ // Catch unhandled types
+ assert>(false);
+ }
CanvasEntityAdapterBase.registerInitCallback(async (adapter) => {
// Skip the callback if the adapter is not the one we are creating
@@ -222,7 +237,16 @@ export const useNewCanvasFromImage = () => {
dispatch(canvasReset());
// The `bboxChangedFromCanvas` reducer does no validation! Careful!
dispatch(bboxChangedFromCanvas({ x: 0, y: 0, width, height }));
- dispatch(rasterLayerAdded({ overrides, isSelected: true }));
+
+ // The type casts are safe because the type is checked above
+ if (type === 'raster_layer') {
+ dispatch(rasterLayerAdded({ overrides: overrides as Partial, isSelected: true }));
+ } else if (type === 'control_layer') {
+ dispatch(controlLayerAdded({ overrides: overrides as Partial, isSelected: true }));
+ } else {
+ // Catch unhandled types
+ assert>(false);
+ }
},
[base, bboxRect.x, bboxRect.y, dispatch]
);
diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageMenuItemNewFromImageSubMenu.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageMenuItemNewFromImageSubMenu.tsx
index 7d0d599c3e..3ea5dd5bc5 100644
--- a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageMenuItemNewFromImageSubMenu.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageMenuItemNewFromImageSubMenu.tsx
@@ -32,8 +32,19 @@ export const ImageMenuItemNewFromImageSubMenu = memo(() => {
const newRegionalGuidanceFromImage = useNewRegionalGuidanceFromImage();
const newCanvasFromImage = useNewCanvasFromImage();
- const onClickNewCanvasFromImage = useCallback(() => {
- newCanvasFromImage(imageDTO);
+ const onClickNewCanvasWithRasterLayerFromImage = useCallback(() => {
+ newCanvasFromImage(imageDTO, 'raster_layer');
+ dispatch(setActiveTab('canvas'));
+ imageViewer.close();
+ toast({
+ id: 'SENT_TO_CANVAS',
+ title: t('toast.sentToCanvas'),
+ status: 'success',
+ });
+ }, [dispatch, imageDTO, imageViewer, newCanvasFromImage, t]);
+
+ const onClickNewCanvasWithControlLayerFromImage = useCallback(() => {
+ newCanvasFromImage(imageDTO, 'control_layer');
dispatch(setActiveTab('canvas'));
imageViewer.close();
toast({
@@ -98,8 +109,15 @@ export const ImageMenuItemNewFromImageSubMenu = memo(() => {
- } onClickCapture={onClickNewCanvasFromImage} isDisabled={isBusy}>
- {t('controlLayers.canvas')}
+ } onClickCapture={onClickNewCanvasWithRasterLayerFromImage} isDisabled={isBusy}>
+ {t('controlLayers.canvasAsRasterLayer')}
+
+ }
+ onClickCapture={onClickNewCanvasWithControlLayerFromImage}
+ isDisabled={isBusy}
+ >
+ {t('controlLayers.canvasAsControlLayer')}
} onClickCapture={onClickNewInpaintMaskFromImage} isDisabled={isBusy}>
{t('controlLayers.inpaintMask')}
From aebcec28e0204f8936da266bbc7d0e5dcae2440f Mon Sep 17 00:00:00 2001
From: psychedelicious <4822129+psychedelicious@users.noreply.github.com>
Date: Sat, 26 Oct 2024 12:05:18 +1100
Subject: [PATCH 8/8] chore: bump version to v5.3.0
---
invokeai/version/invokeai_version.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/invokeai/version/invokeai_version.py b/invokeai/version/invokeai_version.py
index af963de03f..f5752882da 100644
--- a/invokeai/version/invokeai_version.py
+++ b/invokeai/version/invokeai_version.py
@@ -1 +1 @@
-__version__ = "5.3.0rc2"
+__version__ = "5.3.0"