mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
feat(ui): prevent creating new canvases while staging
Disable these items while staging: - New Canvas From Image context menu - Edit image hook & launchpad button - Generate from Text launchpad button (only while on canvas tab) - Use a Layout Image launchpad button
This commit is contained in:
@@ -2,6 +2,7 @@ import { Menu, MenuButton, MenuItem, MenuList } from '@invoke-ai/ui-library';
|
||||
import { useAppStore } from 'app/store/storeHooks';
|
||||
import { SubMenuButtonContent, useSubMenu } from 'common/hooks/useSubMenu';
|
||||
import { useCanvasIsBusySafe } from 'features/controlLayers/hooks/useCanvasIsBusy';
|
||||
import { useCanvasIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { useImageDTOContext } from 'features/gallery/contexts/ImageDTOContext';
|
||||
import { newCanvasFromImage } from 'features/imageActions/actions';
|
||||
import { toast } from 'features/toast/toast';
|
||||
@@ -17,6 +18,7 @@ export const ImageMenuItemNewCanvasFromImageSubMenu = memo(() => {
|
||||
const store = useAppStore();
|
||||
const imageDTO = useImageDTOContext();
|
||||
const isBusy = useCanvasIsBusySafe();
|
||||
const isStaging = useCanvasIsStaging();
|
||||
|
||||
const onClickNewCanvasWithRasterLayerFromImage = useCallback(async () => {
|
||||
const { dispatch, getState } = store;
|
||||
@@ -97,27 +99,31 @@ export const ImageMenuItemNewCanvasFromImageSubMenu = memo(() => {
|
||||
<SubMenuButtonContent label={t('controlLayers.newCanvasFromImage')} />
|
||||
</MenuButton>
|
||||
<MenuList {...subMenu.menuListProps}>
|
||||
<MenuItem icon={<PiFileBold />} onClickCapture={onClickNewCanvasWithRasterLayerFromImage} isDisabled={isBusy}>
|
||||
<MenuItem
|
||||
icon={<PiFileBold />}
|
||||
onClickCapture={onClickNewCanvasWithRasterLayerFromImage}
|
||||
isDisabled={isStaging || isBusy}
|
||||
>
|
||||
{t('controlLayers.asRasterLayer')}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon={<PiFileBold />}
|
||||
onClickCapture={onClickNewCanvasWithRasterLayerFromImageWithResize}
|
||||
isDisabled={isBusy}
|
||||
isDisabled={isStaging || isBusy}
|
||||
>
|
||||
{t('controlLayers.asRasterLayerResize')}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon={<PiFileBold />}
|
||||
onClickCapture={onClickNewCanvasWithControlLayerFromImage}
|
||||
isDisabled={isBusy}
|
||||
isDisabled={isStaging || isBusy}
|
||||
>
|
||||
{t('controlLayers.asControlLayer')}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon={<PiFileBold />}
|
||||
onClickCapture={onClickNewCanvasWithControlLayerFromImageWithResize}
|
||||
isDisabled={isBusy}
|
||||
isDisabled={isStaging || isBusy}
|
||||
>
|
||||
{t('controlLayers.asControlLayerResize')}
|
||||
</MenuItem>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useAppStore } from 'app/store/storeHooks';
|
||||
import { useCanvasManagerSafe } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import { useCanvasIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { newCanvasFromImage } from 'features/imageActions/actions';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import { navigationApi } from 'features/ui/layouts/navigation-api';
|
||||
@@ -13,13 +14,17 @@ export const useEditImage = (imageDTO?: ImageDTO | null) => {
|
||||
|
||||
const { getState, dispatch } = useAppStore();
|
||||
const canvasManager = useCanvasManagerSafe();
|
||||
const isStaging = useCanvasIsStaging();
|
||||
|
||||
const isEnabled = useMemo(() => {
|
||||
if (!imageDTO) {
|
||||
return false;
|
||||
}
|
||||
if (isStaging) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}, [imageDTO]);
|
||||
}, [imageDTO, isStaging]);
|
||||
|
||||
const edit = useCallback(async () => {
|
||||
if (!imageDTO) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Flex, Heading, Icon, Text } from '@invoke-ai/ui-library';
|
||||
import { useAppStore } from 'app/store/storeHooks';
|
||||
import { useImageUploadButton } from 'common/hooks/useImageUploadButton';
|
||||
import { useCanvasIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { newCanvasFromImageDndTarget } from 'features/dnd/dnd';
|
||||
import { DndDropTarget } from 'features/dnd/DndDropTarget';
|
||||
import { newCanvasFromImage } from 'features/imageActions/actions';
|
||||
@@ -17,6 +18,7 @@ const dndTargetData = newCanvasFromImageDndTarget.getData(NEW_CANVAS_OPTIONS);
|
||||
export const LaunchpadEditImageButton = memo((props: { extraAction?: () => void }) => {
|
||||
const { t } = useTranslation();
|
||||
const { getState, dispatch } = useAppStore();
|
||||
const isStaging = useCanvasIsStaging();
|
||||
|
||||
const onUpload = useCallback(
|
||||
(imageDTO: ImageDTO) => {
|
||||
@@ -28,17 +30,22 @@ export const LaunchpadEditImageButton = memo((props: { extraAction?: () => void
|
||||
const uploadApi = useImageUploadButton({ allowMultiple: false, onUpload });
|
||||
|
||||
return (
|
||||
<LaunchpadButton {...uploadApi.getUploadButtonProps()} position="relative" gap={8}>
|
||||
<LaunchpadButton {...uploadApi.getUploadButtonProps()} position="relative" gap={8} isDisabled={isStaging}>
|
||||
<Icon as={PiPencilBold} boxSize={8} color="base.500" />
|
||||
<Flex flexDir="column" alignItems="flex-start" gap={2}>
|
||||
<Heading size="sm">{t('ui.launchpad.editImage.title')}</Heading>
|
||||
<Text color="base.300">{t('ui.launchpad.editImage.description')}</Text>
|
||||
<Text>{t('ui.launchpad.editImage.description')}</Text>
|
||||
</Flex>
|
||||
<Flex position="absolute" right={3} bottom={3}>
|
||||
<PiUploadBold />
|
||||
<input {...uploadApi.getUploadInputProps()} />
|
||||
</Flex>
|
||||
<DndDropTarget dndTarget={newCanvasFromImageDndTarget} dndTargetData={dndTargetData} label="Drop" />
|
||||
<DndDropTarget
|
||||
dndTarget={newCanvasFromImageDndTarget}
|
||||
dndTargetData={dndTargetData}
|
||||
label="Drop"
|
||||
isDisabled={isStaging}
|
||||
/>
|
||||
</LaunchpadButton>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { Flex, Heading, Icon, Text } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useCanvasIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { LaunchpadButton } from 'features/ui/layouts/LaunchpadButton';
|
||||
import { selectActiveTab } from 'features/ui/store/uiSelectors';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiCursorTextBold, PiTextAaBold } from 'react-icons/pi';
|
||||
@@ -14,16 +17,19 @@ const focusOnPrompt = () => {
|
||||
|
||||
export const LaunchpadGenerateFromTextButton = memo((props: { extraAction?: () => void }) => {
|
||||
const { t } = useTranslation();
|
||||
const tab = useAppSelector(selectActiveTab);
|
||||
const isStaging = useCanvasIsStaging();
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
focusOnPrompt();
|
||||
props.extraAction?.();
|
||||
}, [props]);
|
||||
return (
|
||||
<LaunchpadButton onClick={onClick} position="relative" gap={8}>
|
||||
<LaunchpadButton onClick={onClick} position="relative" gap={8} isDisabled={tab === 'canvas' && isStaging}>
|
||||
<Icon as={PiTextAaBold} boxSize={8} color="base.500" />
|
||||
<Flex flexDir="column" alignItems="flex-start" gap={2}>
|
||||
<Heading size="sm">{t('ui.launchpad.generateFromText.title')}</Heading>
|
||||
<Text color="base.300">{t('ui.launchpad.generateFromText.description')}</Text>
|
||||
<Text>{t('ui.launchpad.generateFromText.description')}</Text>
|
||||
</Flex>
|
||||
<Flex position="absolute" right={3} bottom={3}>
|
||||
<PiCursorTextBold />
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Flex, Heading, Icon, Text } from '@invoke-ai/ui-library';
|
||||
import { useAppStore } from 'app/store/storeHooks';
|
||||
import { useImageUploadButton } from 'common/hooks/useImageUploadButton';
|
||||
import { useCanvasIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { newCanvasFromImageDndTarget } from 'features/dnd/dnd';
|
||||
import { DndDropTarget } from 'features/dnd/DndDropTarget';
|
||||
import { newCanvasFromImage } from 'features/imageActions/actions';
|
||||
@@ -18,6 +19,7 @@ const dndTargetData = newCanvasFromImageDndTarget.getData(NEW_CANVAS_OPTIONS);
|
||||
export const LaunchpadUseALayoutImageButton = memo((props: { extraAction?: () => void }) => {
|
||||
const { t } = useTranslation();
|
||||
const { getState, dispatch } = useAppStore();
|
||||
const isStaging = useCanvasIsStaging();
|
||||
|
||||
const onUpload = useCallback(
|
||||
(imageDTO: ImageDTO) => {
|
||||
@@ -29,17 +31,22 @@ export const LaunchpadUseALayoutImageButton = memo((props: { extraAction?: () =>
|
||||
const uploadApi = useImageUploadButton({ allowMultiple: false, onUpload });
|
||||
|
||||
return (
|
||||
<LaunchpadButton {...uploadApi.getUploadButtonProps()} position="relative" gap={8}>
|
||||
<LaunchpadButton {...uploadApi.getUploadButtonProps()} position="relative" gap={8} isDisabled={isStaging}>
|
||||
<Icon as={PiRectangleDashedBold} boxSize={8} color="base.500" />
|
||||
<Flex flexDir="column" alignItems="flex-start" gap={2}>
|
||||
<Heading size="sm">{t('ui.launchpad.useALayoutImage.title')}</Heading>
|
||||
<Text color="base.300">{t('ui.launchpad.useALayoutImage.description')}</Text>
|
||||
<Text>{t('ui.launchpad.useALayoutImage.description')}</Text>
|
||||
</Flex>
|
||||
<Flex position="absolute" right={3} bottom={3}>
|
||||
<PiUploadBold />
|
||||
<input {...uploadApi.getUploadInputProps()} />
|
||||
</Flex>
|
||||
<DndDropTarget dndTarget={newCanvasFromImageDndTarget} dndTargetData={dndTargetData} label="Drop" />
|
||||
<DndDropTarget
|
||||
dndTarget={newCanvasFromImageDndTarget}
|
||||
dndTargetData={dndTargetData}
|
||||
label="Drop"
|
||||
isDisabled={isStaging}
|
||||
/>
|
||||
</LaunchpadButton>
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user