Skip to content

Commit 52aa0c9

Browse files
ui: miscellaneous fixes (#3386)
2 parents 4333852 + 7f5f468 commit 52aa0c9

File tree

9 files changed

+134
-115
lines changed

9 files changed

+134
-115
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { Badge, Flex } from '@chakra-ui/react';
2+
import { Image } from 'app/types/invokeai';
3+
import { isNumber, isString } from 'lodash-es';
4+
import { useMemo } from 'react';
5+
6+
type ImageMetadataOverlayProps = {
7+
image: Image;
8+
};
9+
10+
const ImageMetadataOverlay = ({ image }: ImageMetadataOverlayProps) => {
11+
const dimensions = useMemo(() => {
12+
if (!isNumber(image.metadata?.width) || isNumber(!image.metadata?.height)) {
13+
return;
14+
}
15+
16+
return `${image.metadata?.width} × ${image.metadata?.height}`;
17+
}, [image.metadata]);
18+
19+
const model = useMemo(() => {
20+
if (!isString(image.metadata?.invokeai?.node?.model)) {
21+
return;
22+
}
23+
24+
return image.metadata?.invokeai?.node?.model;
25+
}, [image.metadata]);
26+
27+
return (
28+
<Flex
29+
sx={{
30+
pointerEvents: 'none',
31+
flexDirection: 'column',
32+
position: 'absolute',
33+
top: 0,
34+
right: 0,
35+
p: 2,
36+
alignItems: 'flex-end',
37+
gap: 2,
38+
}}
39+
>
40+
{dimensions && (
41+
<Badge variant="solid" colorScheme="base">
42+
{dimensions}
43+
</Badge>
44+
)}
45+
{model && (
46+
<Badge variant="solid" colorScheme="base">
47+
{model}
48+
</Badge>
49+
)}
50+
</Flex>
51+
);
52+
};
53+
54+
export default ImageMetadataOverlay;

invokeai/frontend/web/src/common/components/ImageToImageOverlay.tsx

Lines changed: 0 additions & 37 deletions
This file was deleted.

invokeai/frontend/web/src/features/gallery/components/CurrentImagePreview.tsx

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Box, Flex, Image, Skeleton, useBoolean } from '@chakra-ui/react';
1+
import { Box, Flex, Image } from '@chakra-ui/react';
22
import { createSelector } from '@reduxjs/toolkit';
33
import { useAppSelector } from 'app/store/storeHooks';
44
import { useGetUrl } from 'common/util/getUrl';
@@ -11,7 +11,8 @@ import NextPrevImageButtons from './NextPrevImageButtons';
1111
import CurrentImageHidden from './CurrentImageHidden';
1212
import { DragEvent, memo, useCallback } from 'react';
1313
import { systemSelector } from 'features/system/store/systemSelectors';
14-
import CurrentImageFallback from './CurrentImageFallback';
14+
import ImageFallbackSpinner from './ImageFallbackSpinner';
15+
import ImageMetadataOverlay from 'common/components/ImageMetadataOverlay';
1516

