mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Merge branch 'main' into lstein/config-management-fixes
This commit is contained in:
commit
9bcf0b2251
invokeai/frontend/web/src
app/components/ImageDnd
common/components
features
canvas/store
controlNet/components
gallery/components
parameters/components/Parameters/ImageToImage
ui/components/tabs/UnifiedCanvas
@ -46,9 +46,12 @@ const ImageDndContext = (props: ImageDndContextProps) => {
|
||||
const touchSensor = useSensor(TouchSensor, {
|
||||
activationConstraint: { distance: 15 },
|
||||
});
|
||||
const keyboardSensor = useSensor(KeyboardSensor);
|
||||
// TODO: Use KeyboardSensor - needs composition of multiple collisionDetection algos
|
||||
// Alternatively, fix `rectIntersection` collection detection to work with the drag overlay
|
||||
// (currently the drag element collision rect is not correctly calculated)
|
||||
// const keyboardSensor = useSensor(KeyboardSensor);
|
||||
|
||||
const sensors = useSensors(mouseSensor, touchSensor, keyboardSensor);
|
||||
const sensors = useSensors(mouseSensor, touchSensor);
|
||||
|
||||
return (
|
||||
<DndContext
|
||||
|
@ -1,23 +1,17 @@
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
Icon,
|
||||
IconButtonProps,
|
||||
Image,
|
||||
Text,
|
||||
} from '@chakra-ui/react';
|
||||
import { Box, Flex, Icon, IconButtonProps, Image } from '@chakra-ui/react';
|
||||
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';
|
||||
import { AnimatePresence } from 'framer-motion';
|
||||
import { ReactElement, SyntheticEvent } from 'react';
|
||||
import { memo, useRef } from 'react';
|
||||
import { FaImage, FaTimes } from 'react-icons/fa';
|
||||
import { ImageDTO } from 'services/api';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import IAIDropOverlay from './IAIDropOverlay';
|
||||
|
||||
type IAIDndImageProps = {
|
||||
image: ImageDTO | null | undefined;
|
||||
@ -138,7 +132,7 @@ const IAIDndImage = (props: IAIDndImageProps) => {
|
||||
</Box>
|
||||
)}
|
||||
<AnimatePresence>
|
||||
{active && <DropOverlay isOver={isOver} />}
|
||||
{active && <IAIDropOverlay isOver={isOver} />}
|
||||
</AnimatePresence>
|
||||
</Flex>
|
||||
)}
|
||||
@ -164,7 +158,7 @@ const IAIDndImage = (props: IAIDndImageProps) => {
|
||||
/>
|
||||
</Flex>
|
||||
<AnimatePresence>
|
||||
{active && <DropOverlay isOver={isOver} />}
|
||||
{active && <IAIDropOverlay isOver={isOver} />}
|
||||
</AnimatePresence>
|
||||
</>
|
||||
)}
|
||||
@ -173,86 +167,3 @@ const IAIDndImage = (props: IAIDndImageProps) => {
|
||||
};
|
||||
|
||||
export default memo(IAIDndImage);
|
||||
|
||||
type DropOverlayProps = {
|
||||
isOver: boolean;
|
||||
};
|
||||
|
||||
const DropOverlay = (props: DropOverlayProps) => {
|
||||
const { isOver } = props;
|
||||
return (
|
||||
<motion.div
|
||||
key="statusText"
|
||||
initial={{
|
||||
opacity: 0,
|
||||
}}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
transition: { duration: 0.1 },
|
||||
}}
|
||||
exit={{
|
||||
opacity: 0,
|
||||
transition: { duration: 0.1 },
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
bg: 'base.900',
|
||||
opacity: 0.7,
|
||||
borderRadius: 'base',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: '0.1s',
|
||||
}}
|
||||
/>
|
||||
|
||||
<Flex
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
opacity: 1,
|
||||
borderWidth: 2,
|
||||
borderColor: isOver ? 'base.200' : 'base.500',
|
||||
borderRadius: 'base',
|
||||
borderStyle: 'dashed',
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: '0.1s',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<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>
|
||||
);
|
||||
};
|
||||
|
@ -0,0 +1,91 @@
|
||||
import { Flex, Text } from '@chakra-ui/react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { memo, useRef } from 'react';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
type Props = {
|
||||
isOver: boolean;
|
||||
label?: string;
|
||||
};
|
||||
|
||||
export const IAIDropOverlay = (props: Props) => {
|
||||
const { isOver, label = 'Drop' } = props;
|
||||
const motionId = useRef(uuidv4());
|
||||
return (
|
||||
<motion.div
|
||||
key={motionId.current}
|
||||
initial={{
|
||||
opacity: 0,
|
||||
}}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
transition: { duration: 0.1 },
|
||||
}}
|
||||
exit={{
|
||||
opacity: 0,
|
||||
transition: { duration: 0.1 },
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
bg: 'base.900',
|
||||
opacity: 0.7,
|
||||
borderRadius: 'base',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: '0.1s',
|
||||
}}
|
||||
/>
|
||||
|
||||
<Flex
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
opacity: 1,
|
||||
borderWidth: 2,
|
||||
borderColor: isOver ? 'base.200' : 'base.500',
|
||||
borderRadius: 'base',
|
||||
borderStyle: 'dashed',
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: '0.1s',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
sx={{
|
||||
fontSize: '2xl',
|
||||
fontWeight: 600,
|
||||
transform: isOver ? 'scale(1.1)' : 'scale(1)',
|
||||
color: isOver ? 'base.100' : 'base.500',
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: '0.1s',
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</motion.div>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(IAIDropOverlay);
|
@ -30,6 +30,7 @@ import {
|
||||
} from './canvasTypes';
|
||||
import { ImageDTO } from 'services/api';
|
||||
import { sessionCanceled } from 'services/thunks/session';
|
||||
import { setShouldUseCanvasBetaLayout } from 'features/ui/store/uiSlice';
|
||||
|
||||
export const initialLayerState: CanvasLayerState = {
|
||||
objects: [],
|
||||
@ -851,6 +852,10 @@ export const canvasSlice = createSlice({
|
||||
state.layerState.stagingArea = initialLayerState.stagingArea;
|
||||
}
|
||||
});
|
||||
|
||||
builder.addCase(setShouldUseCanvasBetaLayout, (state, action) => {
|
||||
state.doesCanvasNeedScaling = true;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -60,7 +60,10 @@ const ControlNetImagePreview = (props: Props) => {
|
||||
processorType !== 'none';
|
||||
|
||||
return (
|
||||
<Box ref={containerRef} sx={{ position: 'relative', w: 'full', h: 'full' }}>
|
||||
<Box
|
||||
ref={containerRef}
|
||||
sx={{ position: 'relative', w: 'full', h: 'full', aspectRatio: '1/1' }}
|
||||
>
|
||||
<IAIDndImage
|
||||
image={controlImage}
|
||||
onDrop={handleDrop}
|
||||
|
@ -51,6 +51,7 @@ const CurrentImageDisplay = () => {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: 4,
|
||||
position: 'absolute',
|
||||
}}
|
||||
>
|
||||
<CurrentImagePreview />
|
||||
|
@ -72,9 +72,10 @@ const InitialImagePreview = () => {
|
||||
sx={{
|
||||
width: 'full',
|
||||
height: 'full',
|
||||
position: 'relative',
|
||||
position: 'absolute',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
p: 4,
|
||||
}}
|
||||
>
|
||||
<IAIDndImage
|
||||
|
@ -1,72 +0,0 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
// import IAICanvas from 'features/canvas/components/IAICanvas';
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAICanvas from 'features/canvas/components/IAICanvas';
|
||||
import IAICanvasResizer from 'features/canvas/components/IAICanvasResizer';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { useLayoutEffect } from 'react';
|
||||
import UnifiedCanvasToolbarBeta from './UnifiedCanvasToolbarBeta';
|
||||
import UnifiedCanvasToolSettingsBeta from './UnifiedCanvasToolSettingsBeta';
|
||||
import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale';
|
||||
|
||||
const selector = createSelector(
|
||||
[canvasSelector],
|
||||
(canvas) => {
|
||||
const { doesCanvasNeedScaling } = canvas;
|
||||
return {
|
||||
doesCanvasNeedScaling,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const UnifiedCanvasContentBeta = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { doesCanvasNeedScaling } = useAppSelector(selector);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
dispatch(requestCanvasRescale());
|
||||
const resizeCallback = () => {
|
||||
dispatch(requestCanvasRescale());
|
||||
};
|
||||
|
||||
window.addEventListener('resize', resizeCallback);
|
||||
|
||||
return () => window.removeEventListener('resize', resizeCallback);
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
borderRadius: 'base',
|
||||
bg: 'base.850',
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
flexDirection="row"
|
||||
width="100%"
|
||||
height="100%"
|
||||
columnGap={4}
|
||||
padding={4}
|
||||
>
|
||||
<UnifiedCanvasToolbarBeta />
|
||||
<Flex width="100%" height="100%" flexDirection="column" rowGap={4}>
|
||||
<UnifiedCanvasToolSettingsBeta />
|
||||
{doesCanvasNeedScaling ? <IAICanvasResizer /> : <IAICanvas />}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default UnifiedCanvasContentBeta;
|
@ -1,34 +1,58 @@
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import IAICanvas from 'features/canvas/components/IAICanvas';
|
||||
import IAICanvasResizer from 'features/canvas/components/IAICanvasResizer';
|
||||
import IAICanvasToolbar from 'features/canvas/components/IAICanvasToolbar/IAICanvasToolbar';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { uiSelector } from 'features/ui/store/uiSelectors';
|
||||
|
||||
import { memo, useLayoutEffect } from 'react';
|
||||
import { memo, useCallback, useLayoutEffect } from 'react';
|
||||
import UnifiedCanvasToolbarBeta from './UnifiedCanvasBeta/UnifiedCanvasToolbarBeta';
|
||||
import UnifiedCanvasToolSettingsBeta from './UnifiedCanvasBeta/UnifiedCanvasToolSettingsBeta';
|
||||
import { ImageDTO } from 'services/api';
|
||||
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
||||
import { useDroppable } from '@dnd-kit/core';
|
||||
import IAIDropOverlay from 'common/components/IAIDropOverlay';
|
||||
|
||||
const selector = createSelector(
|
||||
[canvasSelector],
|
||||
(canvas) => {
|
||||
[canvasSelector, uiSelector],
|
||||
(canvas, ui) => {
|
||||
const { doesCanvasNeedScaling } = canvas;
|
||||
const { shouldUseCanvasBetaLayout } = ui;
|
||||
return {
|
||||
doesCanvasNeedScaling,
|
||||
shouldUseCanvasBetaLayout,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: isEqual,
|
||||
},
|
||||
}
|
||||
defaultSelectorOptions
|
||||
);
|
||||
|
||||
const UnifiedCanvasContent = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { doesCanvasNeedScaling } = useAppSelector(selector);
|
||||
const { doesCanvasNeedScaling, shouldUseCanvasBetaLayout } =
|
||||
useAppSelector(selector);
|
||||
|
||||
const onDrop = useCallback(
|
||||
(droppedImage: ImageDTO) => {
|
||||
dispatch(setInitialCanvasImage(droppedImage));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const {
|
||||
isOver,
|
||||
setNodeRef: setDroppableRef,
|
||||
active,
|
||||
} = useDroppable({
|
||||
id: 'unifiedCanvas',
|
||||
data: {
|
||||
handleDrop: onDrop,
|
||||
},
|
||||
});
|
||||
|
||||
useLayoutEffect(() => {
|
||||
dispatch(requestCanvasRescale());
|
||||
@ -42,14 +66,57 @@ const UnifiedCanvasContent = () => {
|
||||
return () => window.removeEventListener('resize', resizeCallback);
|
||||
}, [dispatch]);
|
||||
|
||||
if (shouldUseCanvasBetaLayout) {
|
||||
return (
|
||||
<Box
|
||||
ref={setDroppableRef}
|
||||
tabIndex={0}
|
||||
sx={{
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
borderRadius: 'base',
|
||||
bg: 'base.850',
|
||||
p: 4,
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
sx={{
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
gap: 4,
|
||||
}}
|
||||
>
|
||||
<UnifiedCanvasToolbarBeta />
|
||||
<Flex
|
||||
sx={{
|
||||
flexDir: 'column',
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
gap: 4,
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
<UnifiedCanvasToolSettingsBeta />
|
||||
<Box sx={{ w: 'full', h: 'full', position: 'relative' }}>
|
||||
{doesCanvasNeedScaling ? <IAICanvasResizer /> : <IAICanvas />}
|
||||
{active && <IAIDropOverlay isOver={isOver} />}
|
||||
</Box>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box
|
||||
ref={setDroppableRef}
|
||||
tabIndex={-1}
|
||||
sx={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
padding: 4,
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
borderRadius: 'base',
|
||||
bg: 'base.850',
|
||||
p: 4,
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
@ -57,8 +124,8 @@ const UnifiedCanvasContent = () => {
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: 4,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
}}
|
||||
>
|
||||
<IAICanvasToolbar />
|
||||
@ -68,11 +135,14 @@ const UnifiedCanvasContent = () => {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: 4,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
}}
|
||||
>
|
||||
{doesCanvasNeedScaling ? <IAICanvasResizer /> : <IAICanvas />}
|
||||
<Box sx={{ w: 'full', h: 'full', position: 'relative' }}>
|
||||
{doesCanvasNeedScaling ? <IAICanvasResizer /> : <IAICanvas />}
|
||||
{active && <IAIDropOverlay isOver={isOver} />}
|
||||
</Box>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Box>
|
||||
|
@ -1,34 +1,16 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { memo } from 'react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { uiSelector } from 'features/ui/store/uiSelectors';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import UnifiedCanvasContent from './UnifiedCanvasContent';
|
||||
import UnifiedCanvasParameters from './UnifiedCanvasParameters';
|
||||
import UnifiedCanvasContentBeta from './UnifiedCanvasBeta/UnifiedCanvasContentBeta';
|
||||
import ParametersPinnedWrapper from '../../ParametersPinnedWrapper';
|
||||
|
||||
const selector = createSelector(uiSelector, (ui) => {
|
||||
const { shouldUseCanvasBetaLayout } = ui;
|
||||
|
||||
return {
|
||||
shouldUseCanvasBetaLayout,
|
||||
};
|
||||
});
|
||||
|
||||
const UnifiedCanvasTab = () => {
|
||||
const { shouldUseCanvasBetaLayout } = useAppSelector(selector);
|
||||
|
||||
return (
|
||||
<Flex sx={{ gap: 4, w: 'full', h: 'full' }}>
|
||||
<ParametersPinnedWrapper>
|
||||
<UnifiedCanvasParameters />
|
||||
</ParametersPinnedWrapper>
|
||||
{shouldUseCanvasBetaLayout ? (
|
||||
<UnifiedCanvasContentBeta />
|
||||
) : (
|
||||
<UnifiedCanvasContent />
|
||||
)}
|
||||
<UnifiedCanvasContent />
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user