feat(ui): initial mini controlnet UI, dnd improvements

This commit is contained in:
psychedelicious 2023-06-03 11:13:33 +10:00
parent 72b4371804
commit 6b824eb112
18 changed files with 377 additions and 223 deletions

View File

@ -61,6 +61,7 @@
"@chakra-ui/theme-tools": "^2.0.16",
"@dagrejs/graphlib": "^2.1.12",
"@dnd-kit/core": "^6.0.8",
"@dnd-kit/modifiers": "^6.0.1",
"@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6",
"@floating-ui/react-dom": "^2.0.0",

View File

@ -6,6 +6,7 @@ import {
KeyboardSensor,
MouseSensor,
TouchSensor,
pointerWithin,
useSensor,
useSensors,
} from '@dnd-kit/core';
@ -13,6 +14,7 @@ import { PropsWithChildren, memo, useCallback, useState } from 'react';
import OverlayDragImage from './OverlayDragImage';
import { ImageDTO } from 'services/api';
import { isImageDTO } from 'services/types/guards';
import { snapCenterToCursor } from '@dnd-kit/modifiers';
type ImageDndContextProps = PropsWithChildren;
@ -53,9 +55,10 @@ const ImageDndContext = (props: ImageDndContextProps) => {
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
sensors={sensors}
collisionDetection={pointerWithin}
>
{props.children}
<DragOverlay dropAnimation={null}>
<DragOverlay dropAnimation={null} modifiers={[snapCenterToCursor]}>
{draggedImage && <OverlayDragImage image={draggedImage} />}
</DragOverlay>
</DndContext>

View File

@ -1,4 +1,4 @@
import { Image } from '@chakra-ui/react';
import { Box, Image } from '@chakra-ui/react';
import { memo } from 'react';
import { ImageDTO } from 'services/api';
@ -8,15 +8,27 @@ type OverlayDragImageProps = {
const OverlayDragImage = (props: OverlayDragImageProps) => {
return (
<Image
sx={{
maxW: 36,
maxH: 36,
borderRadius: 'base',
shadow: 'dark-lg',
<Box
style={{
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
userSelect: 'none',
cursor: 'grabbing',
}}
src={props.image.thumbnail_url}
/>
>
<Image
sx={{
maxW: 36,
maxH: 36,
borderRadius: 'base',
shadow: 'dark-lg',
}}
src={props.image.thumbnail_url}
/>
</Box>
);
};

View File

@ -95,6 +95,7 @@ export type AppFeature =
* A disable-able Stable Diffusion feature
*/
export type SDFeature =
| 'controlNet'
| 'noise'
| 'variation'
| 'symmetry'

View File

@ -109,7 +109,7 @@ const IAICustomSelect = (props: IAICustomSelectProps) => {
top: 0,
left: 0,
flexDirection: 'column',
zIndex: 1,
zIndex: 2,
bg: 'base.800',
borderRadius: 'base',
border: '1px',

View File

@ -0,0 +1,27 @@
import { Flex, FlexProps, Spinner, SpinnerProps } from '@chakra-ui/react';
type Props = FlexProps & {
spinnerProps?: SpinnerProps;
};
export const IAIImageFallback = (props: Props) => {
const { spinnerProps, ...rest } = props;
const { sx, ...restFlexProps } = rest;
return (
<Flex
sx={{
bg: 'base.900',
opacity: 0.7,
w: 'full',
h: 'full',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 'base',
...sx,
}}
{...restFlexProps}
>
<Spinner size="xl" {...spinnerProps} />
</Flex>
);
};

View File

@ -40,7 +40,7 @@ import IAIIconButton, { IAIIconButtonProps } from './IAIIconButton';
import { roundDownToMultiple } from 'common/util/roundDownToMultiple';
export type IAIFullSliderProps = {
label: string;
label?: string;
value: number;
min?: number;
max?: number;
@ -178,9 +178,11 @@ const IAISlider = (props: IAIFullSliderProps) => {
isDisabled={isDisabled}
{...sliderFormControlProps}
>
<FormLabel {...sliderFormLabelProps} mb={-1}>
{label}
</FormLabel>
{label && (
<FormLabel {...sliderFormLabelProps} mb={-1}>
{label}
</FormLabel>
)}
<HStack w="100%" gap={2} alignItems="center">
<Slider

View File

@ -1,39 +1,29 @@
import { memo, useCallback, useState } from 'react';
import { ImageDTO } from 'services/api';
import { memo, useCallback } from 'react';
import {
ControlNet,
controlNetImageChanged,
controlNetProcessedImageChanged,
controlNetRemoved,
controlNetSelector,
} from '../store/controlNetSlice';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useAppDispatch } from 'app/store/storeHooks';
import ParamControlNetModel from './parameters/ParamControlNetModel';
import ParamControlNetWeight from './parameters/ParamControlNetWeight';
import ParamControlNetBeginStepPct from './parameters/ParamControlNetBeginStepPct';
import ParamControlNetEndStepPct from './parameters/ParamControlNetEndStepPct';
import {
Box,
Flex,
Spinner,
Tab,
TabList,
TabPanel,
TabPanels,
Tabs,
Text,
} from '@chakra-ui/react';
import IAISelectableImage from './parameters/IAISelectableImage';
import IAIButton from 'common/components/IAIButton';
import { FaUndo } from 'react-icons/fa';
import { TbSquareToggle } from 'react-icons/tb';
import ParamControlNetProcessorSelect from './parameters/ParamControlNetProcessorSelect';
import ControlNetProcessorComponent from './ControlNetProcessorComponent';
import ControlNetPreprocessButton from './ControlNetPreprocessButton';
import IAIIconButton from 'common/components/IAIIconButton';
import ParamControlNetBeginEnd from './parameters/ParamControlNetBeginEnd';
import ControlNetImagePreview from './ControlNetImagePreview';
import { createSelector } from '@reduxjs/toolkit';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
type ControlNetProps = {
controlNet: ControlNet;

View File

@ -5,13 +5,12 @@ import {
controlNetSelector,
} from '../store/controlNetSlice';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { Box, Flex, Spinner } from '@chakra-ui/react';
import IAISelectableImage from './parameters/IAISelectableImage';
import { TbSquareToggle } from 'react-icons/tb';
import IAIIconButton from 'common/components/IAIIconButton';
import { Box } from '@chakra-ui/react';
import IAIDndImage from './parameters/IAISelectableImage';
import { createSelector } from '@reduxjs/toolkit';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { AnimatePresence, motion } from 'framer-motion';
import { IAIImageFallback } from 'common/components/IAIImageFallback';
const selector = createSelector(
controlNetSelector,
@ -43,24 +42,20 @@ const ControlNetImagePreview = (props: Props) => {
[controlNetId, dispatch]
);
const handleControlImageReset = useCallback(() => {
dispatch(controlNetImageChanged({ controlNetId, controlImage: null }));
}, [controlNetId, dispatch]);
const shouldShowProcessedImageBackdrop =
Number(controlImage?.width) > Number(processedControlImage?.width) ||
Number(controlImage?.height) > Number(processedControlImage?.height);
return (
<Box sx={{ position: 'relative', aspectRatio: '1/1' }}>
<IAISelectableImage
<Box
sx={{ position: 'relative', h: 'inherit' }}
onMouseOver={() => setShouldShowProcessedImage(false)}
onMouseOut={() => setShouldShowProcessedImage(true)}
>
<IAIDndImage
image={controlImage}
onChange={handleControlImageChanged}
onReset={handleControlImageReset}
onDrop={handleControlImageChanged}
isDropDisabled={Boolean(processedControlImage)}
fallback={<ProcessedImageFallback />}
withResetIcon
resetIconSize="sm"
/>
<AnimatePresence>
{controlImage &&
@ -108,13 +103,10 @@ const ControlNetImagePreview = (props: Props) => {
h: 'full',
}}
>
<IAISelectableImage
<IAIDndImage
image={processedControlImage}
onChange={handleControlImageChanged}
onReset={handleControlImageReset}
withResetIcon
resetIconSize="sm"
fallback={<ProcessedImageFallback />}
onDrop={handleControlImageChanged}
payloadImage={controlImage}
/>
</Box>
</Box>
@ -131,18 +123,7 @@ const ControlNetImagePreview = (props: Props) => {
h: 'full',
}}
>
<ProcessedImageFallback />
</Box>
)}
{processedControlImage && !isProcessingControlImage && (
<Box sx={{ position: 'absolute', bottom: 0, insetInlineEnd: 0, p: 2 }}>
<IAIIconButton
aria-label="Hide Preview"
icon={<TbSquareToggle />}
size="sm"
onMouseOver={() => setShouldShowProcessedImage(false)}
onMouseOut={() => setShouldShowProcessedImage(true)}
/>
<IAIImageFallback />
</Box>
)}
</Box>
@ -150,19 +131,3 @@ const ControlNetImagePreview = (props: Props) => {
};
export default memo(ControlNetImagePreview);
const ProcessedImageFallback = () => (
<Flex
sx={{
bg: 'base.900',
opacity: 0.7,
w: 'full',
h: 'full',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 'base',
}}
>
<Spinner size="xl" />
</Flex>
);

View File

@ -0,0 +1,101 @@
import { memo, useCallback } from 'react';
import {
ControlNet,
controlNetProcessedImageChanged,
controlNetRemoved,
} from '../store/controlNetSlice';
import { useAppDispatch } from 'app/store/storeHooks';
import ParamControlNetModel from './parameters/ParamControlNetModel';
import ParamControlNetWeight from './parameters/ParamControlNetWeight';
import {
Box,
Flex,
Tab,
TabList,
TabPanel,
TabPanels,
Tabs,
Text,
} from '@chakra-ui/react';
import IAIButton from 'common/components/IAIButton';
import { FaUndo } from 'react-icons/fa';
import ParamControlNetProcessorSelect from './parameters/ParamControlNetProcessorSelect';
import ControlNetProcessorComponent from './ControlNetProcessorComponent';
import ControlNetPreprocessButton from './ControlNetPreprocessButton';
import ParamControlNetBeginEnd from './parameters/ParamControlNetBeginEnd';
import ControlNetImagePreview from './ControlNetImagePreview';
type ControlNetProps = {
controlNet: ControlNet;
};
const ControlNet = (props: ControlNetProps) => {
const {
controlNetId,
isEnabled,
model,
weight,
beginStepPct,
endStepPct,
controlImage,
isControlImageProcessed,
processedControlImage,
processorNode,
} = props.controlNet;
const dispatch = useAppDispatch();
const handleReset = useCallback(() => {
dispatch(
controlNetProcessedImageChanged({
controlNetId,
processedControlImage: null,
})
);
}, [controlNetId, dispatch]);
const handleControlNetRemoved = useCallback(() => {
dispatch(controlNetRemoved(controlNetId));
}, [controlNetId, dispatch]);
return (
<Flex
sx={{
gap: 4,
p: 2,
paddingInlineEnd: 4,
bg: 'base.850',
borderRadius: 'base',
}}
>
<Flex
sx={{
alignItems: 'center',
justifyContent: 'center',
h: 36,
w: 36,
}}
>
<ControlNetImagePreview
controlNetId={controlNetId}
controlImage={controlImage}
processedControlImage={processedControlImage}
/>
</Flex>
<Flex sx={{ flexDir: 'column', gap: 2, w: 'full', h: 'full' }}>
<ParamControlNetModel controlNetId={controlNetId} model={model} />
<ParamControlNetWeight
controlNetId={controlNetId}
weight={weight}
mini
/>
<ParamControlNetBeginEnd
controlNetId={controlNetId}
beginStepPct={beginStepPct}
endStepPct={endStepPct}
mini
/>
</Flex>
</Flex>
);
};
export default memo(ControlNet);

View File

@ -4,11 +4,12 @@ import {
Icon,
IconButtonProps,
Image,
Spinner,
Text,
} from '@chakra-ui/react';
import { useDroppable } from '@dnd-kit/core';
import { useDraggable, useDroppable } from '@dnd-kit/core';
import { useCombinedRefs } from '@dnd-kit/utilities';
import IAIIconButton from 'common/components/IAIIconButton';
import { IAIImageFallback } from 'common/components/IAIImageFallback';
import ImageMetadataOverlay from 'common/components/ImageMetadataOverlay';
import { useGetUrl } from 'common/util/getUrl';
import { AnimatePresence, motion } from 'framer-motion';
@ -18,42 +19,65 @@ import { FaImage, FaTimes } from 'react-icons/fa';
import { ImageDTO } from 'services/api';
import { v4 as uuidv4 } from 'uuid';
const PLACEHOLDER_MIN_HEIGHT = 48;
const PLACEHOLDER_MIN_HEIGHT = 36;
type IAISelectableImageProps = {
image: ImageDTO | null | undefined;
onChange: (image: ImageDTO) => void;
onDrop: (image: ImageDTO) => void;
onReset?: () => void;
onError?: (event: SyntheticEvent<HTMLImageElement>) => void;
onLoad?: (event: SyntheticEvent<HTMLImageElement>) => void;
resetIconSize?: IconButtonProps['size'];
withResetIcon?: boolean;
withMetadataOverlay?: boolean;
isDragDisabled?: boolean;
isDropDisabled?: boolean;
fallback?: ReactElement;
payloadImage?: ImageDTO | null | undefined;
};
const IAISelectableImage = (props: IAISelectableImageProps) => {
const IAIDndImage = (props: IAISelectableImageProps) => {
const {
image,
onChange,
onDrop,
onReset,
onError,
resetIconSize = 'md',
withResetIcon = false,
withMetadataOverlay = false,
isDropDisabled = false,
fallback = <ImageFallback />,
isDragDisabled = false,
fallback = <IAIImageFallback />,
payloadImage,
} = props;
const droppableId = useRef(uuidv4());
const dndId = useRef(uuidv4());
const { getUrl } = useGetUrl();
const { isOver, setNodeRef, active } = useDroppable({
id: droppableId.current,
const {
isOver,
setNodeRef: setDroppableRef,
active,
} = useDroppable({
id: dndId.current,
disabled: isDropDisabled,
data: {
handleDrop: onChange,
handleDrop: onDrop,
},
});
const {
attributes,
listeners,
setNodeRef: setDraggableRef,
} = useDraggable({
id: dndId.current,
data: {
image: payloadImage ? payloadImage : image,
},
disabled: isDragDisabled,
});
const setNodeRef = useCombinedRefs(setDroppableRef, setDraggableRef);
return (
<Flex
sx={{
@ -62,7 +86,11 @@ const IAISelectableImage = (props: IAISelectableImageProps) => {
alignItems: 'center',
justifyContent: 'center',
position: 'relative',
minW: 36,
minH: 36,
}}
{...attributes}
{...listeners}
ref={setNodeRef}
>
{image && (
@ -80,8 +108,11 @@ const IAISelectableImage = (props: IAISelectableImageProps) => {
fallbackStrategy="beforeLoadOrError"
fallback={fallback}
onError={onError}
objectFit="contain"
draggable={false}
sx={{
maxW: 'full',
maxH: 'full',
borderRadius: 'base',
}}
/>
@ -139,7 +170,7 @@ const IAISelectableImage = (props: IAISelectableImageProps) => {
);
};
export default memo(IAISelectableImage);
export default memo(IAIDndImage);
type DropOverlayProps = {
isOver: boolean;
@ -179,14 +210,15 @@ const DropOverlay = (props: DropOverlayProps) => {
w: 'full',
h: 'full',
bg: 'base.900',
opacity: isOver ? 0.9 : 0.7,
opacity: 0.7,
borderRadius: 'base',
alignItems: 'center',
justifyContent: 'center',
transitionProperty: 'common',
transitionDuration: '0.15s',
transitionDuration: '0.1s',
}}
/>
<Flex
sx={{
position: 'absolute',
@ -194,51 +226,31 @@ const DropOverlay = (props: DropOverlayProps) => {
left: 0,
w: 'full',
h: 'full',
opacity: isOver ? 1 : 0.9,
alignItems: 'center',
justifyContent: 'center',
transitionProperty: 'common',
transitionDuration: '0.15s',
}}
>
<Text sx={{ fontSize: '2xl', fontWeight: 600, color: 'base.200' }}>
Drop Image
</Text>
</Flex>
<Flex
sx={{
position: 'absolute',
top: 0,
left: 0,
w: 'full',
h: 'full',
opacity: isOver ? 1 : 0.7,
opacity: 1,
borderWidth: 2,
borderColor: 'base.500',
borderColor: isOver ? 'base.200' : 'base.500',
borderRadius: 'base',
borderStyle: 'dashed',
transitionProperty: 'common',
transitionDuration: '0.15s',
transitionDuration: '0.1s',
alignItems: 'center',
justifyContent: 'center',
}}
></Flex>
>
<Text
sx={{
fontSize: '2xl',
fontWeight: 600,
transform: isOver ? 'scale(1.1)' : 'scale(1)',
color: isOver ? 'base.100' : 'base.500',
transitionProperty: 'common',
transitionDuration: '0.1s',
}}
>
Drop
</Text>
</Flex>
</Flex>
</motion.div>
);
};
const ImageFallback = () => (
<Flex
sx={{
w: 'full',
h: 'full',
alignItems: 'center',
justifyContent: 'center',
minH: PLACEHOLDER_MIN_HEIGHT,
color: 'base.400',
bg: 'base.850',
borderRadius: 'base',
}}
>
<Spinner size="xl" />
</Flex>
);

View File

@ -23,12 +23,13 @@ type Props = {
controlNetId: string;
beginStepPct: number;
endStepPct: number;
mini?: boolean;
};
const formatPct = (v: number) => `${Math.round(v * 100)}%`;
const ParamControlNetBeginEnd = (props: Props) => {
const { controlNetId, beginStepPct, endStepPct } = props;
const { controlNetId, beginStepPct, endStepPct, mini = false } = props;
const dispatch = useAppDispatch();
const { t } = useTranslation();
@ -69,52 +70,59 @@ const ParamControlNetBeginEnd = (props: Props) => {
<Tooltip label={formatPct(endStepPct)} placement="top" hasArrow>
<RangeSliderThumb index={1} />
</Tooltip>
<RangeSliderMark
value={0}
sx={{
fontSize: 'xs',
fontWeight: '500',
color: 'base.200',
insetInlineStart: '0 !important',
insetInlineEnd: 'unset !important',
mt: 1.5,
}}
>
0%
</RangeSliderMark>
<RangeSliderMark
value={0.5}
sx={{
fontSize: 'xs',
fontWeight: '500',
color: 'base.200',
mt: 1.5,
}}
>
50%
</RangeSliderMark>
<RangeSliderMark
value={1}
sx={{
fontSize: 'xs',
fontWeight: '500',
color: 'base.200',
insetInlineStart: 'unset !important',
insetInlineEnd: '0 !important',
mt: 1.5,
}}
>
100%
</RangeSliderMark>
{!mini && (
<>
{' '}
<RangeSliderMark
value={0}
sx={{
fontSize: 'xs',
fontWeight: '500',
color: 'base.200',
insetInlineStart: '0 !important',
insetInlineEnd: 'unset !important',
mt: 1.5,
}}
>
0%
</RangeSliderMark>
<RangeSliderMark
value={0.5}
sx={{
fontSize: 'xs',
fontWeight: '500',
color: 'base.200',
mt: 1.5,
}}
>
50%
</RangeSliderMark>
<RangeSliderMark
value={1}
sx={{
fontSize: 'xs',
fontWeight: '500',
color: 'base.200',
insetInlineStart: 'unset !important',
insetInlineEnd: '0 !important',
mt: 1.5,
}}
>
100%
</RangeSliderMark>
</>
)}
</RangeSlider>
<IAIIconButton
size="sm"
aria-label={t('accessibility.reset')}
tooltip={t('accessibility.reset')}
icon={<BiReset />}
onClick={handleStepPctReset}
/>
{!mini && (
<IAIIconButton
size="sm"
aria-label={t('accessibility.reset')}
tooltip={t('accessibility.reset')}
icon={<BiReset />}
onClick={handleStepPctReset}
/>
)}
</HStack>
</FormControl>
);

View File

@ -6,10 +6,11 @@ import { memo, useCallback } from 'react';
type ParamControlNetWeightProps = {
controlNetId: string;
weight: number;
mini?: boolean;
};
const ParamControlNetWeight = (props: ParamControlNetWeightProps) => {
const { controlNetId, weight } = props;
const { controlNetId, weight, mini = false } = props;
const dispatch = useAppDispatch();
const handleWeightChanged = useCallback(
@ -23,6 +24,20 @@ const ParamControlNetWeight = (props: ParamControlNetWeightProps) => {
dispatch(controlNetWeightChanged({ controlNetId, weight: 1 }));
};
if (mini) {
return (
<IAISlider
label={'Weight'}
sliderFormLabelProps={{ pb: 1 }}
value={weight}
onChange={handleWeightChanged}
min={0}
max={1}
step={0.01}
/>
);
}
return (
<IAISlider
label="Weight"

View File

@ -1,21 +1,20 @@
import { Box, Flex, Image } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useGetUrl } from 'common/util/getUrl';
import { uiSelector } from 'features/ui/store/uiSelectors';
import { isEqual } from 'lodash-es';
import { gallerySelector } from '../store/gallerySelectors';
import ImageMetadataViewer from './ImageMetaDataViewer/ImageMetadataViewer';
import NextPrevImageButtons from './NextPrevImageButtons';
import { DragEvent, memo, useCallback, useEffect, useState } from 'react';
import { memo, useCallback } from 'react';
import { systemSelector } from 'features/system/store/systemSelectors';
import ImageFallbackSpinner from './ImageFallbackSpinner';
import ImageMetadataOverlay from 'common/components/ImageMetadataOverlay';
import { configSelector } from '../../system/store/configSelectors';
import { useAppToaster } from 'app/components/Toaster';
import { imageSelected } from '../store/gallerySlice';
import { useDraggable } from '@dnd-kit/core';
import IAIDndImage from 'features/controlNet/components/parameters/IAISelectableImage';
import { ImageDTO } from 'services/api';
import { IAIImageFallback } from 'common/components/IAIImageFallback';
export const imagesSelector = createSelector(
[uiSelector, gallerySelector, systemSelector],
@ -52,17 +51,8 @@ const CurrentImagePreview = () => {
shouldAntialiasProgressImage,
} = useAppSelector(imagesSelector);
const { shouldFetchImages } = useAppSelector(configSelector);
const { getUrl } = useGetUrl();
const toaster = useAppToaster();
const dispatch = useAppDispatch();
const [isLoaded, setIsLoaded] = useState(false);
const { attributes, listeners, setNodeRef } = useDraggable({
id: `currentImage_${image?.image_name}`,
data: {
image,
},
});
const handleError = useCallback(() => {
dispatch(imageSelected());
@ -75,9 +65,12 @@ const CurrentImagePreview = () => {
}
}, [dispatch, toaster, shouldFetchImages]);
useEffect(() => {
setIsLoaded(false);
}, [image]);
const handleDrop = useCallback(
(droppedImage: ImageDTO) => {
dispatch(imageSelected(droppedImage));
},
[dispatch]
);
return (
<Flex
@ -108,42 +101,28 @@ const CurrentImagePreview = () => {
image && (
<Flex
sx={{
width: 'full',
height: 'full',
alignItems: 'center',
justifyContent: 'center',
position: 'absolute',
}}
>
<Image
ref={setNodeRef}
{...listeners}
{...attributes}
src={getUrl(image.image_url)}
fallbackStrategy="beforeLoadOrError"
fallback={<ImageFallbackSpinner />}
sx={{
objectFit: 'contain',
maxWidth: '100%',
maxHeight: '100%',
height: 'auto',
borderRadius: 'base',
touchAction: 'none',
}}
<IAIDndImage
image={image}
onDrop={handleDrop}
onError={handleError}
onLoad={() => {
setIsLoaded(true);
}}
fallback={<IAIImageFallback sx={{ bg: 'none' }} />}
/>
{isLoaded && <ImageMetadataOverlay image={image} />}
</Flex>
)
)}
{shouldShowImageDetails && image && 'metadata' in image && (
{shouldShowImageDetails && image && image.metadata && (
<Box
sx={{
position: 'absolute',
top: '0',
width: '100%',
height: '100%',
width: 'full',
height: 'full',
borderRadius: 'base',
overflow: 'scroll',
}}
@ -151,7 +130,19 @@ const CurrentImagePreview = () => {
<ImageMetadataViewer image={image} />
</Box>
)}
{!shouldShowImageDetails && <NextPrevImageButtons />}
{!shouldShowImageDetails && (
<Box
sx={{
position: 'absolute',
top: '0',
width: 'full',
height: 'full',
pointerEvents: 'none',
}}
>
<NextPrevImageButtons />
</Box>
)}
</Flex>
);
};

View File

@ -8,7 +8,7 @@ import {
import { memo, useCallback } from 'react';
import { FieldComponentProps } from './types';
import IAISelectableImage from 'features/controlNet/components/parameters/IAISelectableImage';
import IAIDndImage from 'features/controlNet/components/parameters/IAISelectableImage';
import { ImageDTO } from 'services/api';
import { Flex } from '@chakra-ui/react';
@ -51,9 +51,9 @@ const ImageInputFieldComponent = (
justifyContent: 'center',
}}
>
<IAISelectableImage
<IAIDndImage
image={field.value}
onChange={handleChange}
onDrop={handleChange}
onReset={handleReset}
resetIconSize="sm"
/>

View File

@ -24,6 +24,8 @@ import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { map, startCase } from 'lodash-es';
import { v4 as uuidv4 } from 'uuid';
import { CloseIcon } from '@chakra-ui/icons';
import ControlNetMini from 'features/controlNet/components/ControlNetMini';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
const selector = createSelector(
controlNetSelector,
@ -38,6 +40,7 @@ const selector = createSelector(
const ParamControlNetCollapse = () => {
const { t } = useTranslation();
const { controlNetsArray, isEnabled } = useAppSelector(selector);
const isControlNetDisabled = useFeatureStatus('controlNet').isFeatureDisabled;
const dispatch = useAppDispatch();
const handleClickControlNetToggle = useCallback(() => {
@ -48,6 +51,18 @@ const ParamControlNetCollapse = () => {
dispatch(controlNetAdded({ controlNetId: uuidv4() }));
}, [dispatch]);
if (isControlNetDisabled) {
return null;
}
return (
<>
{controlNetsArray.map((c) => (
<ControlNetMini key={c.controlNetId} controlNet={c} />
))}
</>
);
return (
<IAICollapse
label={'ControlNet'}
@ -80,6 +95,7 @@ const ParamControlNetCollapse = () => {
{controlNetsArray.map((c) => (
<TabPanel key={`tabPanel_${c.controlNetId}`} sx={{ p: 0 }}>
<ControlNet controlNet={c} />
{/* <ControlNetMini controlNet={c} /> */}
</TabPanel>
))}
</TabPanels>

View File

@ -12,8 +12,9 @@ import { generationSelector } from 'features/parameters/store/generationSelector
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { configSelector } from '../../../../system/store/configSelectors';
import { useAppToaster } from 'app/components/Toaster';
import IAISelectableImage from 'features/controlNet/components/parameters/IAISelectableImage';
import IAIDndImage from 'features/controlNet/components/parameters/IAISelectableImage';
import { ImageDTO } from 'services/api';
import { IAIImageFallback } from 'common/components/IAIImageFallback';
const selector = createSelector(
[generationSelector],
@ -73,10 +74,11 @@ const InitialImagePreview = () => {
justifyContent: 'center',
}}
>
<IAISelectableImage
<IAIDndImage
image={initialImage}
onChange={handleChange}
onDrop={handleChange}
onReset={handleReset}
fallback={<IAIImageFallback sx={{ bg: 'none' }} />}
/>
</Flex>
);

View File

@ -953,6 +953,14 @@
"@dnd-kit/utilities" "^3.2.1"
tslib "^2.0.0"
"@dnd-kit/modifiers@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/@dnd-kit/modifiers/-/modifiers-6.0.1.tgz#9e39b25fd6e323659604cc74488fe044d33188c8"
integrity sha512-rbxcsg3HhzlcMHVHWDuh9LCjpOVAgqbV78wLGI8tziXY3+qcMQ61qVXIvNKQFuhj75dSfD+o+PYZQ/NUk2A23A==
dependencies:
"@dnd-kit/utilities" "^3.2.1"
tslib "^2.0.0"
"@dnd-kit/utilities@^3.2.1":
version "3.2.1"
resolved "https://registry.yarnpkg.com/@dnd-kit/utilities/-/utilities-3.2.1.tgz#53f9e2016fd2506ec49e404c289392cfff30332a"