mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): revise drop zones
The main viewer area has two drop zones: - Select for Viewer - Select for Compare These do what you'd imagine they would do.
This commit is contained in:
parent
0f0a6852f1
commit
ff2b2fad83
@ -380,6 +380,7 @@
|
|||||||
"problemDeletingImagesDesc": "One or more images could not be deleted",
|
"problemDeletingImagesDesc": "One or more images could not be deleted",
|
||||||
"viewerImage": "Viewer Image",
|
"viewerImage": "Viewer Image",
|
||||||
"compareImage": "Compare Image",
|
"compareImage": "Compare Image",
|
||||||
|
"selectForViewer": "Select for Viewer",
|
||||||
"selectForCompare": "Select for Compare",
|
"selectForCompare": "Select for Compare",
|
||||||
"selectAnImageToCompare": "Select an Image to Compare",
|
"selectAnImageToCompare": "Select an Image to Compare",
|
||||||
"slider": "Slider",
|
"slider": "Slider",
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import type { TypesafeDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
import type { TypesafeDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
||||||
import { isValidDrop } from 'features/dnd/util/isValidDrop';
|
import { isValidDrop } from 'features/dnd/util/isValidDrop';
|
||||||
import { imageSelected, imageToCompareChanged } from 'features/gallery/store/gallerySlice';
|
import { imageSelected, imageToCompareChanged, isImageViewerOpenChanged } from 'features/gallery/store/gallerySlice';
|
||||||
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
|
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
|
||||||
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
import { imagesApi } from 'services/api/endpoints/images';
|
import { imagesApi } from 'services/api/endpoints/images';
|
||||||
@ -54,6 +54,7 @@ export const addImageDroppedListener = (startAppListening: AppStartListening) =>
|
|||||||
activeData.payload.imageDTO
|
activeData.payload.imageDTO
|
||||||
) {
|
) {
|
||||||
dispatch(imageSelected(activeData.payload.imageDTO));
|
dispatch(imageSelected(activeData.payload.imageDTO));
|
||||||
|
dispatch(isImageViewerOpenChanged(true));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,6 +196,7 @@ export const addImageDroppedListener = (startAppListening: AppStartListening) =>
|
|||||||
) {
|
) {
|
||||||
const { imageDTO } = activeData.payload;
|
const { imageDTO } = activeData.payload;
|
||||||
dispatch(imageToCompareChanged(imageDTO));
|
dispatch(imageToCompareChanged(imageDTO));
|
||||||
|
dispatch(isImageViewerOpenChanged(true));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ type BaseDropData = {
|
|||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type CurrentImageDropData = BaseDropData & {
|
export type CurrentImageDropData = BaseDropData & {
|
||||||
actionType: 'SET_CURRENT_IMAGE';
|
actionType: 'SET_CURRENT_IMAGE';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -30,11 +30,7 @@ export const isValidDrop = (overData?: TypesafeDroppableData | null, activeData?
|
|||||||
case 'SET_NODES_IMAGE':
|
case 'SET_NODES_IMAGE':
|
||||||
return payloadType === 'IMAGE_DTO';
|
return payloadType === 'IMAGE_DTO';
|
||||||
case 'SELECT_FOR_COMPARE':
|
case 'SELECT_FOR_COMPARE':
|
||||||
return (
|
return payloadType === 'IMAGE_DTO';
|
||||||
payloadType === 'IMAGE_DTO' &&
|
|
||||||
activeData.id !== 'image-compare-first-image' &&
|
|
||||||
activeData.id !== 'image-compare-second-image'
|
|
||||||
);
|
|
||||||
case 'ADD_TO_BOARD': {
|
case 'ADD_TO_BOARD': {
|
||||||
// If the board is the same, don't allow the drop
|
// If the board is the same, don't allow the drop
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ import IAIDndImage from 'common/components/IAIDndImage';
|
|||||||
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
||||||
import type { TypesafeDraggableData } from 'features/dnd/types';
|
import type { TypesafeDraggableData } from 'features/dnd/types';
|
||||||
import ImageMetadataViewer from 'features/gallery/components/ImageMetadataViewer/ImageMetadataViewer';
|
import ImageMetadataViewer from 'features/gallery/components/ImageMetadataViewer/ImageMetadataViewer';
|
||||||
import { ImageComparisonDroppable } from 'features/gallery/components/ImageViewer/ImageComparisonDroppable';
|
|
||||||
import NextPrevImageButtons from 'features/gallery/components/NextPrevImageButtons';
|
import NextPrevImageButtons from 'features/gallery/components/NextPrevImageButtons';
|
||||||
import { selectLastSelectedImage } from 'features/gallery/store/gallerySelectors';
|
import { selectLastSelectedImage } from 'features/gallery/store/gallerySelectors';
|
||||||
import type { AnimationProps } from 'framer-motion';
|
import type { AnimationProps } from 'framer-motion';
|
||||||
@ -75,12 +74,10 @@ const CurrentImagePreview = () => {
|
|||||||
isUploadDisabled={true}
|
isUploadDisabled={true}
|
||||||
fitContainer
|
fitContainer
|
||||||
useThumbailFallback
|
useThumbailFallback
|
||||||
dropLabel={t('gallery.setCurrentImage')}
|
|
||||||
noContentFallback={<IAINoContentFallback icon={PiImageBold} label={t('gallery.noImageSelected')} />}
|
noContentFallback={<IAINoContentFallback icon={PiImageBold} label={t('gallery.noImageSelected')} />}
|
||||||
dataTestId="image-preview"
|
dataTestId="image-preview"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<ImageComparisonDroppable />
|
|
||||||
{shouldShowImageDetails && imageDTO && (
|
{shouldShowImageDetails && imageDTO && (
|
||||||
<Box position="absolute" opacity={0.8} top={0} width="full" height="full" borderRadius="base">
|
<Box position="absolute" opacity={0.8} top={0} width="full" height="full" borderRadius="base">
|
||||||
<ImageMetadataViewer image={imageDTO} />
|
<ImageMetadataViewer image={imageDTO} />
|
||||||
|
@ -1,16 +1,12 @@
|
|||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIDroppable from 'common/components/IAIDroppable';
|
|
||||||
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
||||||
import type { SelectForCompareDropData } from 'features/dnd/types';
|
|
||||||
import { ImageComparisonSideBySide } from 'features/gallery/components/ImageViewer/ImageComparisonSideBySide';
|
import { ImageComparisonSideBySide } from 'features/gallery/components/ImageViewer/ImageComparisonSideBySide';
|
||||||
import { ImageComparisonSlider } from 'features/gallery/components/ImageViewer/ImageComparisonSlider';
|
import { ImageComparisonSlider } from 'features/gallery/components/ImageViewer/ImageComparisonSlider';
|
||||||
import { selectGallerySlice } from 'features/gallery/store/gallerySlice';
|
import { selectGallerySlice } from 'features/gallery/store/gallerySlice';
|
||||||
import type { PropsWithChildren } from 'react';
|
import { memo } from 'react';
|
||||||
import { memo, useMemo } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { PiImagesBold } from 'react-icons/pi';
|
import { PiImagesBold } from 'react-icons/pi';
|
||||||
import type { ImageDTO } from 'services/api/types';
|
|
||||||
|
|
||||||
const selector = createMemoizedSelector(selectGallerySlice, (gallerySlice) => {
|
const selector = createMemoizedSelector(selectGallerySlice, (gallerySlice) => {
|
||||||
const firstImage = gallerySlice.selection.slice(-1)[0] ?? null;
|
const firstImage = gallerySlice.selection.slice(-1)[0] ?? null;
|
||||||
@ -24,56 +20,17 @@ export const ImageComparison = memo(() => {
|
|||||||
const { firstImage, secondImage } = useAppSelector(selector);
|
const { firstImage, secondImage } = useAppSelector(selector);
|
||||||
|
|
||||||
if (!firstImage || !secondImage) {
|
if (!firstImage || !secondImage) {
|
||||||
return (
|
// Should rarely/never happen - we don't render this component unless we have images to compare
|
||||||
<ImageComparisonWrapper firstImage={firstImage} secondImage={secondImage}>
|
return <IAINoContentFallback label={t('gallery.selectAnImageToCompare')} icon={PiImagesBold} />;
|
||||||
<IAINoContentFallback label={t('gallery.selectAnImageToCompare')} icon={PiImagesBold} />
|
|
||||||
</ImageComparisonWrapper>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (comparisonMode === 'slider') {
|
if (comparisonMode === 'slider') {
|
||||||
return (
|
return <ImageComparisonSlider firstImage={firstImage} secondImage={secondImage} />;
|
||||||
<ImageComparisonWrapper firstImage={firstImage} secondImage={secondImage}>
|
|
||||||
<ImageComparisonSlider firstImage={firstImage} secondImage={secondImage} />
|
|
||||||
</ImageComparisonWrapper>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (comparisonMode === 'side-by-side') {
|
if (comparisonMode === 'side-by-side') {
|
||||||
return (
|
return <ImageComparisonSideBySide firstImage={firstImage} secondImage={secondImage} />;
|
||||||
<ImageComparisonWrapper firstImage={firstImage} secondImage={secondImage}>
|
|
||||||
<ImageComparisonSideBySide firstImage={firstImage} secondImage={secondImage} />
|
|
||||||
</ImageComparisonWrapper>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ImageComparison.displayName = 'ImageComparison';
|
ImageComparison.displayName = 'ImageComparison';
|
||||||
|
|
||||||
type Props = PropsWithChildren<{
|
|
||||||
firstImage: ImageDTO | null;
|
|
||||||
secondImage: ImageDTO | null;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
const ImageComparisonWrapper = memo((props: Props) => {
|
|
||||||
const droppableData = useMemo<SelectForCompareDropData>(
|
|
||||||
() => ({
|
|
||||||
id: 'image-comparison',
|
|
||||||
actionType: 'SELECT_FOR_COMPARE',
|
|
||||||
context: {
|
|
||||||
firstImageName: props.firstImage?.image_name,
|
|
||||||
secondImageName: props.secondImage?.image_name,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
[props.firstImage?.image_name, props.secondImage?.image_name]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{props.children}
|
|
||||||
<IAIDroppable data={droppableData} dropLabel="Select for Compare" />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
ImageComparisonWrapper.displayName = 'ImageComparisonWrapper';
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
import { Flex } from '@invoke-ai/ui-library';
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIDroppable from 'common/components/IAIDroppable';
|
import IAIDroppable from 'common/components/IAIDroppable';
|
||||||
import type { SelectForCompareDropData } from 'features/dnd/types';
|
import type { CurrentImageDropData, SelectForCompareDropData } from 'features/dnd/types';
|
||||||
import { selectGallerySlice } from 'features/gallery/store/gallerySlice';
|
import { selectGallerySlice } from 'features/gallery/store/gallerySlice';
|
||||||
import { memo, useMemo } from 'react';
|
import { memo, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -12,10 +13,15 @@ const selector = createMemoizedSelector(selectGallerySlice, (gallerySlice) => {
|
|||||||
return { firstImage, secondImage };
|
return { firstImage, secondImage };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const setCurrentImageDropData: CurrentImageDropData = {
|
||||||
|
id: 'current-image',
|
||||||
|
actionType: 'SET_CURRENT_IMAGE',
|
||||||
|
};
|
||||||
|
|
||||||
export const ImageComparisonDroppable = memo(() => {
|
export const ImageComparisonDroppable = memo(() => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { firstImage, secondImage } = useAppSelector(selector);
|
const { firstImage, secondImage } = useAppSelector(selector);
|
||||||
const droppableData = useMemo<SelectForCompareDropData>(
|
const selectForCompareDropData = useMemo<SelectForCompareDropData>(
|
||||||
() => ({
|
() => ({
|
||||||
id: 'image-comparison',
|
id: 'image-comparison',
|
||||||
actionType: 'SELECT_FOR_COMPARE',
|
actionType: 'SELECT_FOR_COMPARE',
|
||||||
@ -27,7 +33,16 @@ export const ImageComparisonDroppable = memo(() => {
|
|||||||
[firstImage?.image_name, secondImage?.image_name]
|
[firstImage?.image_name, secondImage?.image_name]
|
||||||
);
|
);
|
||||||
|
|
||||||
return <IAIDroppable data={droppableData} dropLabel={t('gallery.selectForCompare')} />;
|
return (
|
||||||
|
<Flex position="absolute" top={0} right={0} bottom={0} left={0} gap={2} pointerEvents="none">
|
||||||
|
<Flex position="relative" flex={1}>
|
||||||
|
<IAIDroppable data={setCurrentImageDropData} dropLabel={t('gallery.selectForViewer')} />
|
||||||
|
</Flex>
|
||||||
|
<Flex position="relative" flex={1}>
|
||||||
|
<IAIDroppable data={selectForCompareDropData} dropLabel={t('gallery.selectForCompare')} />
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
ImageComparisonDroppable.displayName = 'ImageComparisonDroppable';
|
ImageComparisonDroppable.displayName = 'ImageComparisonDroppable';
|
||||||
|
@ -132,7 +132,7 @@ export const ImageComparisonSlider = memo(({ firstImage, secondImage }: Props) =
|
|||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex
|
||||||
id="image-comparison-container"
|
id="image-comparison-wrapper"
|
||||||
w="full"
|
w="full"
|
||||||
h="full"
|
h="full"
|
||||||
maxW="full"
|
maxW="full"
|
||||||
@ -144,7 +144,7 @@ export const ImageComparisonSlider = memo(({ firstImage, secondImage }: Props) =
|
|||||||
<Box
|
<Box
|
||||||
ref={imageContainerRef}
|
ref={imageContainerRef}
|
||||||
position="relative"
|
position="relative"
|
||||||
id="image-comparison-second-image-container"
|
id="image-comparison-image-container"
|
||||||
w={fittedSize.width}
|
w={fittedSize.width}
|
||||||
h={fittedSize.height}
|
h={fittedSize.height}
|
||||||
maxW="full"
|
maxW="full"
|
||||||
@ -163,9 +163,10 @@ export const ImageComparisonSlider = memo(({ firstImage, secondImage }: Props) =
|
|||||||
backgroundImage={STAGE_BG_DATAURL}
|
backgroundImage={STAGE_BG_DATAURL}
|
||||||
backgroundRepeat="repeat"
|
backgroundRepeat="repeat"
|
||||||
opacity={0.2}
|
opacity={0.2}
|
||||||
zIndex={-1}
|
|
||||||
/>
|
/>
|
||||||
<Image
|
<Image
|
||||||
|
position="relative"
|
||||||
|
id="image-comparison-second-image"
|
||||||
src={secondImage.image_url}
|
src={secondImage.image_url}
|
||||||
fallbackSrc={secondImage.thumbnail_url}
|
fallbackSrc={secondImage.thumbnail_url}
|
||||||
w={sliderFit === 'fill' ? fittedSize.width : (fittedSize.width * secondImage.width) / firstImage.width}
|
w={sliderFit === 'fill' ? fittedSize.width : (fittedSize.width * secondImage.width) / firstImage.width}
|
||||||
@ -197,6 +198,7 @@ export const ImageComparisonSlider = memo(({ firstImage, secondImage }: Props) =
|
|||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
|
id="image-comparison-first-image"
|
||||||
src={firstImage.image_url}
|
src={firstImage.image_url}
|
||||||
fallbackSrc={firstImage.thumbnail_url}
|
fallbackSrc={firstImage.thumbnail_url}
|
||||||
w={fittedSize.width}
|
w={fittedSize.width}
|
||||||
|
@ -15,14 +15,18 @@ const VIEWER_ENABLED_TABS: InvokeTabName[] = ['canvas', 'generation', 'workflows
|
|||||||
export const ImageViewer = memo(() => {
|
export const ImageViewer = memo(() => {
|
||||||
const { isOpen, onToggle, onClose } = useImageViewer();
|
const { isOpen, onToggle, onClose } = useImageViewer();
|
||||||
const activeTabName = useAppSelector(activeTabNameSelector);
|
const activeTabName = useAppSelector(activeTabNameSelector);
|
||||||
|
const workflowsMode = useAppSelector((s) => s.workflow.mode);
|
||||||
const isComparing = useAppSelector((s) => s.gallery.imageToCompare !== null);
|
const isComparing = useAppSelector((s) => s.gallery.imageToCompare !== null);
|
||||||
const isViewerEnabled = useMemo(() => VIEWER_ENABLED_TABS.includes(activeTabName), [activeTabName]);
|
const isViewerEnabled = useMemo(() => VIEWER_ENABLED_TABS.includes(activeTabName), [activeTabName]);
|
||||||
const shouldShowViewer = useMemo(() => {
|
const shouldShowViewer = useMemo(() => {
|
||||||
|
if (activeTabName === 'workflows' && workflowsMode === 'view') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (!isViewerEnabled) {
|
if (!isViewerEnabled) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return isOpen;
|
return isOpen;
|
||||||
}, [isOpen, isViewerEnabled]);
|
}, [isOpen, isViewerEnabled, workflowsMode, activeTabName]);
|
||||||
|
|
||||||
useHotkeys('z', onToggle, { enabled: isViewerEnabled }, [isViewerEnabled, onToggle]);
|
useHotkeys('z', onToggle, { enabled: isViewerEnabled }, [isViewerEnabled, onToggle]);
|
||||||
useHotkeys('esc', onClose, { enabled: isViewerEnabled }, [isViewerEnabled, onClose]);
|
useHotkeys('esc', onClose, { enabled: isViewerEnabled }, [isViewerEnabled, onClose]);
|
||||||
@ -45,7 +49,6 @@ export const ImageViewer = memo(() => {
|
|||||||
rowGap={4}
|
rowGap={4}
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
zIndex={10} // reactflow puts its minimap at 5, so we need to be above that
|
|
||||||
>
|
>
|
||||||
{isComparing && <CompareToolbar />}
|
{isComparing && <CompareToolbar />}
|
||||||
{!isComparing && <ViewerToolbar />}
|
{!isComparing && <ViewerToolbar />}
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
import { Flex } from '@invoke-ai/ui-library';
|
|
||||||
import { ToggleMetadataViewerButton } from 'features/gallery/components/ImageViewer/ToggleMetadataViewerButton';
|
|
||||||
import { ToggleProgressButton } from 'features/gallery/components/ImageViewer/ToggleProgressButton';
|
|
||||||
import { memo } from 'react';
|
|
||||||
|
|
||||||
import CurrentImageButtons from './CurrentImageButtons';
|
|
||||||
import CurrentImagePreview from './CurrentImagePreview';
|
|
||||||
|
|
||||||
export const ImageViewerWorkflows = memo(() => {
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
layerStyle="first"
|
|
||||||
borderRadius="base"
|
|
||||||
position="absolute"
|
|
||||||
flexDirection="column"
|
|
||||||
top={0}
|
|
||||||
right={0}
|
|
||||||
bottom={0}
|
|
||||||
left={0}
|
|
||||||
p={2}
|
|
||||||
rowGap={4}
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="center"
|
|
||||||
zIndex={10} // reactflow puts its minimap at 5, so we need to be above that
|
|
||||||
>
|
|
||||||
<Flex w="full" gap={2}>
|
|
||||||
<Flex flex={1} justifyContent="center">
|
|
||||||
<Flex gap={2} marginInlineEnd="auto">
|
|
||||||
<ToggleProgressButton />
|
|
||||||
<ToggleMetadataViewerButton />
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Flex flex={1} gap={2} justifyContent="center">
|
|
||||||
<CurrentImageButtons />
|
|
||||||
</Flex>
|
|
||||||
<Flex flex={1} justifyContent="center">
|
|
||||||
<Flex gap={2} marginInlineStart="auto" />
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<CurrentImagePreview />
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
ImageViewerWorkflows.displayName = 'ImageViewerWorkflows';
|
|
@ -1,12 +1,23 @@
|
|||||||
import { Flex } from '@invoke-ai/ui-library';
|
import { Flex } from '@invoke-ai/ui-library';
|
||||||
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { ToggleMetadataViewerButton } from 'features/gallery/components/ImageViewer/ToggleMetadataViewerButton';
|
import { ToggleMetadataViewerButton } from 'features/gallery/components/ImageViewer/ToggleMetadataViewerButton';
|
||||||
import { ToggleProgressButton } from 'features/gallery/components/ImageViewer/ToggleProgressButton';
|
import { ToggleProgressButton } from 'features/gallery/components/ImageViewer/ToggleProgressButton';
|
||||||
import { memo } from 'react';
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
|
import { memo, useMemo } from 'react';
|
||||||
|
|
||||||
import CurrentImageButtons from './CurrentImageButtons';
|
import CurrentImageButtons from './CurrentImageButtons';
|
||||||
import { ViewerToggleMenu } from './ViewerToggleMenu';
|
import { ViewerToggleMenu } from './ViewerToggleMenu';
|
||||||
|
|
||||||
export const ViewerToolbar = memo(() => {
|
export const ViewerToolbar = memo(() => {
|
||||||
|
const workflowsMode = useAppSelector((s) => s.workflow.mode);
|
||||||
|
const activeTabName = useAppSelector(activeTabNameSelector);
|
||||||
|
const shouldShowToggleMenu = useMemo(() => {
|
||||||
|
if (activeTabName !== 'workflows') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return workflowsMode === 'edit';
|
||||||
|
}, [workflowsMode, activeTabName]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex w="full" gap={2}>
|
<Flex w="full" gap={2}>
|
||||||
<Flex flex={1} justifyContent="center">
|
<Flex flex={1} justifyContent="center">
|
||||||
@ -20,7 +31,7 @@ export const ViewerToolbar = memo(() => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
<Flex flex={1} justifyContent="center">
|
<Flex flex={1} justifyContent="center">
|
||||||
<Flex gap={2} marginInlineStart="auto">
|
<Flex gap={2} marginInlineStart="auto">
|
||||||
<ViewerToggleMenu />
|
{shouldShowToggleMenu && <ViewerToggleMenu />}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Box } from '@invoke-ai/ui-library';
|
import { Box } from '@invoke-ai/ui-library';
|
||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { ImageViewerWorkflows } from 'features/gallery/components/ImageViewer/ImageViewerWorkflows';
|
import { ImageComparisonDroppable } from 'features/gallery/components/ImageViewer/ImageComparisonDroppable';
|
||||||
|
import { ImageViewer } from 'features/gallery/components/ImageViewer/ImageViewer';
|
||||||
import NodeEditor from 'features/nodes/components/NodeEditor';
|
import NodeEditor from 'features/nodes/components/NodeEditor';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import { ReactFlowProvider } from 'reactflow';
|
import { ReactFlowProvider } from 'reactflow';
|
||||||
@ -10,7 +11,8 @@ const NodesTab = () => {
|
|||||||
if (mode === 'view') {
|
if (mode === 'view') {
|
||||||
return (
|
return (
|
||||||
<Box layerStyle="first" position="relative" w="full" h="full" p={2} borderRadius="base">
|
<Box layerStyle="first" position="relative" w="full" h="full" p={2} borderRadius="base">
|
||||||
<ImageViewerWorkflows />
|
<ImageViewer />
|
||||||
|
<ImageComparisonDroppable />
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Box } from '@invoke-ai/ui-library';
|
import { Box } from '@invoke-ai/ui-library';
|
||||||
import { ControlLayersEditor } from 'features/controlLayers/components/ControlLayersEditor';
|
import { ControlLayersEditor } from 'features/controlLayers/components/ControlLayersEditor';
|
||||||
|
import { ImageComparisonDroppable } from 'features/gallery/components/ImageViewer/ImageComparisonDroppable';
|
||||||
import { ImageViewer } from 'features/gallery/components/ImageViewer/ImageViewer';
|
import { ImageViewer } from 'features/gallery/components/ImageViewer/ImageViewer';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
@ -8,6 +9,7 @@ const TextToImageTab = () => {
|
|||||||
<Box layerStyle="first" position="relative" w="full" h="full" p={2} borderRadius="base">
|
<Box layerStyle="first" position="relative" w="full" h="full" p={2} borderRadius="base">
|
||||||
<ControlLayersEditor />
|
<ControlLayersEditor />
|
||||||
<ImageViewer />
|
<ImageViewer />
|
||||||
|
<ImageComparisonDroppable />
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user