Skip to content

Commit 0843d60

Browse files
feat(ui): add list of warnings in tooltip on ref image
1 parent 95bd9ce commit 0843d60

File tree

3 files changed

+112
-71
lines changed

3 files changed

+112
-71
lines changed

invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageHeader.tsx

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,17 @@ import { createSelector } from '@reduxjs/toolkit';
44
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
55
import { useRefImageEntity } from 'features/controlLayers/components/RefImage/useRefImageEntity';
66
import { useRefImageIdContext } from 'features/controlLayers/contexts/RefImageIdContext';
7+
import { selectMainModelConfig } from 'features/controlLayers/store/paramsSlice';
78
import {
89
refImageDeleted,
910
refImageIsEnabledToggled,
1011
selectRefImageEntityIds,
1112
} from 'features/controlLayers/store/refImagesSlice';
13+
import { getGlobalReferenceImageWarnings } from 'features/controlLayers/store/validators';
1214
import { memo, useCallback, useMemo } from 'react';
13-
import { PiCircleBold, PiCircleFill, PiTrashBold } from 'react-icons/pi';
15+
import { PiCircleBold, PiCircleFill, PiTrashBold, PiWarningBold } from 'react-icons/pi';
16+
17+
import { RefImageWarningTooltipContent } from './RefImageWarningTooltipContent';
1418

