feat(ui): clean up image view components & code

This commit is contained in:
psychedelicious
2025-06-20 17:01:19 +10:00
parent 3264188ffd
commit a30933b09c
15 changed files with 110 additions and 391 deletions

View File

@@ -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';

View File

@@ -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';

View File

@@ -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 />;
}

View File

@@ -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 },
};

View File

@@ -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(() => (

View File

@@ -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);

View File

@@ -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';

View File

@@ -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(() => (

View File

@@ -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';

View File

@@ -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';

View File

@@ -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'}

View File

@@ -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>
);
});

View File

@@ -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';

View File

@@ -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,

View File

@@ -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';