1617
export const imagesSelector = createSelector(
1718
[uiSelector, gallerySelector, systemSelector],
@@ -50,8 +51,6 @@ const CurrentImagePreview = () => {
5051
} = useAppSelector(imagesSelector);
5152
const { getUrl } = useGetUrl();
5253

53-
const [isLoaded, { on, off }] = useBoolean();
54-
5554
const handleDragStart = useCallback(
5655
(e: DragEvent<HTMLDivElement>) => {
5756
if (!image) {
@@ -67,11 +66,11 @@ const CurrentImagePreview = () => {
6766
return (
6867
<Flex
6968
sx={{
70-
position: 'relative',
71-
justifyContent: 'center',
72-
alignItems: 'center',
7369
width: '100%',
7470
height: '100%',
71+
position: 'relative',
72+
alignItems: 'center',
73+
justifyContent: 'center',
7574
}}
7675
>
7776
{progressImage && shouldShowProgressInViewer ? (
@@ -91,28 +90,23 @@ const CurrentImagePreview = () => {
9190
/>
9291
) : (
9392
image && (
94-
<Image
95-
onDragStart={handleDragStart}
96-
fallbackStrategy="beforeLoadOrError"
97-
src={shouldHidePreview ? undefined : getUrl(image.url)}
98-
width={image.metadata.width || 'auto'}
99-
height={image.metadata.height || 'auto'}
100-
fallback={
101-
shouldHidePreview ? (
102-
<CurrentImageHidden />
103-
) : (
104-
<CurrentImageFallback />
105-
)
106-
}
107-
sx={{
108-
objectFit: 'contain',
109-
maxWidth: '100%',
110-
maxHeight: '100%',
111-
height: 'auto',
112-
position: 'absolute',
113-
borderRadius: 'base',
114-
}}
115-
/>
93+
<>
94+
<Image
95+
src={getUrl(image.url)}
96+
fallbackStrategy="beforeLoadOrError"
97+
fallback={<ImageFallbackSpinner />}
98+
onDragStart={handleDragStart}
99+
sx={{
100+
objectFit: 'contain',
101+
maxWidth: '100%',
102+
maxHeight: '100%',
103+
height: 'auto',
104+
position: 'absolute',
105+
borderRadius: 'base',
106+
}}
107+
/>
108+
<ImageMetadataOverlay image={image} />
109+
</>
116110
)
117111
)}
118112
{shouldShowImageDetails && image && 'metadata' in image && (

invokeai/frontend/web/src/features/gallery/components/GalleryProgressImage.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Box, Flex, Image } from '@chakra-ui/react';
1+
import { Flex, Image, Spinner } from '@chakra-ui/react';
22
import { createSelector } from '@reduxjs/toolkit';
33
import { useAppSelector } from 'app/store/storeHooks';
44
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
@@ -42,6 +42,7 @@ const GalleryProgressImage = () => {
4242
alignItems: 'center',
4343
justifyContent: 'center',
4444
aspectRatio: '1/1',
45+
position: 'relative',
4546
}}
4647
>
4748
<Image
@@ -61,6 +62,7 @@ const GalleryProgressImage = () => {
6162
imageRendering: shouldAntialiasProgressImage ? 'auto' : 'pixelated',
6263
}}
6364
/>
65+
<Spinner sx={{ position: 'absolute', top: 1, right: 1, opacity: 0.7 }} />
6466
</Flex>
6567
);
6668
};

invokeai/frontend/web/src/features/gallery/components/HoverableImage.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ const HoverableImage = memo((props: HoverableImageProps) => {
278278
h: 'full',
279279
transition: 'transform 0.2s ease-out',
280280
aspectRatio: '1/1',
281+
cursor: 'pointer',
281282
}}
282283
>
283284
<Image

invokeai/frontend/web/src/features/gallery/components/CurrentImageFallback.tsx renamed to invokeai/frontend/web/src/features/gallery/components/ImageFallbackSpinner.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { Flex, Spinner, SpinnerProps } from '@chakra-ui/react';
22

3-
type CurrentImageFallbackProps = SpinnerProps;
3+
type ImageFallbackSpinnerProps = SpinnerProps;
44

5-
const CurrentImageFallback = (props: CurrentImageFallbackProps) => {
5+
const ImageFallbackSpinner = (props: ImageFallbackSpinnerProps) => {
66
const { size = 'xl', ...rest } = props;
77

88
return (
@@ -21,4 +21,4 @@ const CurrentImageFallback = (props: CurrentImageFallbackProps) => {
2121
);
2222
};
2323

24-
export default CurrentImageFallback;
24+
export default ImageFallbackSpinner;

invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { PayloadAction } from '@reduxjs/toolkit';
22
import { createSlice } from '@reduxjs/toolkit';
33
import { Image } from 'app/types/invokeai';
4+
import { imageReceived, thumbnailReceived } from 'services/thunks/image';
45

56
type GalleryImageObjectFitType = 'contain' | 'cover';
67

@@ -63,6 +64,29 @@ export const gallerySlice = createSlice({
6364
state.shouldUseSingleGalleryColumn = action.payload;
6465
},
6566
},
67+
extraReducers(builder) {
68+
builder.addCase(imageReceived.fulfilled, (state, action) => {
69+
// When we get an updated URL for an image, we need to update the selectedImage in gallery,
70+
// which is currently its own object (instead of a reference to an image in results/uploads)
71+
const { imagePath } = action.payload;
72+
const { imageName } = action.meta.arg;
73+
74+
if (state.selectedImage?.name === imageName) {
75+
state.selectedImage.url = imagePath;
76+
}
77+
});
78+
79+
builder.addCase(thumbnailReceived.fulfilled, (state, action) => {
80+
// When we get an updated URL for an image, we need to update the selectedImage in gallery,
81+
// which is currently its own object (instead of a reference to an image in results/uploads)
82+
const { thumbnailPath } = action.payload;
83+
const { thumbnailName } = action.meta.arg;
84+
85+
if (state.selectedImage?.name === thumbnailName) {
86+
state.selectedImage.thumbnail = thumbnailPath;
87+
}
88+
});
89+
},
6690
});
6791

6892
export const {

invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/InitialImagePreview.tsx

Lines changed: 25 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
1-
import { Flex, Image, Spinner } from '@chakra-ui/react';
1+
import { Flex, Image } from '@chakra-ui/react';
22
import { createSelector } from '@reduxjs/toolkit';
33
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
44
import SelectImagePlaceholder from 'common/components/SelectImagePlaceholder';
55
import { useGetUrl } from 'common/util/getUrl';
66
import { clearInitialImage } from 'features/parameters/store/generationSlice';
77
import { addToast } from 'features/system/store/systemSlice';
8-
import { DragEvent, useCallback, useState } from 'react';
8+
import { DragEvent, useCallback } from 'react';
99
import { useTranslation } from 'react-i18next';
1010
import { ImageType } from 'services/api';
11-
import ImageToImageOverlay from 'common/components/ImageToImageOverlay';
11+
import ImageMetadataOverlay from 'common/components/ImageMetadataOverlay';
1212
import { generationSelector } from 'features/parameters/store/generationSelectors';
1313
import { initialImageSelected } from 'features/parameters/store/actions';
1414
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
15+
import ImageFallbackSpinner from 'features/gallery/components/ImageFallbackSpinner';
1516

1617
const selector = createSelector(
1718
[generationSelector],
@@ -30,8 +31,6 @@ const InitialImagePreview = () => {
3031
const dispatch = useAppDispatch();
3132
const { t } = useTranslation();
3233

33-
const [isLoaded, setIsLoaded] = useState(false);
34-
3534
const onError = () => {
3635
dispatch(
3736
addToast({
@@ -42,13 +41,10 @@ const InitialImagePreview = () => {
4241
})
4342
);
4443
dispatch(clearInitialImage());
45-
setIsLoaded(false);
4644
};
4745

4846
const handleDrop = useCallback(
4947
(e: DragEvent<HTMLDivElement>) => {
50-
setIsLoaded(false);
51-
5248
const name = e.dataTransfer.getData('invokeai/imageName');
5349
const type = e.dataTransfer.getData('invokeai/imageType') as ImageType;
5450

@@ -62,48 +58,32 @@ const InitialImagePreview = () => {
6258
sx={{
6359
width: 'full',
6460
height: 'full',
61+
position: 'relative',
6562
alignItems: 'center',
6663
justifyContent: 'center',
67-
position: 'relative',
6864
}}
6965
onDrop={handleDrop}
7066
>
71-
<Flex
72-
sx={{
73-
height: 'full',
74-
width: 'full',
75-
blur: '5px',
76-
position: 'relative',
77-
alignItems: 'center',
78-
justifyContent: 'center',
79-
}}
80-
>
81-
{initialImage?.url && (
82-
<>
83-
<Image
84-
sx={{
85-
objectFit: 'contain',
86-
borderRadius: 'base',
87-
maxHeight: 'full',
88-
}}
89-
src={getUrl(initialImage?.url)}
90-
onError={onError}
91-
onLoad={() => {
92-
setIsLoaded(true);
93-
}}
94-
fallback={
95-
<Flex
96-
sx={{ h: 36, alignItems: 'center', justifyContent: 'center' }}
97-
>
98-
<Spinner color="grey" w="5rem" h="5rem" />
99-
</Flex>
100-
}
101-
/>
102-
{isLoaded && <ImageToImageOverlay image={initialImage} />}
103-
</>
104-
)}
105-
{!initialImage?.url && <SelectImagePlaceholder />}
106-
</Flex>
67+
{initialImage?.url && (
68+
<>
69+
<Image
70+
src={getUrl(initialImage?.url)}
71+
fallbackStrategy="beforeLoadOrError"
72+
fallback={<ImageFallbackSpinner />}
73+
onError={onError}
74+
sx={{
75+
objectFit: 'contain',
76+
maxWidth: '100%',
77+
maxHeight: '100%',
78+
height: 'auto',
79+
position: 'absolute',
80+
borderRadius: 'base',
81+
}}
82+
/>
83+
<ImageMetadataOverlay image={initialImage} />
84+
</>
85+
)}
86+
{!initialImage?.url && <SelectImagePlaceholder />}
10787
</Flex>
10888
);
10989
};

invokeai/frontend/web/src/features/system/store/systemSlice.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,7 @@ export const systemSlice = createSlice({
418418
state.currentStep = 0;
419419
state.totalSteps = 0;
420420
state.statusTranslationKey = 'common.statusConnected';
421+
state.progressImage = null;
421422

422423
state.toastQueue.push(
423424
makeToast({ title: t('toast.canceled'), status: 'warning' })

0 commit comments

Comments
 (0)