1519
const textSx: SystemStyleObject = {
1620
color: 'base.300',
@@ -28,6 +32,12 @@ export const RefImageHeader = memo(() => {
2832
);
2933
const refImageNumber = useAppSelector(selectRefImageNumber);
3034
const entity = useRefImageEntity(id);
35+
const mainModelConfig = useAppSelector(selectMainModelConfig);
36+
37+
const warnings = useMemo(() => {
38+
return getGlobalReferenceImageWarnings(entity, mainModelConfig);
39+
}, [entity, mainModelConfig]);
40+
3141
const deleteRefImage = useCallback(() => {
3242
dispatch(refImageDeleted({ id }));
3343
}, [dispatch, id]);
@@ -42,6 +52,18 @@ export const RefImageHeader = memo(() => {
4252
Reference Image #{refImageNumber}
4353
</Text>
4454
<Flex alignItems="center" gap={1}>
55+
{warnings.length > 0 && (
56+
<IconButton
57+
as="span"
58+
size="sm"
59+
variant="link"
60+
alignSelf="stretch"
61+
aria-label="warnings"
62+
tooltip={<RefImageWarningTooltipContent warnings={warnings} />}
63+
icon={<PiWarningBold />}
64+
colorScheme="warning"
65+
/>
66+
)}
4567
{!entity.isEnabled && (
4668
<Text fontSize="xs" fontStyle="italic" color="base.400">
4769
Disabled

invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImagePreview.tsx

Lines changed: 71 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { SystemStyleObject } from '@invoke-ai/ui-library';
2-
import { Flex, Icon, IconButton, Image, Skeleton, Text } from '@invoke-ai/ui-library';
2+
import { Flex, Icon, IconButton, Image, Skeleton, Text, Tooltip } from '@invoke-ai/ui-library';
33
import { skipToken } from '@reduxjs/toolkit/query';
44
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
55
import { round } from 'es-toolkit/compat';
@@ -17,6 +17,8 @@ import { memo, useCallback, useEffect, useMemo, useState } from 'react';
1717
import { PiExclamationMarkBold, PiEyeSlashBold, PiImageBold } from 'react-icons/pi';
1818
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
1919

20+
import { RefImageWarningTooltipContent } from './RefImageWarningTooltipContent';
21+
2022
const baseSx: SystemStyleObject = {
2123
'&[data-is-open="true"]': {
2224
borderColor: 'invokeBlue.300',
@@ -51,9 +53,6 @@ const getImageSxWithWeight = (weight: number): SystemStyleObject => {
5153

5254
return {
5355
...baseSx,
54-
'&[data-is-disabled="true"]': {
55-
opacity: 0.4,
56-
},
5756
_after: {
5857
content: '""',
5958
position: 'absolute',
@@ -95,8 +94,8 @@ export const RefImagePreview = memo(() => {
9594
};
9695
}, [entity.config]);
9796

98-
const isInvalid = useMemo(() => {
99-
return getGlobalReferenceImageWarnings(entity, mainModelConfig).length > 0;
97+
const warnings = useMemo(() => {
98+
return getGlobalReferenceImageWarnings(entity, mainModelConfig);
10099
}, [entity, mainModelConfig]);
101100

102101
const onClick = useCallback(() => {
@@ -126,74 +125,76 @@ export const RefImagePreview = memo(() => {
126125
);
127126
}
128127
return (
129-
<Flex
130-
position="relative"
131-
borderWidth={1}
132-
borderStyle="solid"
133-
borderRadius="base"
134-
aspectRatio="1/1"
135-
maxW="full"
136-
maxH="full"
137-
flexShrink={0}
138-
sx={sx}
139-
data-is-open={selectedEntityId === id && isPanelOpen}
140-
data-is-error={isInvalid}
141-
data-is-disabled={!entity.isEnabled}
142-
role="button"
143-
onClick={onClick}
144-
cursor="pointer"
145-
>
146-
<Image
147-
src={imageDTO?.thumbnail_url}
148-
objectFit="contain"
128+
<Tooltip label={warnings.length > 0 ? <RefImageWarningTooltipContent warnings={warnings} /> : undefined}>
129+
<Flex
130+
position="relative"
131+
borderWidth={1}
132+
borderStyle="solid"
133+
borderRadius="base"
149134
aspectRatio="1/1"
150-
height={imageDTO?.height}
151-
fallback={<Skeleton h="full" aspectRatio="1/1" />}
152135
maxW="full"
153136
maxH="full"
154-
borderRadius="base"
155-
/>
156-
{isIPAdapterConfig(entity.config) && (
157-
<Flex
158-
position="absolute"
159-
inset={0}
160-
fontWeight="semibold"
161-
alignItems="center"
162-
justifyContent="center"
163-
zIndex={1}
164-
data-visible={showWeightDisplay}
165-
sx={weightDisplaySx}
166-
>
167-
<Text filter="drop-shadow(0px 0px 4px rgb(0, 0, 0)) drop-shadow(0px 0px 2px rgba(0, 0, 0, 1))">
168-
{`${round(entity.config.weight * 100, 2)}%`}
169-
</Text>
170-
</Flex>
171-
)}
172-
{!entity.isEnabled && (
173-
<Icon
174-
position="absolute"
175-
top="50%"
176-
left="50%"
177-
transform="translateX(-50%) translateY(-50%)"
178-
filter="drop-shadow(0px 0px 4px rgb(0, 0, 0)) drop-shadow(0px 0px 2px rgba(0, 0, 0, 1))"
179-
color="base.300"
180-
boxSize={8}
181-
as={PiEyeSlashBold}
182-
/>
183-
)}
184-
{entity.isEnabled && isInvalid && (
185-
<Icon
186-
position="absolute"
187-
top="50%"
188-
left="50%"
189-
transform="translateX(-50%) translateY(-50%)"
190-
filter="drop-shadow(0px 0px 4px rgb(0, 0, 0)) drop-shadow(0px 0px 2px rgba(0, 0, 0, 1))"
191-
color="error.500"
192-
boxSize={12}
193-
as={PiExclamationMarkBold}
137+
flexShrink={0}
138+
sx={sx}
139+
data-is-open={selectedEntityId === id && isPanelOpen}
140+
data-is-error={warnings.length > 0}
141+
data-is-disabled={!entity.isEnabled}
142+
role="button"
143+
onClick={onClick}
144+
cursor="pointer"
145+
>
146+
<Image
147+
src={imageDTO?.thumbnail_url}
148+
objectFit="contain"
149+
aspectRatio="1/1"
150+
height={imageDTO?.height}
151+
fallback={<Skeleton h="full" aspectRatio="1/1" />}
152+
maxW="full"
153+
maxH="full"
154+
borderRadius="base"
194155
/>
195-
)}
196-
</Flex>
156+
{isIPAdapterConfig(entity.config) && (
157+
<Flex
158+
position="absolute"
159+
inset={0}
160+
fontWeight="semibold"
161+
alignItems="center"
162+
justifyContent="center"
163+
zIndex={1}
164+
data-visible={showWeightDisplay}
165+
sx={weightDisplaySx}
166+
>
167+
<Text filter="drop-shadow(0px 0px 4px rgb(0, 0, 0)) drop-shadow(0px 0px 2px rgba(0, 0, 0, 1))">
168+
{`${round(entity.config.weight * 100, 2)}%`}
169+
</Text>
170+
</Flex>
171+
)}
172+
{!entity.isEnabled && (
173+
<Icon
174+
position="absolute"
175+
top="50%"
176+
left="50%"
177+
transform="translateX(-50%) translateY(-50%)"
178+
filter="drop-shadow(0px 0px 4px rgb(0, 0, 0)) drop-shadow(0px 0px 2px rgba(0, 0, 0, 1))"
179+
color="base.300"
180+
boxSize={8}
181+
as={PiEyeSlashBold}
182+
/>
183+
)}
184+
{entity.isEnabled && warnings.length > 0 && (
185+
<Icon
186+
position="absolute"
187+
top="50%"
188+
left="50%"
189+
transform="translateX(-50%) translateY(-50%)"
190+
filter="drop-shadow(0px 0px 4px rgb(0, 0, 0)) drop-shadow(0px 0px 2px rgba(0, 0, 0, 1))"
191+
color="error.500"
192+
boxSize={12}
193+
as={PiExclamationMarkBold}
194+
/>
195+
)}
196+
</Flex>
197+
</Tooltip>
197198
);
198199
});
199200
RefImagePreview.displayName = 'RefImagePreview';
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Flex, ListItem, Text, UnorderedList } from '@invoke-ai/ui-library';
2+
import { upperFirst } from 'es-toolkit/compat';
3+
import { useTranslation } from 'react-i18next';
4+
5+
export const RefImageWarningTooltipContent = ({ warnings }: { warnings: string[] }) => {
6+
const { t } = useTranslation();
7+
8+
return (
9+
<Flex flexDir="column">
10+
<Text fontWeight="semibold">Invalid Reference Image:</Text>
11+
<UnorderedList>
12+
{warnings.map((tKey) => (
13+
<ListItem key={tKey}>{upperFirst(t(tKey))}</ListItem>
14+
))}
15+
</UnorderedList>
16+
</Flex>
17+
);
18+
};

0 commit comments

Comments
 (0)