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( + } - isLoading={isProcessing} loadingText={t('controlLayers.selectObject.saveAs')} variant="ghost" - isDisabled={!isValid || !hasProcessed} + isDisabled={isProcessing || !isValid || !hasProcessed} + rightIcon={} > {t('controlLayers.selectObject.saveAs')} - - {t('controlLayers.replaceCurrent')} - {t('controlLayers.newInpaintMask')} @@ -162,12 +165,7 @@ const FilterContent = memo( - 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( + } - isLoading={isProcessing} loadingText={t('controlLayers.selectObject.saveAs')} variant="ghost" - isDisabled={!hasImageState} + isDisabled={isProcessing || !hasImageState} + rightIcon={} > {t('controlLayers.selectObject.saveAs')} - - {t('controlLayers.replaceCurrent')} - - + {t('controlLayers.newInpaintMask')} - + {t('controlLayers.newRegionalGuidance')} - + {t('controlLayers.newControlLayer')} - + {t('controlLayers.newRasterLayer')}