Skip to content

Commit adc69ea

Browse files
feat(ui): represent IP adapter weight in ref image thumbnail
1 parent b599729 commit adc69ea

File tree

1 file changed

+87
-14
lines changed
  • invokeai/frontend/web/src/features/controlLayers/components/RefImage

1 file changed

+87
-14
lines changed

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

Lines changed: 87 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
PopoverContent,
1212
Portal,
1313
Skeleton,
14+
Text,
1415
} from '@invoke-ai/ui-library';
1516
import { skipToken } from '@reduxjs/toolkit/query';
1617
import { POPPER_MODIFIERS } from 'common/components/InformationalPopover/constants';
@@ -21,7 +22,9 @@ import { RefImageHeader } from 'features/controlLayers/components/RefImage/RefIm
2122
import { RefImageSettings } from 'features/controlLayers/components/RefImage/RefImageSettings';
2223
import { useRefImageEntity } from 'features/controlLayers/components/RefImage/useRefImageEntity';
2324
import { useRefImageIdContext } from 'features/controlLayers/contexts/RefImageIdContext';
24-
import { memo, useCallback, useRef } from 'react';
25+
import { isIPAdapterConfig } from 'features/controlLayers/store/types';
26+
import { round } from 'lodash-es';
27+
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
2528
import { PiImageBold } from 'react-icons/pi';
2629
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
2730

@@ -80,10 +83,11 @@ export const RefImage = memo(() => {
8083
});
8184
RefImage.displayName = 'RefImage';
8285

83-
const imageSx: SystemStyleObject = {
86+
const baseSx: SystemStyleObject = {
8487
opacity: 0.7,
8588
transitionProperty: 'opacity',
8689
transitionDuration: 'normal',
90+
position: 'relative',
8791
_hover: {
8892
opacity: 1,
8993
},
@@ -92,11 +96,58 @@ const imageSx: SystemStyleObject = {
9296
},
9397
};
9498

99+
const weightDisplaySx: SystemStyleObject = {
100+
pointerEvents: 'none',
101+
transitionProperty: 'opacity',
102+
transitionDuration: 'normal',
103+
opacity: 0,
104+
'&[data-visible="true"]': {
105+
opacity: 1,
106+
},
107+
};
108+
109+
const getImageSxWithWeight = (weight: number): SystemStyleObject => {
110+
const fillPercentage = Math.max(0, Math.min(100, weight * 100));
111+
112+
return {
113+
...baseSx,
114+
_after: {
115+
content: '""',
116+
position: 'absolute',
117+
inset: 0,
118+
background: `linear-gradient(to top, transparent ${fillPercentage}%, rgba(0, 0, 0, 0.8) ${fillPercentage}%)`,
119+
pointerEvents: 'none',
120+
borderRadius: 'base',
121+
},
122+
};
123+
};
124+
95125
const Thumbnail = memo(({ disclosure }: { disclosure: UseDisclosure }) => {
96126
const id = useRefImageIdContext();
97127
const entity = useRefImageEntity(id);
128+
const [showWeightDisplay, setShowWeightDisplay] = useState(false);
98129
const { data: imageDTO } = useGetImageDTOQuery(entity.config.image?.image_name ?? skipToken);
99130

131+
const sx = useMemo(() => {
132+
if (!isIPAdapterConfig(entity.config)) {
133+
return baseSx;
134+
}
135+
return getImageSxWithWeight(entity.config.weight);
136+
}, [entity.config]);
137+
138+
useEffect(() => {
139+
if (!isIPAdapterConfig(entity.config)) {
140+
return;
141+
}
142+
setShowWeightDisplay(true);
143+
const timeout = window.setTimeout(() => {
144+
setShowWeightDisplay(false);
145+
}, 1000);
146+
return () => {
147+
window.clearTimeout(timeout);
148+
};
149+
}, [entity.config]);
150+
100151
if (!entity.config.image) {
101152
return (
102153
<PopoverAnchor>
@@ -120,25 +171,47 @@ const Thumbnail = memo(({ disclosure }: { disclosure: UseDisclosure }) => {
120171
}
121172
return (
122173
<PopoverAnchor>
123-
<Image
174+
<Flex
175+
position="relative"
124176
borderWidth={1}
125177
borderStyle="solid"
126-
id={getRefImagePopoverTriggerId(id)}
127-
role="button"
128-
src={imageDTO?.thumbnail_url}
129-
objectFit="contain"
178+
borderRadius="base"
130179
aspectRatio="1/1"
131-
// width={imageDTO?.width}
132-
height={imageDTO?.height}
133-
fallback={<Skeleton h="full" aspectRatio="1/1" />}
134180
maxW="full"
135181
maxH="full"
136-
borderRadius="base"
137-
onClick={disclosure.toggle}
138182
flexShrink={0}
139-
sx={imageSx}
183+
sx={sx}
140184
data-is-open={disclosure.isOpen}
141-
/>
185+
id={getRefImagePopoverTriggerId(id)}
186+
role="button"
187+
onClick={disclosure.toggle}
188+
cursor="pointer"
189+
>
190+
<Image
191+
src={imageDTO?.thumbnail_url}
192+
objectFit="contain"
193+
aspectRatio="1/1"
194+
height={imageDTO?.height}
195+
fallback={<Skeleton h="full" aspectRatio="1/1" />}
196+
maxW="full"
197+
maxH="full"
198+
borderRadius="base"
199+
/>
200+
<Flex
201+
position="absolute"
202+
inset={0}
203+
fontWeight="semibold"
204+
alignItems="center"
205+
justifyContent="center"
206+
zIndex={1}
207+
data-visible={showWeightDisplay}
208+
sx={weightDisplaySx}
209+
>
210+
<Text filter="drop-shadow(0px 0px 4px rgb(0, 0, 0)) drop-shadow(0px 0px 2px rgba(0, 0, 0, 1))">
211+
{`${round(entity.config.weight * 100, 2)}%`}
212+
</Text>
213+
</Flex>
214+
</Flex>
142215
</PopoverAnchor>
143216
);
144217
});

0 commit comments

Comments
 (0)