mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-02-12 15:24:55 -05:00
feat(ui): clean up image view components & code
This commit is contained in:
@@ -1,35 +0,0 @@
|
||||
import { Flex, Tab, TabList, TabPanel, TabPanels, Tabs } from '@invoke-ai/ui-library';
|
||||
import { GenerateLaunchpadPanel } from 'features/controlLayers/components/SimpleSession/GenerateLaunchpadPanel';
|
||||
import { ImageViewer } from 'features/gallery/components/ImageViewer/ImageViewer2';
|
||||
import { ProgressImage } from 'features/gallery/components/ImageViewer/ProgressImage2';
|
||||
import { ViewerToolbar } from 'features/gallery/components/ImageViewer/ViewerToolbar2';
|
||||
import { memo } from 'react';
|
||||
|
||||
export const SimpleSession = memo(() => {
|
||||
return (
|
||||
<Tabs w="full" h="full" px={2}>
|
||||
<TabList>
|
||||
<Tab>Launchpad</Tab>
|
||||
<Tab>Viewer</Tab>
|
||||
<Tab>Generation Progress</Tab>
|
||||
</TabList>
|
||||
<TabPanels w="full" h="full">
|
||||
<TabPanel w="full" h="full" justifyContent="center">
|
||||
<GenerateLaunchpadPanel />
|
||||
</TabPanel>
|
||||
<TabPanel w="full" h="full">
|
||||
<Flex flexDir="column" w="full" h="full">
|
||||
<ViewerToolbar />
|
||||
<ImageViewer />
|
||||
</Flex>
|
||||
</TabPanel>
|
||||
<TabPanel w="full" h="full">
|
||||
<Flex flexDir="column" w="full" h="full" overflow="hidden" p={2}>
|
||||
<ProgressImage />
|
||||
</Flex>
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
);
|
||||
});
|
||||
SimpleSession.displayName = 'SimpleSession';
|
||||
@@ -22,25 +22,13 @@ import {
|
||||
PiRulerBold,
|
||||
} from 'react-icons/pi';
|
||||
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
|
||||
import type { ImageDTO } from 'services/api/types';
|
||||
|
||||
const CurrentImageButtons = () => {
|
||||
export const CurrentImageButtons = memo(() => {
|
||||
const lastSelectedImage = useAppSelector(selectLastSelectedImage);
|
||||
const { currentData: imageDTO } = useGetImageDTOQuery(lastSelectedImage?.image_name ?? skipToken);
|
||||
|
||||
if (!imageDTO) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <CurrentImageButtonsContent imageDTO={imageDTO} />;
|
||||
};
|
||||
|
||||
export default memo(CurrentImageButtons);
|
||||
|
||||
const CurrentImageButtonsContent = memo(({ imageDTO }: { imageDTO: ImageDTO }) => {
|
||||
const { t } = useTranslation();
|
||||
const hasTemplates = useStore($hasTemplates);
|
||||
const imageActions = useImageActions(imageDTO);
|
||||
const imageActions = useImageActions(imageDTO ?? null);
|
||||
const isStaging = useAppSelector(selectIsStaging);
|
||||
const isUpscalingEnabled = useFeatureStatus('upscaling');
|
||||
|
||||
@@ -65,7 +53,7 @@ const CurrentImageButtonsContent = memo(({ imageDTO }: { imageDTO: ImageDTO }) =
|
||||
icon={<PiFlowArrowBold />}
|
||||
tooltip={`${t('nodes.loadWorkflow')} (W)`}
|
||||
aria-label={`${t('nodes.loadWorkflow')} (W)`}
|
||||
isDisabled={!imageActions.hasWorkflow || !hasTemplates}
|
||||
isDisabled={!imageDTO || !imageActions.hasWorkflow || !hasTemplates}
|
||||
variant="link"
|
||||
alignSelf="stretch"
|
||||
onClick={imageActions.loadWorkflow}
|
||||
@@ -74,7 +62,7 @@ const CurrentImageButtonsContent = memo(({ imageDTO }: { imageDTO: ImageDTO }) =
|
||||
icon={<PiArrowsCounterClockwiseBold />}
|
||||
tooltip={`${t('parameters.remixImage')} (R)`}
|
||||
aria-label={`${t('parameters.remixImage')} (R)`}
|
||||
isDisabled={!imageActions.hasMetadata}
|
||||
isDisabled={!imageDTO || !imageActions.hasMetadata}
|
||||
variant="link"
|
||||
alignSelf="stretch"
|
||||
onClick={imageActions.remix}
|
||||
@@ -83,7 +71,7 @@ const CurrentImageButtonsContent = memo(({ imageDTO }: { imageDTO: ImageDTO }) =
|
||||
icon={<PiQuotesBold />}
|
||||
tooltip={`${t('parameters.usePrompt')} (P)`}
|
||||
aria-label={`${t('parameters.usePrompt')} (P)`}
|
||||
isDisabled={!imageActions.hasPrompts}
|
||||
isDisabled={!imageDTO || !imageActions.hasPrompts}
|
||||
variant="link"
|
||||
alignSelf="stretch"
|
||||
onClick={imageActions.recallPrompts}
|
||||
@@ -92,7 +80,7 @@ const CurrentImageButtonsContent = memo(({ imageDTO }: { imageDTO: ImageDTO }) =
|
||||
icon={<PiPlantBold />}
|
||||
tooltip={`${t('parameters.useSeed')} (S)`}
|
||||
aria-label={`${t('parameters.useSeed')} (S)`}
|
||||
isDisabled={!imageActions.hasSeed}
|
||||
isDisabled={!imageDTO || !imageActions.hasSeed}
|
||||
variant="link"
|
||||
alignSelf="stretch"
|
||||
onClick={imageActions.recallSeed}
|
||||
@@ -104,13 +92,13 @@ const CurrentImageButtonsContent = memo(({ imageDTO }: { imageDTO: ImageDTO }) =
|
||||
variant="link"
|
||||
alignSelf="stretch"
|
||||
onClick={imageActions.recallSize}
|
||||
isDisabled={isStaging}
|
||||
isDisabled={!imageDTO || isStaging}
|
||||
/>
|
||||
<IconButton
|
||||
icon={<PiAsteriskBold />}
|
||||
tooltip={`${t('parameters.useAll')} (A)`}
|
||||
aria-label={`${t('parameters.useAll')} (A)`}
|
||||
isDisabled={!imageActions.hasMetadata}
|
||||
isDisabled={!imageDTO || !imageActions.hasMetadata}
|
||||
variant="link"
|
||||
alignSelf="stretch"
|
||||
onClick={imageActions.recallAll}
|
||||
@@ -120,9 +108,9 @@ const CurrentImageButtonsContent = memo(({ imageDTO }: { imageDTO: ImageDTO }) =
|
||||
|
||||
<Divider orientation="vertical" h={8} mx={2} />
|
||||
|
||||
<DeleteImageButton onClick={imageActions.delete} />
|
||||
<DeleteImageButton onClick={imageActions.delete} isDisabled={!imageDTO} />
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
CurrentImageButtonsContent.displayName = 'CurrentImageButtonsContent';
|
||||
CurrentImageButtons.displayName = 'CurrentImageButtons';
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
import { Box, Flex } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasAlertsInvocationProgress } from 'features/controlLayers/components/CanvasAlerts/CanvasAlertsInvocationProgress';
|
||||
import { DndImage } from 'features/dnd/DndImage';
|
||||
import ImageMetadataViewer from 'features/gallery/components/ImageMetadataViewer/ImageMetadataViewer';
|
||||
import NextPrevImageButtons from 'features/gallery/components/NextPrevImageButtons';
|
||||
import { selectShouldShowImageDetails, selectShouldShowProgressInViewer } from 'features/ui/store/uiSelectors';
|
||||
import { selectShouldShowImageDetails } from 'features/ui/store/uiSelectors';
|
||||
import type { AnimationProps } from 'framer-motion';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { memo, useCallback, useRef, useState } from 'react';
|
||||
import type { ImageDTO } from 'services/api/types';
|
||||
import { $hasLastProgressImage } from 'services/events/stores';
|
||||
|
||||
import { NoContentForViewer } from './NoContentForViewer';
|
||||
import ProgressImage from './ProgressImage';
|
||||
|
||||
const CurrentImagePreview = ({ imageDTO }: { imageDTO?: ImageDTO }) => {
|
||||
export const CurrentImagePreview = memo(({ imageDTO }: { imageDTO?: ImageDTO }) => {
|
||||
const shouldShowImageDetails = useAppSelector(selectShouldShowImageDetails);
|
||||
|
||||
// Show and hide the next/prev buttons on mouse move
|
||||
@@ -79,18 +76,10 @@ const CurrentImagePreview = ({ imageDTO }: { imageDTO?: ImageDTO }) => {
|
||||
</AnimatePresence>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(CurrentImagePreview);
|
||||
});
|
||||
CurrentImagePreview.displayName = 'CurrentImagePreview';
|
||||
|
||||
const ImageContent = memo(({ imageDTO }: { imageDTO?: ImageDTO }) => {
|
||||
const hasProgressImage = useStore($hasLastProgressImage);
|
||||
const shouldShowProgressInViewer = useAppSelector(selectShouldShowProgressInViewer);
|
||||
|
||||
if (hasProgressImage && shouldShowProgressInViewer) {
|
||||
return <ProgressImage />;
|
||||
}
|
||||
|
||||
if (!imageDTO) {
|
||||
return <NoContentForViewer />;
|
||||
}
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
import { Box, Flex } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasAlertsInvocationProgress } from 'features/controlLayers/components/CanvasAlerts/CanvasAlertsInvocationProgress';
|
||||
import { DndImage } from 'features/dnd/DndImage';
|
||||
import ImageMetadataViewer from 'features/gallery/components/ImageMetadataViewer/ImageMetadataViewer';
|
||||
import NextPrevImageButtons from 'features/gallery/components/NextPrevImageButtons';
|
||||
import { selectShouldShowImageDetails } from 'features/ui/store/uiSelectors';
|
||||
import type { AnimationProps } from 'framer-motion';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { memo, useCallback, useRef, useState } from 'react';
|
||||
import type { ImageDTO } from 'services/api/types';
|
||||
|
||||
import { NoContentForViewer } from './NoContentForViewer';
|
||||
|
||||
export const CurrentImagePreview = memo(({ imageDTO }: { imageDTO?: ImageDTO }) => {
|
||||
const shouldShowImageDetails = useAppSelector(selectShouldShowImageDetails);
|
||||
|
||||
// Show and hide the next/prev buttons on mouse move
|
||||
const [shouldShowNextPrevButtons, setShouldShowNextPrevButtons] = useState<boolean>(false);
|
||||
const timeoutId = useRef(0);
|
||||
const onMouseOver = useCallback(() => {
|
||||
setShouldShowNextPrevButtons(true);
|
||||
window.clearTimeout(timeoutId.current);
|
||||
}, []);
|
||||
const onMouseOut = useCallback(() => {
|
||||
timeoutId.current = window.setTimeout(() => {
|
||||
setShouldShowNextPrevButtons(false);
|
||||
}, 500);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
onMouseOver={onMouseOver}
|
||||
onMouseOut={onMouseOut}
|
||||
width="full"
|
||||
height="full"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
position="relative"
|
||||
>
|
||||
<ImageContent imageDTO={imageDTO} />
|
||||
<Flex
|
||||
flexDir="column"
|
||||
gap={2}
|
||||
position="absolute"
|
||||
top={0}
|
||||
insetInlineStart={0}
|
||||
pointerEvents="none"
|
||||
alignItems="flex-start"
|
||||
>
|
||||
<CanvasAlertsInvocationProgress />
|
||||
</Flex>
|
||||
{shouldShowImageDetails && imageDTO && (
|
||||
<Box position="absolute" opacity={0.8} top={0} width="full" height="full" borderRadius="base">
|
||||
<ImageMetadataViewer image={imageDTO} />
|
||||
</Box>
|
||||
)}
|
||||
<AnimatePresence>
|
||||
{shouldShowNextPrevButtons && imageDTO && (
|
||||
<Box
|
||||
as={motion.div}
|
||||
key="nextPrevButtons"
|
||||
initial={initial}
|
||||
animate={animateArrows}
|
||||
exit={exit}
|
||||
position="absolute"
|
||||
top={0}
|
||||
right={0}
|
||||
bottom={0}
|
||||
left={0}
|
||||
pointerEvents="none"
|
||||
>
|
||||
<NextPrevImageButtons />
|
||||
</Box>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
CurrentImagePreview.displayName = 'CurrentImagePreview';
|
||||
|
||||
const ImageContent = memo(({ imageDTO }: { imageDTO?: ImageDTO }) => {
|
||||
if (!imageDTO) {
|
||||
return <NoContentForViewer />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex w="full" h="full" position="absolute" alignItems="center" justifyContent="center">
|
||||
<DndImage imageDTO={imageDTO} />
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
ImageContent.displayName = 'ImageContent';
|
||||
|
||||
const initial: AnimationProps['initial'] = {
|
||||
opacity: 0,
|
||||
};
|
||||
const animateArrows: AnimationProps['animate'] = {
|
||||
opacity: 1,
|
||||
transition: { duration: 0.07 },
|
||||
};
|
||||
const exit: AnimationProps['exit'] = {
|
||||
opacity: 0,
|
||||
transition: { duration: 0.07 },
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Flex } from '@invoke-ai/ui-library';
|
||||
import { ProgressImage } from 'features/gallery/components/ImageViewer/ProgressImage2';
|
||||
import { ProgressImage } from 'features/gallery/components/ImageViewer/ProgressImage';
|
||||
import { memo } from 'react';
|
||||
|
||||
export const GenerationProgressPanel = memo(() => (
|
||||
|
||||
@@ -1,12 +1,32 @@
|
||||
import { skipToken } from '@reduxjs/toolkit/query';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectImageToCompare } from 'features/gallery/components/ImageViewer/common';
|
||||
import CurrentImagePreview from 'features/gallery/components/ImageViewer/CurrentImagePreview';
|
||||
import { CurrentImagePreview } from 'features/gallery/components/ImageViewer/CurrentImagePreview';
|
||||
import { ImageComparison } from 'features/gallery/components/ImageViewer/ImageComparison';
|
||||
import { selectLastSelectedImageName } from 'features/gallery/store/gallerySelectors';
|
||||
import { memo } from 'react';
|
||||
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
|
||||
|
||||
// type Props = {
|
||||
// closeButton?: ReactNode;
|
||||
// };
|
||||
|
||||
// const useFocusRegionOptions = {
|
||||
// focusOnMount: true,
|
||||
// };
|
||||
|
||||
// const FOCUS_REGION_STYLES: SystemStyleObject = {
|
||||
// display: 'flex',
|
||||
// width: 'full',
|
||||
// height: 'full',
|
||||
// position: 'absolute',
|
||||
// flexDirection: 'column',
|
||||
// inset: 0,
|
||||
// alignItems: 'center',
|
||||
// justifyContent: 'center',
|
||||
// overflow: 'hidden',
|
||||
// };
|
||||
|
||||
export const ImageViewer = memo(() => {
|
||||
const lastSelectedImageName = useAppSelector(selectLastSelectedImageName);
|
||||
const { data: lastSelectedImageDTO } = useGetImageDTOQuery(lastSelectedImageName ?? skipToken);
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
import { skipToken } from '@reduxjs/toolkit/query';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectImageToCompare } from 'features/gallery/components/ImageViewer/common';
|
||||
import { CurrentImagePreview } from 'features/gallery/components/ImageViewer/CurrentImagePreview2';
|
||||
import { ImageComparison } from 'features/gallery/components/ImageViewer/ImageComparison';
|
||||
import { selectLastSelectedImageName } from 'features/gallery/store/gallerySelectors';
|
||||
import { memo } from 'react';
|
||||
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
|
||||
|
||||
// type Props = {
|
||||
// closeButton?: ReactNode;
|
||||
// };
|
||||
|
||||
// const useFocusRegionOptions = {
|
||||
// focusOnMount: true,
|
||||
// };
|
||||
|
||||
// const FOCUS_REGION_STYLES: SystemStyleObject = {
|
||||
// display: 'flex',
|
||||
// width: 'full',
|
||||
// height: 'full',
|
||||
// position: 'absolute',
|
||||
// flexDirection: 'column',
|
||||
// inset: 0,
|
||||
// alignItems: 'center',
|
||||
// justifyContent: 'center',
|
||||
// overflow: 'hidden',
|
||||
// };
|
||||
|
||||
export const ImageViewer = memo(() => {
|
||||
const lastSelectedImageName = useAppSelector(selectLastSelectedImageName);
|
||||
const { data: lastSelectedImageDTO } = useGetImageDTOQuery(lastSelectedImageName ?? skipToken);
|
||||
const comparisonImageDTO = useAppSelector(selectImageToCompare);
|
||||
|
||||
if (lastSelectedImageDTO && comparisonImageDTO) {
|
||||
return <ImageComparison firstImage={lastSelectedImageDTO} secondImage={comparisonImageDTO} />;
|
||||
}
|
||||
|
||||
return <CurrentImagePreview imageDTO={lastSelectedImageDTO} />;
|
||||
});
|
||||
|
||||
ImageViewer.displayName = 'ImageViewer';
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Divider, Flex } from '@invoke-ai/ui-library';
|
||||
import { ImageViewer } from 'features/gallery/components/ImageViewer/ImageViewer2';
|
||||
import { ViewerToolbar } from 'features/gallery/components/ImageViewer/ViewerToolbar2';
|
||||
import { ImageViewer } from 'features/gallery/components/ImageViewer/ImageViewer';
|
||||
import { ViewerToolbar } from 'features/gallery/components/ImageViewer/ViewerToolbar';
|
||||
import { memo } from 'react';
|
||||
|
||||
export const ImageViewerPanel = memo(() => (
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import type { SystemStyleObject } from '@invoke-ai/ui-library';
|
||||
import { Image } from '@invoke-ai/ui-library';
|
||||
import { Flex, Image } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
||||
import { selectSystemSlice } from 'features/system/store/systemSlice';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { PiPulseBold } from 'react-icons/pi';
|
||||
import { $lastProgressImage } from 'services/events/stores';
|
||||
|
||||
const selectShouldAntialiasProgressImage = createSelector(
|
||||
@@ -12,7 +14,7 @@ const selectShouldAntialiasProgressImage = createSelector(
|
||||
(system) => system.shouldAntialiasProgressImage
|
||||
);
|
||||
|
||||
const CurrentImagePreview = () => {
|
||||
export const ProgressImage = memo(() => {
|
||||
const progressImage = useStore($lastProgressImage);
|
||||
const shouldAntialiasProgressImage = useAppSelector(selectShouldAntialiasProgressImage);
|
||||
|
||||
@@ -24,24 +26,31 @@ const CurrentImagePreview = () => {
|
||||
);
|
||||
|
||||
if (!progressImage) {
|
||||
return null;
|
||||
return (
|
||||
<Flex width="full" height="full" alignItems="center" justifyContent="center">
|
||||
<IAINoContentFallback icon={PiPulseBold} label="No Generation in Progress" />
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Image
|
||||
src={progressImage.dataURL}
|
||||
width={progressImage.width}
|
||||
height={progressImage.height}
|
||||
draggable={false}
|
||||
data-testid="progress-image"
|
||||
objectFit="contain"
|
||||
maxWidth="full"
|
||||
maxHeight="full"
|
||||
position="absolute"
|
||||
borderRadius="base"
|
||||
sx={sx}
|
||||
/>
|
||||
<Flex width="full" height="full" alignItems="center" justifyContent="center" minW={0} minH={0}>
|
||||
<Image
|
||||
src={progressImage.dataURL}
|
||||
width={progressImage.width}
|
||||
height={progressImage.height}
|
||||
draggable={false}
|
||||
data-testid="progress-image"
|
||||
objectFit="contain"
|
||||
maxWidth="full"
|
||||
maxHeight="full"
|
||||
borderRadius="base"
|
||||
sx={sx}
|
||||
minH={0}
|
||||
minW={0}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default memo(CurrentImagePreview);
|
||||
ProgressImage.displayName = 'ProgressImage';
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
import type { SystemStyleObject } from '@invoke-ai/ui-library';
|
||||
import { Flex, Image } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
||||
import { selectSystemSlice } from 'features/system/store/systemSlice';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { PiPulseBold } from 'react-icons/pi';
|
||||
import { $lastProgressImage } from 'services/events/stores';
|
||||
|
||||
const selectShouldAntialiasProgressImage = createSelector(
|
||||
selectSystemSlice,
|
||||
(system) => system.shouldAntialiasProgressImage
|
||||
);
|
||||
|
||||
export const ProgressImage = memo(() => {
|
||||
const progressImage = useStore($lastProgressImage);
|
||||
const shouldAntialiasProgressImage = useAppSelector(selectShouldAntialiasProgressImage);
|
||||
|
||||
const sx = useMemo<SystemStyleObject>(
|
||||
() => ({
|
||||
imageRendering: shouldAntialiasProgressImage ? 'auto' : 'pixelated',
|
||||
}),
|
||||
[shouldAntialiasProgressImage]
|
||||
);
|
||||
|
||||
if (!progressImage) {
|
||||
return (
|
||||
<Flex width="full" height="full" alignItems="center" justifyContent="center">
|
||||
<IAINoContentFallback icon={PiPulseBold} label="No Generation in Progress" />
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex width="full" height="full" alignItems="center" justifyContent="center" minW={0} minH={0}>
|
||||
<Image
|
||||
src={progressImage.dataURL}
|
||||
width={progressImage.width}
|
||||
height={progressImage.height}
|
||||
draggable={false}
|
||||
data-testid="progress-image"
|
||||
objectFit="contain"
|
||||
maxWidth="full"
|
||||
maxHeight="full"
|
||||
borderRadius="base"
|
||||
sx={sx}
|
||||
minH={0}
|
||||
minW={0}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
|
||||
ProgressImage.displayName = 'ProgressImage';
|
||||
@@ -14,16 +14,14 @@ export const ToggleMetadataViewerButton = memo(() => {
|
||||
const imageDTO = useAppSelector(selectLastSelectedImage);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const toggleMetadataViewer = useCallback(
|
||||
() => dispatch(setShouldShowImageDetails(!shouldShowImageDetails)),
|
||||
[dispatch, shouldShowImageDetails]
|
||||
);
|
||||
const toggleMetadataViewer = useCallback(() => {
|
||||
dispatch(setShouldShowImageDetails(!shouldShowImageDetails));
|
||||
}, [dispatch, shouldShowImageDetails]);
|
||||
|
||||
useRegisteredHotkeys({
|
||||
id: 'toggleMetadata',
|
||||
category: 'viewer',
|
||||
callback: toggleMetadataViewer,
|
||||
options: { enabled: Boolean(imageDTO) },
|
||||
dependencies: [imageDTO, shouldShowImageDetails],
|
||||
});
|
||||
|
||||
@@ -33,7 +31,6 @@ export const ToggleMetadataViewerButton = memo(() => {
|
||||
tooltip={`${t('parameters.info')} (I)`}
|
||||
aria-label={`${t('parameters.info')} (I)`}
|
||||
onClick={toggleMetadataViewer}
|
||||
isDisabled={!imageDTO}
|
||||
variant="link"
|
||||
alignSelf="stretch"
|
||||
colorScheme={shouldShowImageDetails ? 'invokeBlue' : 'base'}
|
||||
|
||||
@@ -1,32 +1,16 @@
|
||||
import { Flex } from '@invoke-ai/ui-library';
|
||||
import { ButtonGroup, Flex } from '@invoke-ai/ui-library';
|
||||
import { ToggleMetadataViewerButton } from 'features/gallery/components/ImageViewer/ToggleMetadataViewerButton';
|
||||
import { ToggleProgressButton } from 'features/gallery/components/ImageViewer/ToggleProgressButton';
|
||||
import type { ReactNode } from 'react';
|
||||
import { memo } from 'react';
|
||||
|
||||
import CurrentImageButtons from './CurrentImageButtons';
|
||||
import { CurrentImageButtons } from './CurrentImageButtons';
|
||||
|
||||
type Props = {
|
||||
closeButton?: ReactNode;
|
||||
};
|
||||
|
||||
export const ViewerToolbar = memo(({ closeButton }: Props) => {
|
||||
export const ViewerToolbar = memo(() => {
|
||||
return (
|
||||
<Flex w="full" px={2} gap={2} bg="base.750" borderTopRadius="base" h={12}>
|
||||
<Flex flex={1} justifyContent="center">
|
||||
<Flex marginInlineEnd="auto" alignItems="center">
|
||||
<ToggleProgressButton />
|
||||
<ToggleMetadataViewerButton />
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex flex={1} justifyContent="center" alignItems="center">
|
||||
<Flex w="full" justifyContent="center" h={8}>
|
||||
<ButtonGroup>
|
||||
<ToggleMetadataViewerButton />
|
||||
<CurrentImageButtons />
|
||||
</Flex>
|
||||
<Flex flex={1} justifyContent="center">
|
||||
<Flex marginInlineStart="auto" alignItems="center">
|
||||
{closeButton}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</ButtonGroup>
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import { ButtonGroup, Flex } from '@invoke-ai/ui-library';
|
||||
import { ToggleMetadataViewerButton } from 'features/gallery/components/ImageViewer/ToggleMetadataViewerButton';
|
||||
import { memo } from 'react';
|
||||
|
||||
import CurrentImageButtons from './CurrentImageButtons';
|
||||
|
||||
export const ViewerToolbar = memo(() => {
|
||||
return (
|
||||
<Flex w="full" justifyContent="center" h="24px">
|
||||
<ButtonGroup>
|
||||
<ToggleMetadataViewerButton />
|
||||
<CurrentImageButtons />
|
||||
</ButtonGroup>
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
|
||||
ViewerToolbar.displayName = 'ViewerToolbar';
|
||||
@@ -24,12 +24,12 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useDebouncedMetadata } from 'services/api/hooks/useDebouncedMetadata';
|
||||
import type { ImageDTO } from 'services/api/types';
|
||||
|
||||
export const useImageActions = (imageDTO: ImageDTO) => {
|
||||
export const useImageActions = (imageDTO: ImageDTO | null) => {
|
||||
const { dispatch, getState } = useAppStore();
|
||||
const { t } = useTranslation();
|
||||
const activeStylePresetId = useAppSelector(selectStylePresetActivePresetId);
|
||||
const isStaging = useAppSelector(selectIsStaging);
|
||||
const { metadata } = useDebouncedMetadata(imageDTO.image_name);
|
||||
const { metadata } = useDebouncedMetadata(imageDTO?.image_name ?? null);
|
||||
const [hasMetadata, setHasMetadata] = useState(false);
|
||||
const [hasSeed, setHasSeed] = useState(false);
|
||||
const [hasPrompts, setHasPrompts] = useState(false);
|
||||
@@ -79,15 +79,21 @@ export const useImageActions = (imageDTO: ImageDTO) => {
|
||||
}, [dispatch, activeStylePresetId, t]);
|
||||
|
||||
const recallAll = useCallback(() => {
|
||||
if (!imageDTO) {
|
||||
return;
|
||||
}
|
||||
if (!metadata) {
|
||||
return;
|
||||
}
|
||||
const activeTabName = selectActiveTab(getState());
|
||||
parseAndRecallAllMetadata(metadata, activeTabName === 'canvas', isStaging ? ['width', 'height'] : []);
|
||||
clearStylePreset();
|
||||
}, [metadata, getState, isStaging, clearStylePreset]);
|
||||
}, [imageDTO, metadata, getState, isStaging, clearStylePreset]);
|
||||
|
||||
const remix = useCallback(() => {
|
||||
if (!imageDTO) {
|
||||
return;
|
||||
}
|
||||
if (!metadata) {
|
||||
return;
|
||||
}
|
||||
@@ -95,9 +101,12 @@ export const useImageActions = (imageDTO: ImageDTO) => {
|
||||
// Recalls all metadata parameters except seed
|
||||
parseAndRecallAllMetadata(metadata, activeTabName === 'canvas', ['seed']);
|
||||
clearStylePreset();
|
||||
}, [metadata, getState, clearStylePreset]);
|
||||
}, [imageDTO, metadata, getState, clearStylePreset]);
|
||||
|
||||
const recallSeed = useCallback(() => {
|
||||
if (!imageDTO) {
|
||||
return;
|
||||
}
|
||||
if (!metadata) {
|
||||
return;
|
||||
}
|
||||
@@ -109,17 +118,23 @@ export const useImageActions = (imageDTO: ImageDTO) => {
|
||||
.catch(() => {
|
||||
// no-op, the toast will show the error
|
||||
});
|
||||
}, [metadata]);
|
||||
}, [imageDTO, metadata]);
|
||||
|
||||
const recallPrompts = useCallback(() => {
|
||||
if (!imageDTO) {
|
||||
return;
|
||||
}
|
||||
if (!metadata) {
|
||||
return;
|
||||
}
|
||||
parseAndRecallPrompts(metadata);
|
||||
clearStylePreset();
|
||||
}, [metadata, clearStylePreset]);
|
||||
}, [imageDTO, metadata, clearStylePreset]);
|
||||
|
||||
const createAsPreset = useCallback(async () => {
|
||||
if (!imageDTO) {
|
||||
return;
|
||||
}
|
||||
if (!metadata) {
|
||||
return;
|
||||
}
|
||||
@@ -153,14 +168,20 @@ export const useImageActions = (imageDTO: ImageDTO) => {
|
||||
const loadWorkflowWithDialog = useLoadWorkflowWithDialog();
|
||||
|
||||
const loadWorkflowFromImage = useCallback(() => {
|
||||
if (!imageDTO) {
|
||||
return;
|
||||
}
|
||||
if (!imageDTO.has_workflow || !hasTemplates) {
|
||||
return;
|
||||
}
|
||||
|
||||
loadWorkflowWithDialog({ type: 'image', data: imageDTO.image_name });
|
||||
}, [hasTemplates, imageDTO.has_workflow, imageDTO.image_name, loadWorkflowWithDialog]);
|
||||
}, [hasTemplates, imageDTO, loadWorkflowWithDialog]);
|
||||
|
||||
const recallSize = useCallback(() => {
|
||||
if (!imageDTO) {
|
||||
return;
|
||||
}
|
||||
if (isStaging) {
|
||||
return;
|
||||
}
|
||||
@@ -168,10 +189,16 @@ export const useImageActions = (imageDTO: ImageDTO) => {
|
||||
}, [imageDTO, isStaging]);
|
||||
|
||||
const upscale = useCallback(() => {
|
||||
if (!imageDTO) {
|
||||
return;
|
||||
}
|
||||
dispatch(adHocPostProcessingRequested({ imageDTO }));
|
||||
}, [dispatch, imageDTO]);
|
||||
|
||||
const _delete = useCallback(() => {
|
||||
if (!imageDTO) {
|
||||
return;
|
||||
}
|
||||
deleteImageModal.delete([imageDTO]);
|
||||
}, [deleteImageModal, imageDTO]);
|
||||
|
||||
@@ -185,7 +212,7 @@ export const useImageActions = (imageDTO: ImageDTO) => {
|
||||
recallPrompts,
|
||||
createAsPreset,
|
||||
loadWorkflow: loadWorkflowFromImage,
|
||||
hasWorkflow: imageDTO.has_workflow,
|
||||
hasWorkflow: imageDTO?.has_workflow ?? false,
|
||||
recallSize,
|
||||
upscale,
|
||||
delete: _delete,
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { AdvancedSession } from 'features/controlLayers/components/AdvancedSession/AdvancedSession';
|
||||
import { SimpleSession } from 'features/controlLayers/components/SimpleSession/SimpleSession';
|
||||
import { selectCanvasSessionId } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { ImageViewer } from 'features/gallery/components/ImageViewer/ImageViewer';
|
||||
import ModelManagerTab from 'features/ui/components/tabs/ModelManagerTab';
|
||||
import QueueTab from 'features/ui/components/tabs/QueueTab';
|
||||
import { WorkflowsMainPanel } from 'features/ui/components/tabs/WorkflowsTabContent';
|
||||
import { selectActiveTab } from 'features/ui/store/uiSelectors';
|
||||
import { memo } from 'react';
|
||||
import type { Equals } from 'tsafe';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
export const MainPanelContent = memo(() => {
|
||||
const tab = useAppSelector(selectActiveTab);
|
||||
const canvasId = useAppSelector(selectCanvasSessionId);
|
||||
|
||||
if (tab === 'generate') {
|
||||
return <SimpleSession />;
|
||||
}
|
||||
if (tab === 'canvas') {
|
||||
return <AdvancedSession id={canvasId} />;
|
||||
}
|
||||
if (tab === 'upscaling') {
|
||||
return <ImageViewer />;
|
||||
}
|
||||
if (tab === 'workflows') {
|
||||
return <WorkflowsMainPanel />;
|
||||
}
|
||||
if (tab === 'models') {
|
||||
return <ModelManagerTab />;
|
||||
}
|
||||
if (tab === 'queue') {
|
||||
return <QueueTab />;
|
||||
}
|
||||
|
||||
assert<Equals<never, typeof tab>>(false);
|
||||
});
|
||||
MainPanelContent.displayName = 'MainPanelContent';
|
||||
Reference in New Issue
Block a user