mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): controlnet/image dnd wip
Implement `dnd-kit` for image drag and drop - vastly simplifies logic bc we can drag and drop non-serializable data (like an `ImageDTO`) - also much prettier - also will fix conflicts with file upload via OS drag and drop, bc `dnd-kit` does not use native HTML drag and drop API - Implemented for Init image, controlnet, and node editor so far More progress on the ControlNet UI
This commit is contained in:
parent
e2e07696fc
commit
3b9426eb72
@ -60,6 +60,7 @@
|
|||||||
"@chakra-ui/styled-system": "^2.9.0",
|
"@chakra-ui/styled-system": "^2.9.0",
|
||||||
"@chakra-ui/theme-tools": "^2.0.16",
|
"@chakra-ui/theme-tools": "^2.0.16",
|
||||||
"@dagrejs/graphlib": "^2.1.12",
|
"@dagrejs/graphlib": "^2.1.12",
|
||||||
|
"@dnd-kit/core": "^6.0.8",
|
||||||
"@emotion/react": "^11.10.6",
|
"@emotion/react": "^11.10.6",
|
||||||
"@emotion/styled": "^11.10.6",
|
"@emotion/styled": "^11.10.6",
|
||||||
"@floating-ui/react-dom": "^2.0.0",
|
"@floating-ui/react-dom": "^2.0.0",
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
import {
|
||||||
|
DndContext,
|
||||||
|
DragEndEvent,
|
||||||
|
DragOverlay,
|
||||||
|
DragStartEvent,
|
||||||
|
} from '@dnd-kit/core';
|
||||||
|
import { PropsWithChildren, memo, useCallback, useState } from 'react';
|
||||||
|
import OverlayDragImage from './OverlayDragImage';
|
||||||
|
import { ImageDTO } from 'services/api';
|
||||||
|
import { isImageDTO } from 'services/types/guards';
|
||||||
|
|
||||||
|
type ImageDndContextProps = PropsWithChildren;
|
||||||
|
|
||||||
|
const ImageDndContext = (props: ImageDndContextProps) => {
|
||||||
|
const [draggedImage, setDraggedImage] = useState<ImageDTO | null>(null);
|
||||||
|
|
||||||
|
const handleDragStart = useCallback((event: DragStartEvent) => {
|
||||||
|
const dragData = event.active.data.current;
|
||||||
|
if (dragData && 'image' in dragData && isImageDTO(dragData.image)) {
|
||||||
|
setDraggedImage(dragData.image);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleDragEnd = useCallback(
|
||||||
|
(event: DragEndEvent) => {
|
||||||
|
const handleDrop = event.over?.data.current?.handleDrop;
|
||||||
|
if (handleDrop && typeof handleDrop === 'function' && draggedImage) {
|
||||||
|
handleDrop(draggedImage);
|
||||||
|
}
|
||||||
|
setDraggedImage(null);
|
||||||
|
},
|
||||||
|
[draggedImage]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DndContext onDragStart={handleDragStart} onDragEnd={handleDragEnd}>
|
||||||
|
{props.children}
|
||||||
|
<DragOverlay dropAnimation={null}>
|
||||||
|
{draggedImage && <OverlayDragImage image={draggedImage} />}
|
||||||
|
</DragOverlay>
|
||||||
|
</DndContext>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(ImageDndContext);
|
@ -0,0 +1,23 @@
|
|||||||
|
import { Image } from '@chakra-ui/react';
|
||||||
|
import { memo } from 'react';
|
||||||
|
import { ImageDTO } from 'services/api';
|
||||||
|
|
||||||
|
type OverlayDragImageProps = {
|
||||||
|
image: ImageDTO;
|
||||||
|
};
|
||||||
|
|
||||||
|
const OverlayDragImage = (props: OverlayDragImageProps) => {
|
||||||
|
return (
|
||||||
|
<Image
|
||||||
|
sx={{
|
||||||
|
maxW: 32,
|
||||||
|
maxH: 32,
|
||||||
|
borderRadius: 'base',
|
||||||
|
shadow: 'dark-lg',
|
||||||
|
}}
|
||||||
|
src={props.image.thumbnail_url}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(OverlayDragImage);
|
@ -16,6 +16,7 @@ import { PartialAppConfig } from 'app/types/invokeai';
|
|||||||
import '../../i18n';
|
import '../../i18n';
|
||||||
import { socketMiddleware } from 'services/events/middleware';
|
import { socketMiddleware } from 'services/events/middleware';
|
||||||
import { Middleware } from '@reduxjs/toolkit';
|
import { Middleware } from '@reduxjs/toolkit';
|
||||||
|
import ImageDndContext from './ImageDnd/ImageDndContext';
|
||||||
|
|
||||||
const App = lazy(() => import('./App'));
|
const App = lazy(() => import('./App'));
|
||||||
const ThemeLocaleProvider = lazy(() => import('./ThemeLocaleProvider'));
|
const ThemeLocaleProvider = lazy(() => import('./ThemeLocaleProvider'));
|
||||||
@ -69,11 +70,13 @@ const InvokeAIUI = ({
|
|||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<React.Suspense fallback={<Loading />}>
|
<React.Suspense fallback={<Loading />}>
|
||||||
<ThemeLocaleProvider>
|
<ThemeLocaleProvider>
|
||||||
<App
|
<ImageDndContext>
|
||||||
config={config}
|
<App
|
||||||
headerComponent={headerComponent}
|
config={config}
|
||||||
setIsReady={setIsReady}
|
headerComponent={headerComponent}
|
||||||
/>
|
setIsReady={setIsReady}
|
||||||
|
/>
|
||||||
|
</ImageDndContext>
|
||||||
</ThemeLocaleProvider>
|
</ThemeLocaleProvider>
|
||||||
</React.Suspense>
|
</React.Suspense>
|
||||||
</Provider>
|
</Provider>
|
||||||
|
@ -2,12 +2,10 @@ import { initialImageChanged } from 'features/parameters/store/generationSlice';
|
|||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import { addToast } from 'features/system/store/systemSlice';
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
import { startAppListening } from '..';
|
import { startAppListening } from '..';
|
||||||
import {
|
import { initialImageSelected } from 'features/parameters/store/actions';
|
||||||
initialImageSelected,
|
|
||||||
isImageDTO,
|
|
||||||
} from 'features/parameters/store/actions';
|
|
||||||
import { makeToast } from 'app/components/Toaster';
|
import { makeToast } from 'app/components/Toaster';
|
||||||
import { selectImagesById } from 'features/gallery/store/imagesSlice';
|
import { selectImagesById } from 'features/gallery/store/imagesSlice';
|
||||||
|
import { isImageDTO } from 'services/types/guards';
|
||||||
|
|
||||||
export const addInitialImageSelectedListener = () => {
|
export const addInitialImageSelectedListener = () => {
|
||||||
startAppListening({
|
startAppListening({
|
||||||
|
@ -13,6 +13,7 @@ import galleryReducer from 'features/gallery/store/gallerySlice';
|
|||||||
import imagesReducer from 'features/gallery/store/imagesSlice';
|
import imagesReducer from 'features/gallery/store/imagesSlice';
|
||||||
import lightboxReducer from 'features/lightbox/store/lightboxSlice';
|
import lightboxReducer from 'features/lightbox/store/lightboxSlice';
|
||||||
import generationReducer from 'features/parameters/store/generationSlice';
|
import generationReducer from 'features/parameters/store/generationSlice';
|
||||||
|
import controlNetReducer from 'features/controlNet/store/controlNetSlice';
|
||||||
import postprocessingReducer from 'features/parameters/store/postprocessingSlice';
|
import postprocessingReducer from 'features/parameters/store/postprocessingSlice';
|
||||||
import systemReducer from 'features/system/store/systemSlice';
|
import systemReducer from 'features/system/store/systemSlice';
|
||||||
// import sessionReducer from 'features/system/store/sessionSlice';
|
// import sessionReducer from 'features/system/store/sessionSlice';
|
||||||
@ -45,6 +46,7 @@ const allReducers = {
|
|||||||
ui: uiReducer,
|
ui: uiReducer,
|
||||||
hotkeys: hotkeysReducer,
|
hotkeys: hotkeysReducer,
|
||||||
images: imagesReducer,
|
images: imagesReducer,
|
||||||
|
controlNet: controlNetReducer,
|
||||||
// session: sessionReducer,
|
// session: sessionReducer,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -62,6 +64,7 @@ const rememberedKeys: (keyof typeof allReducers)[] = [
|
|||||||
'postprocessing',
|
'postprocessing',
|
||||||
'system',
|
'system',
|
||||||
'ui',
|
'ui',
|
||||||
|
'controlNet',
|
||||||
// 'hotkeys',
|
// 'hotkeys',
|
||||||
// 'config',
|
// 'config',
|
||||||
];
|
];
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
import { Checkbox, CheckboxProps } from '@chakra-ui/react';
|
|
||||||
import { memo, ReactNode } from 'react';
|
|
||||||
|
|
||||||
type IAICheckboxProps = CheckboxProps & {
|
|
||||||
label: string | ReactNode;
|
|
||||||
};
|
|
||||||
|
|
||||||
const IAICheckbox = (props: IAICheckboxProps) => {
|
|
||||||
const { label, ...rest } = props;
|
|
||||||
return (
|
|
||||||
<Checkbox colorScheme="accent" {...rest}>
|
|
||||||
{label}
|
|
||||||
</Checkbox>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(IAICheckbox);
|
|
@ -0,0 +1,25 @@
|
|||||||
|
import {
|
||||||
|
Checkbox,
|
||||||
|
CheckboxProps,
|
||||||
|
FormControl,
|
||||||
|
FormControlProps,
|
||||||
|
FormLabel,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import { memo, ReactNode } from 'react';
|
||||||
|
|
||||||
|
type IAIFullCheckboxProps = CheckboxProps & {
|
||||||
|
label: string | ReactNode;
|
||||||
|
formControlProps?: FormControlProps;
|
||||||
|
};
|
||||||
|
|
||||||
|
const IAIFullCheckbox = (props: IAIFullCheckboxProps) => {
|
||||||
|
const { label, formControlProps, ...rest } = props;
|
||||||
|
return (
|
||||||
|
<FormControl {...formControlProps}>
|
||||||
|
<FormLabel>{label}</FormLabel>
|
||||||
|
<Checkbox colorScheme="accent" {...rest} />
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(IAIFullCheckbox);
|
@ -0,0 +1,19 @@
|
|||||||
|
import { Checkbox, CheckboxProps, Text } from '@chakra-ui/react';
|
||||||
|
import { memo, ReactNode } from 'react';
|
||||||
|
|
||||||
|
type IAISimpleCheckboxProps = CheckboxProps & {
|
||||||
|
label: string | ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
const IAISimpleCheckbox = (props: IAISimpleCheckboxProps) => {
|
||||||
|
const { label, ...rest } = props;
|
||||||
|
return (
|
||||||
|
<Checkbox colorScheme="accent" {...rest}>
|
||||||
|
<Text color="base.200" fontSize="md">
|
||||||
|
{label}
|
||||||
|
</Text>
|
||||||
|
</Checkbox>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(IAISimpleCheckbox);
|
@ -23,9 +23,9 @@ const ImageMetadataOverlay = ({ image }: ImageMetadataOverlayProps) => {
|
|||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: 0,
|
top: 0,
|
||||||
right: 0,
|
insetInlineStart: 0,
|
||||||
p: 2,
|
p: 2,
|
||||||
alignItems: 'flex-end',
|
alignItems: 'flex-start',
|
||||||
gap: 2,
|
gap: 2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -2,7 +2,7 @@ import { ButtonGroup, Flex } from '@chakra-ui/react';
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIButton from 'common/components/IAIButton';
|
import IAIButton from 'common/components/IAIButton';
|
||||||
import IAICheckbox from 'common/components/IAICheckbox';
|
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
|
||||||
import IAIColorPicker from 'common/components/IAIColorPicker';
|
import IAIColorPicker from 'common/components/IAIColorPicker';
|
||||||
import IAIIconButton from 'common/components/IAIIconButton';
|
import IAIIconButton from 'common/components/IAIIconButton';
|
||||||
import IAIPopover from 'common/components/IAIPopover';
|
import IAIPopover from 'common/components/IAIPopover';
|
||||||
@ -117,12 +117,12 @@ const IAICanvasMaskOptions = () => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Flex direction="column" gap={2}>
|
<Flex direction="column" gap={2}>
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={`${t('unifiedCanvas.enableMask')} (H)`}
|
label={`${t('unifiedCanvas.enableMask')} (H)`}
|
||||||
isChecked={isMaskEnabled}
|
isChecked={isMaskEnabled}
|
||||||
onChange={handleToggleEnableMask}
|
onChange={handleToggleEnableMask}
|
||||||
/>
|
/>
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={t('unifiedCanvas.preserveMaskedArea')}
|
label={t('unifiedCanvas.preserveMaskedArea')}
|
||||||
isChecked={shouldPreserveMaskedArea}
|
isChecked={shouldPreserveMaskedArea}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Flex } from '@chakra-ui/react';
|
import { Flex } from '@chakra-ui/react';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAICheckbox from 'common/components/IAICheckbox';
|
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
|
||||||
import IAIIconButton from 'common/components/IAIIconButton';
|
import IAIIconButton from 'common/components/IAIIconButton';
|
||||||
import IAIPopover from 'common/components/IAIPopover';
|
import IAIPopover from 'common/components/IAIPopover';
|
||||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||||
@ -102,50 +102,50 @@ const IAICanvasSettingsButtonPopover = () => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Flex direction="column" gap={2}>
|
<Flex direction="column" gap={2}>
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={t('unifiedCanvas.showIntermediates')}
|
label={t('unifiedCanvas.showIntermediates')}
|
||||||
isChecked={shouldShowIntermediates}
|
isChecked={shouldShowIntermediates}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
dispatch(setShouldShowIntermediates(e.target.checked))
|
dispatch(setShouldShowIntermediates(e.target.checked))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={t('unifiedCanvas.showGrid')}
|
label={t('unifiedCanvas.showGrid')}
|
||||||
isChecked={shouldShowGrid}
|
isChecked={shouldShowGrid}
|
||||||
onChange={(e) => dispatch(setShouldShowGrid(e.target.checked))}
|
onChange={(e) => dispatch(setShouldShowGrid(e.target.checked))}
|
||||||
/>
|
/>
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={t('unifiedCanvas.snapToGrid')}
|
label={t('unifiedCanvas.snapToGrid')}
|
||||||
isChecked={shouldSnapToGrid}
|
isChecked={shouldSnapToGrid}
|
||||||
onChange={handleChangeShouldSnapToGrid}
|
onChange={handleChangeShouldSnapToGrid}
|
||||||
/>
|
/>
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={t('unifiedCanvas.darkenOutsideSelection')}
|
label={t('unifiedCanvas.darkenOutsideSelection')}
|
||||||
isChecked={shouldDarkenOutsideBoundingBox}
|
isChecked={shouldDarkenOutsideBoundingBox}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
dispatch(setShouldDarkenOutsideBoundingBox(e.target.checked))
|
dispatch(setShouldDarkenOutsideBoundingBox(e.target.checked))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={t('unifiedCanvas.autoSaveToGallery')}
|
label={t('unifiedCanvas.autoSaveToGallery')}
|
||||||
isChecked={shouldAutoSave}
|
isChecked={shouldAutoSave}
|
||||||
onChange={(e) => dispatch(setShouldAutoSave(e.target.checked))}
|
onChange={(e) => dispatch(setShouldAutoSave(e.target.checked))}
|
||||||
/>
|
/>
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={t('unifiedCanvas.saveBoxRegionOnly')}
|
label={t('unifiedCanvas.saveBoxRegionOnly')}
|
||||||
isChecked={shouldCropToBoundingBoxOnSave}
|
isChecked={shouldCropToBoundingBoxOnSave}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
dispatch(setShouldCropToBoundingBoxOnSave(e.target.checked))
|
dispatch(setShouldCropToBoundingBoxOnSave(e.target.checked))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={t('unifiedCanvas.limitStrokesToBox')}
|
label={t('unifiedCanvas.limitStrokesToBox')}
|
||||||
isChecked={shouldRestrictStrokesToBox}
|
isChecked={shouldRestrictStrokesToBox}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
dispatch(setShouldRestrictStrokesToBox(e.target.checked))
|
dispatch(setShouldRestrictStrokesToBox(e.target.checked))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={t('unifiedCanvas.showCanvasDebugInfo')}
|
label={t('unifiedCanvas.showCanvasDebugInfo')}
|
||||||
isChecked={shouldShowCanvasDebugInfo}
|
isChecked={shouldShowCanvasDebugInfo}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
@ -153,7 +153,7 @@ const IAICanvasSettingsButtonPopover = () => {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={t('unifiedCanvas.antialiasing')}
|
label={t('unifiedCanvas.antialiasing')}
|
||||||
isChecked={shouldAntialias}
|
isChecked={shouldAntialias}
|
||||||
onChange={(e) => dispatch(setShouldAntialias(e.target.checked))}
|
onChange={(e) => dispatch(setShouldAntialias(e.target.checked))}
|
||||||
|
@ -1,7 +1,32 @@
|
|||||||
import { memo } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { ControlNetProcessorNode } from '../store/types';
|
import { ControlNetProcessorNode } from '../store/types';
|
||||||
import { ImageDTO } from 'services/api';
|
import { ImageDTO } from 'services/api';
|
||||||
import CannyProcessor from './processors/CannyProcessor';
|
import CannyProcessor from './processors/CannyProcessor';
|
||||||
|
import {
|
||||||
|
ControlNet,
|
||||||
|
ControlNetModel,
|
||||||
|
controlNetBeginStepPctChanged,
|
||||||
|
controlNetEndStepPctChanged,
|
||||||
|
controlNetImageChanged,
|
||||||
|
controlNetModelChanged,
|
||||||
|
controlNetProcessedImageChanged,
|
||||||
|
controlNetRemoved,
|
||||||
|
controlNetToggled,
|
||||||
|
controlNetWeightChanged,
|
||||||
|
isControlNetImageProcessedToggled,
|
||||||
|
} from '../store/controlNetSlice';
|
||||||
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
|
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
|
||||||
|
import IAISlider from 'common/components/IAISlider';
|
||||||
|
import ParamControlNetIsEnabled from './parameters/ParamControlNetIsEnabled';
|
||||||
|
import ParamControlNetModel from './parameters/ParamControlNetModel';
|
||||||
|
import ParamControlNetWeight from './parameters/ParamControlNetWeight';
|
||||||
|
import ParamControlNetBeginStepPct from './parameters/ParamControlNetBeginStepPct';
|
||||||
|
import ParamControlNetEndStepPct from './parameters/ParamControlNetEndStepPct';
|
||||||
|
import { Flex, HStack, VStack } from '@chakra-ui/react';
|
||||||
|
import IAISelectableImage from './parameters/IAISelectableImage';
|
||||||
|
import IAIButton from 'common/components/IAIButton';
|
||||||
|
import IAIIconButton from 'common/components/IAIIconButton';
|
||||||
|
|
||||||
export type ControlNetProcessorProps = {
|
export type ControlNetProcessorProps = {
|
||||||
controlNetId: string;
|
controlNetId: string;
|
||||||
@ -16,11 +41,83 @@ const renderProcessorComponent = (props: ControlNetProcessorProps) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const ControlNet = () => {
|
type ControlNetProps = {
|
||||||
|
controlNet: ControlNet;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ControlNet = (props: ControlNetProps) => {
|
||||||
|
const {
|
||||||
|
controlNetId,
|
||||||
|
isEnabled,
|
||||||
|
model,
|
||||||
|
weight,
|
||||||
|
beginStepPct,
|
||||||
|
endStepPct,
|
||||||
|
controlImage,
|
||||||
|
isControlImageProcessed,
|
||||||
|
processedControlImage,
|
||||||
|
} = props.controlNet;
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const handleControlImageChanged = useCallback(
|
||||||
|
(controlImage: ImageDTO) => {
|
||||||
|
dispatch(controlNetImageChanged({ controlNetId, controlImage }));
|
||||||
|
},
|
||||||
|
[controlNetId, dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleControlImageReset = useCallback(() => {
|
||||||
|
dispatch(controlNetImageChanged({ controlNetId, controlImage: null }));
|
||||||
|
}, [controlNetId, dispatch]);
|
||||||
|
|
||||||
|
const handleControlNetRemoved = useCallback(() => {
|
||||||
|
dispatch(controlNetRemoved(controlNetId));
|
||||||
|
}, [controlNetId, dispatch]);
|
||||||
|
|
||||||
|
const handleIsControlImageProcessedToggled = useCallback(() => {
|
||||||
|
dispatch(
|
||||||
|
isControlNetImageProcessedToggled({
|
||||||
|
controlNetId,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}, [controlNetId, dispatch]);
|
||||||
|
|
||||||
|
const handleProcessedControlImageChanged = useCallback(
|
||||||
|
(processedControlImage: ImageDTO | null) => {
|
||||||
|
dispatch(
|
||||||
|
controlNetProcessedImageChanged({
|
||||||
|
controlNetId,
|
||||||
|
processedControlImage,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[controlNetId, dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<Flex sx={{ flexDir: 'column', gap: 3, pb: 4 }}>
|
||||||
<h1>ControlNet</h1>
|
<IAIButton onClick={handleControlNetRemoved}>Remove ControlNet</IAIButton>
|
||||||
</div>
|
<IAISelectableImage
|
||||||
|
image={controlImage}
|
||||||
|
onChange={handleControlImageChanged}
|
||||||
|
onReset={handleControlImageReset}
|
||||||
|
resetIconSize="sm"
|
||||||
|
/>
|
||||||
|
<ParamControlNetModel controlNetId={controlNetId} model={model} />
|
||||||
|
<ParamControlNetIsEnabled
|
||||||
|
controlNetId={controlNetId}
|
||||||
|
isEnabled={isEnabled}
|
||||||
|
/>
|
||||||
|
<ParamControlNetWeight controlNetId={controlNetId} weight={weight} />
|
||||||
|
<ParamControlNetBeginStepPct
|
||||||
|
controlNetId={controlNetId}
|
||||||
|
beginStepPct={beginStepPct}
|
||||||
|
/>
|
||||||
|
<ParamControlNetEndStepPct
|
||||||
|
controlNetId={controlNetId}
|
||||||
|
endStepPct={endStepPct}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -0,0 +1,200 @@
|
|||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Flex,
|
||||||
|
Icon,
|
||||||
|
IconButtonProps,
|
||||||
|
Image,
|
||||||
|
Text,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import { useDroppable } from '@dnd-kit/core';
|
||||||
|
import IAIIconButton from 'common/components/IAIIconButton';
|
||||||
|
import ImageMetadataOverlay from 'common/components/ImageMetadataOverlay';
|
||||||
|
import { useGetUrl } from 'common/util/getUrl';
|
||||||
|
import ImageFallbackSpinner from 'features/gallery/components/ImageFallbackSpinner';
|
||||||
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
|
import { SyntheticEvent } from 'react';
|
||||||
|
import { memo, useRef } from 'react';
|
||||||
|
import { FaImage, FaUndo } from 'react-icons/fa';
|
||||||
|
import { ImageDTO } from 'services/api';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
type IAISelectableImageProps = {
|
||||||
|
image: ImageDTO | null | undefined;
|
||||||
|
onChange: (image: ImageDTO) => void;
|
||||||
|
onReset?: () => void;
|
||||||
|
onError?: (event: SyntheticEvent<HTMLImageElement>) => void;
|
||||||
|
resetIconSize?: IconButtonProps['size'];
|
||||||
|
};
|
||||||
|
|
||||||
|
const IAISelectableImage = (props: IAISelectableImageProps) => {
|
||||||
|
const { image, onChange, onReset, onError, resetIconSize = 'md' } = props;
|
||||||
|
const droppableId = useRef(uuidv4());
|
||||||
|
const { getUrl } = useGetUrl();
|
||||||
|
const { isOver, setNodeRef, active } = useDroppable({
|
||||||
|
id: droppableId.current,
|
||||||
|
data: {
|
||||||
|
handleDrop: onChange,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
sx={{
|
||||||
|
width: 'full',
|
||||||
|
height: 'full',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
position: 'relative',
|
||||||
|
}}
|
||||||
|
ref={setNodeRef}
|
||||||
|
>
|
||||||
|
{image && (
|
||||||
|
<Flex sx={{ position: 'relative' }}>
|
||||||
|
<Image
|
||||||
|
src={getUrl(image.image_url)}
|
||||||
|
fallbackStrategy="beforeLoadOrError"
|
||||||
|
fallback={<ImageFallbackSpinner />}
|
||||||
|
onError={onError}
|
||||||
|
sx={{
|
||||||
|
borderRadius: 'base',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<ImageMetadataOverlay image={image} />
|
||||||
|
<AnimatePresence>
|
||||||
|
{active && <DropOverlay isOver={isOver} />}
|
||||||
|
</AnimatePresence>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
{!image && (
|
||||||
|
<>
|
||||||
|
<Flex
|
||||||
|
sx={{
|
||||||
|
p: 8,
|
||||||
|
bg: 'base.850',
|
||||||
|
w: 'full',
|
||||||
|
h: 'full',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
borderRadius: 'base',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
as={FaImage}
|
||||||
|
sx={{
|
||||||
|
boxSize: 24,
|
||||||
|
color: 'base.500',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
<AnimatePresence>
|
||||||
|
{active && <DropOverlay isOver={isOver} />}
|
||||||
|
</AnimatePresence>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{image && onReset && (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
p: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IAIIconButton
|
||||||
|
size={resetIconSize}
|
||||||
|
aria-label="Reset Image"
|
||||||
|
icon={<FaUndo />}
|
||||||
|
onClick={onReset}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(IAISelectableImage);
|
||||||
|
|
||||||
|
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: isOver ? 0.9 : 0.7,
|
||||||
|
borderRadius: 'base',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
transitionProperty: 'common',
|
||||||
|
transitionDuration: '0.15s',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Flex
|
||||||
|
sx={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
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.50' }}>
|
||||||
|
Drop Image
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
<Flex
|
||||||
|
sx={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
w: 'full',
|
||||||
|
h: 'full',
|
||||||
|
opacity: isOver ? 1 : 0.7,
|
||||||
|
borderWidth: 2,
|
||||||
|
borderColor: 'base.500',
|
||||||
|
borderRadius: 'base',
|
||||||
|
borderStyle: 'dashed',
|
||||||
|
transitionProperty: 'common',
|
||||||
|
transitionDuration: '0.15s',
|
||||||
|
}}
|
||||||
|
></Flex>
|
||||||
|
</Flex>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,44 @@
|
|||||||
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
|
import IAISlider from 'common/components/IAISlider';
|
||||||
|
import { controlNetBeginStepPctChanged } from 'features/controlNet/store/controlNetSlice';
|
||||||
|
import { memo, useCallback } from 'react';
|
||||||
|
|
||||||
|
type ParamControlNetBeginStepPctProps = {
|
||||||
|
controlNetId: string;
|
||||||
|
beginStepPct: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ParamControlNetBeginStepPct = (
|
||||||
|
props: ParamControlNetBeginStepPctProps
|
||||||
|
) => {
|
||||||
|
const { controlNetId, beginStepPct } = props;
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const handleBeginStepPctChanged = useCallback(
|
||||||
|
(beginStepPct: number) => {
|
||||||
|
dispatch(controlNetBeginStepPctChanged({ controlNetId, beginStepPct }));
|
||||||
|
},
|
||||||
|
[controlNetId, dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleBeginStepPctReset = () => {
|
||||||
|
dispatch(controlNetBeginStepPctChanged({ controlNetId, beginStepPct: 0 }));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IAISlider
|
||||||
|
label="Begin Step %"
|
||||||
|
value={beginStepPct}
|
||||||
|
onChange={handleBeginStepPctChanged}
|
||||||
|
withInput
|
||||||
|
withReset
|
||||||
|
handleReset={handleBeginStepPctReset}
|
||||||
|
withSliderMarks
|
||||||
|
min={0}
|
||||||
|
max={1}
|
||||||
|
step={0.01}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(ParamControlNetBeginStepPct);
|
@ -0,0 +1,42 @@
|
|||||||
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
|
import IAISlider from 'common/components/IAISlider';
|
||||||
|
import { controlNetEndStepPctChanged } from 'features/controlNet/store/controlNetSlice';
|
||||||
|
import { memo, useCallback } from 'react';
|
||||||
|
|
||||||
|
type ParamControlNetEndStepPctProps = {
|
||||||
|
controlNetId: string;
|
||||||
|
endStepPct: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ParamControlNetEndStepPct = (props: ParamControlNetEndStepPctProps) => {
|
||||||
|
const { controlNetId, endStepPct } = props;
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const handleEndStepPctChanged = useCallback(
|
||||||
|
(endStepPct: number) => {
|
||||||
|
dispatch(controlNetEndStepPctChanged({ controlNetId, endStepPct }));
|
||||||
|
},
|
||||||
|
[controlNetId, dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleEndStepPctReset = () => {
|
||||||
|
dispatch(controlNetEndStepPctChanged({ controlNetId, endStepPct: 0 }));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IAISlider
|
||||||
|
label="End Step %"
|
||||||
|
value={endStepPct}
|
||||||
|
onChange={handleEndStepPctChanged}
|
||||||
|
withInput
|
||||||
|
withReset
|
||||||
|
handleReset={handleEndStepPctReset}
|
||||||
|
withSliderMarks
|
||||||
|
min={0}
|
||||||
|
max={1}
|
||||||
|
step={0.01}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(ParamControlNetEndStepPct);
|
@ -0,0 +1,28 @@
|
|||||||
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
|
import IAIFullCheckbox from 'common/components/IAIFullCheckbox';
|
||||||
|
import { controlNetToggled } from 'features/controlNet/store/controlNetSlice';
|
||||||
|
import { memo, useCallback } from 'react';
|
||||||
|
|
||||||
|
type ParamControlNetIsEnabledProps = {
|
||||||
|
controlNetId: string;
|
||||||
|
isEnabled: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ParamControlNetIsEnabled = (props: ParamControlNetIsEnabledProps) => {
|
||||||
|
const { controlNetId, isEnabled } = props;
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const handleIsEnabledChanged = useCallback(() => {
|
||||||
|
dispatch(controlNetToggled(controlNetId));
|
||||||
|
}, [dispatch, controlNetId]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IAIFullCheckbox
|
||||||
|
label="Enabled"
|
||||||
|
isChecked={isEnabled}
|
||||||
|
onChange={handleIsEnabledChanged}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(ParamControlNetIsEnabled);
|
@ -0,0 +1,38 @@
|
|||||||
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
|
import IAICustomSelect from 'common/components/IAICustomSelect';
|
||||||
|
import {
|
||||||
|
CONTROLNET_MODELS,
|
||||||
|
ControlNetModel,
|
||||||
|
controlNetModelChanged,
|
||||||
|
} from 'features/controlNet/store/controlNetSlice';
|
||||||
|
import { memo, useCallback } from 'react';
|
||||||
|
|
||||||
|
type ParamIsControlNetModelProps = {
|
||||||
|
controlNetId: string;
|
||||||
|
model: ControlNetModel;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ParamIsControlNetModel = (props: ParamIsControlNetModelProps) => {
|
||||||
|
const { controlNetId, model } = props;
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const handleModelChanged = useCallback(
|
||||||
|
(val: string | null | undefined) => {
|
||||||
|
// TODO: do not cast
|
||||||
|
const model = val as ControlNetModel;
|
||||||
|
dispatch(controlNetModelChanged({ controlNetId, model }));
|
||||||
|
},
|
||||||
|
[controlNetId, dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IAICustomSelect
|
||||||
|
label="Model"
|
||||||
|
items={CONTROLNET_MODELS}
|
||||||
|
selectedItem={model}
|
||||||
|
setSelectedItem={handleModelChanged}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(ParamIsControlNetModel);
|
@ -0,0 +1,42 @@
|
|||||||
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
|
import IAISlider from 'common/components/IAISlider';
|
||||||
|
import { controlNetWeightChanged } from 'features/controlNet/store/controlNetSlice';
|
||||||
|
import { memo, useCallback } from 'react';
|
||||||
|
|
||||||
|
type ParamControlNetWeightProps = {
|
||||||
|
controlNetId: string;
|
||||||
|
weight: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ParamControlNetWeight = (props: ParamControlNetWeightProps) => {
|
||||||
|
const { controlNetId, weight } = props;
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const handleWeightChanged = useCallback(
|
||||||
|
(weight: number) => {
|
||||||
|
dispatch(controlNetWeightChanged({ controlNetId, weight }));
|
||||||
|
},
|
||||||
|
[controlNetId, dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleWeightReset = () => {
|
||||||
|
dispatch(controlNetWeightChanged({ controlNetId, weight: 1 }));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IAISlider
|
||||||
|
label="Weight"
|
||||||
|
value={weight}
|
||||||
|
onChange={handleWeightChanged}
|
||||||
|
withInput
|
||||||
|
withReset
|
||||||
|
handleReset={handleWeightReset}
|
||||||
|
withSliderMarks
|
||||||
|
min={0}
|
||||||
|
max={1}
|
||||||
|
step={0.01}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(ParamControlNetWeight);
|
@ -4,8 +4,6 @@ import { memo, useCallback, useState } from 'react';
|
|||||||
import ControlNetProcessButton from './common/ControlNetProcessButton';
|
import ControlNetProcessButton from './common/ControlNetProcessButton';
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import { controlNetImageProcessed } from 'features/controlNet/store/actions';
|
import { controlNetImageProcessed } from 'features/controlNet/store/actions';
|
||||||
import { ImageDTO } from 'services/api';
|
|
||||||
import ControlNetProcessorImage from './common/ControlNetProcessorImage';
|
|
||||||
import { ControlNetProcessorProps } from '../ControlNet';
|
import { ControlNetProcessorProps } from '../ControlNet';
|
||||||
|
|
||||||
export const CANNY_PROCESSOR = 'canny_processor';
|
export const CANNY_PROCESSOR = 'canny_processor';
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
import { Flex, Image } from '@chakra-ui/react';
|
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
|
||||||
import { selectImagesById } from 'features/gallery/store/imagesSlice';
|
|
||||||
import { DragEvent, memo, useCallback } from 'react';
|
|
||||||
import { ImageDTO } from 'services/api';
|
|
||||||
|
|
||||||
type ControlNetProcessorImageProps = {
|
|
||||||
image: ImageDTO | undefined;
|
|
||||||
setImage: (image: ImageDTO) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ControlNetProcessorImage = (props: ControlNetProcessorImageProps) => {
|
|
||||||
const { image, setImage } = props;
|
|
||||||
const state = useAppSelector((state) => state);
|
|
||||||
const handleDrop = useCallback(
|
|
||||||
(e: DragEvent<HTMLDivElement>) => {
|
|
||||||
const name = e.dataTransfer.getData('invokeai/imageName');
|
|
||||||
const droppedImage = selectImagesById(state, name);
|
|
||||||
if (droppedImage) {
|
|
||||||
setImage(droppedImage);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[setImage, state]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!image) {
|
|
||||||
return <Flex onDrop={handleDrop}>Upload Image</Flex>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <Image src={image.image_url} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(ControlNetProcessorImage);
|
|
@ -1,5 +1,10 @@
|
|||||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
import {
|
||||||
|
$CombinedState,
|
||||||
|
PayloadAction,
|
||||||
|
createSelector,
|
||||||
|
} from '@reduxjs/toolkit';
|
||||||
import { createSlice } from '@reduxjs/toolkit';
|
import { createSlice } from '@reduxjs/toolkit';
|
||||||
|
import { RootState } from 'app/store/store';
|
||||||
import { ImageDTO } from 'services/api';
|
import { ImageDTO } from 'services/api';
|
||||||
|
|
||||||
export const CONTROLNET_MODELS = [
|
export const CONTROLNET_MODELS = [
|
||||||
@ -11,22 +16,22 @@ export const CONTROLNET_MODELS = [
|
|||||||
'lllyasviel/sd-controlnet-scribble',
|
'lllyasviel/sd-controlnet-scribble',
|
||||||
'lllyasviel/sd-controlnet-normal',
|
'lllyasviel/sd-controlnet-normal',
|
||||||
'lllyasviel/sd-controlnet-mlsd',
|
'lllyasviel/sd-controlnet-mlsd',
|
||||||
] as const;
|
];
|
||||||
|
|
||||||
export const CONTROLNET_PROCESSORS = [
|
// export const CONTROLNET_PROCESSORS = [
|
||||||
'canny',
|
// 'canny',
|
||||||
'contentShuffle',
|
// 'contentShuffle',
|
||||||
'hed',
|
// 'hed',
|
||||||
'lineart',
|
// 'lineart',
|
||||||
'lineartAnime',
|
// 'lineartAnime',
|
||||||
'mediapipeFace',
|
// 'mediapipeFace',
|
||||||
'midasDepth',
|
// 'midasDepth',
|
||||||
'mlsd',
|
// 'mlsd',
|
||||||
'normalBae',
|
// 'normalBae',
|
||||||
'openpose',
|
// 'openpose',
|
||||||
'pidi',
|
// 'pidi',
|
||||||
'zoeDepth',
|
// 'zoeDepth',
|
||||||
] as const;
|
// ] as const;
|
||||||
|
|
||||||
export type ControlNetModel = (typeof CONTROLNET_MODELS)[number];
|
export type ControlNetModel = (typeof CONTROLNET_MODELS)[number];
|
||||||
|
|
||||||
@ -37,6 +42,7 @@ export const initialControlNet: Omit<ControlNet, 'controlNetId'> = {
|
|||||||
beginStepPct: 0,
|
beginStepPct: 0,
|
||||||
endStepPct: 1,
|
endStepPct: 1,
|
||||||
controlImage: null,
|
controlImage: null,
|
||||||
|
isControlImageProcessed: false,
|
||||||
processedControlImage: null,
|
processedControlImage: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -48,6 +54,7 @@ export type ControlNet = {
|
|||||||
beginStepPct: number;
|
beginStepPct: number;
|
||||||
endStepPct: number;
|
endStepPct: number;
|
||||||
controlImage: ImageDTO | null;
|
controlImage: ImageDTO | null;
|
||||||
|
isControlImageProcessed: boolean;
|
||||||
processedControlImage: ImageDTO | null;
|
processedControlImage: ImageDTO | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -63,15 +70,14 @@ export const controlNetSlice = createSlice({
|
|||||||
name: 'controlNet',
|
name: 'controlNet',
|
||||||
initialState: initialControlNetState,
|
initialState: initialControlNetState,
|
||||||
reducers: {
|
reducers: {
|
||||||
controlNetAddedFromModel: (
|
controlNetAdded: (
|
||||||
state,
|
state,
|
||||||
action: PayloadAction<{ controlNetId: string; model: ControlNetModel }>
|
action: PayloadAction<{ controlNetId: string }>
|
||||||
) => {
|
) => {
|
||||||
const { controlNetId, model } = action.payload;
|
const { controlNetId } = action.payload;
|
||||||
state.controlNets[controlNetId] = {
|
state.controlNets[controlNetId] = {
|
||||||
...initialControlNet,
|
...initialControlNet,
|
||||||
controlNetId,
|
controlNetId,
|
||||||
model,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
controlNetAddedFromImage: (
|
controlNetAddedFromImage: (
|
||||||
@ -96,11 +102,24 @@ export const controlNetSlice = createSlice({
|
|||||||
},
|
},
|
||||||
controlNetImageChanged: (
|
controlNetImageChanged: (
|
||||||
state,
|
state,
|
||||||
action: PayloadAction<{ controlNetId: string; controlImage: ImageDTO }>
|
action: PayloadAction<{
|
||||||
|
controlNetId: string;
|
||||||
|
controlImage: ImageDTO | null;
|
||||||
|
}>
|
||||||
) => {
|
) => {
|
||||||
const { controlNetId, controlImage } = action.payload;
|
const { controlNetId, controlImage } = action.payload;
|
||||||
state.controlNets[controlNetId].controlImage = controlImage;
|
state.controlNets[controlNetId].controlImage = controlImage;
|
||||||
},
|
},
|
||||||
|
isControlNetImageProcessedToggled: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{
|
||||||
|
controlNetId: string;
|
||||||
|
}>
|
||||||
|
) => {
|
||||||
|
const { controlNetId } = action.payload;
|
||||||
|
state.controlNets[controlNetId].isControlImageProcessed =
|
||||||
|
!state.controlNets[controlNetId].isControlImageProcessed;
|
||||||
|
},
|
||||||
controlNetProcessedImageChanged: (
|
controlNetProcessedImageChanged: (
|
||||||
state,
|
state,
|
||||||
action: PayloadAction<{
|
action: PayloadAction<{
|
||||||
@ -144,10 +163,11 @@ export const controlNetSlice = createSlice({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
controlNetAddedFromModel,
|
controlNetAdded,
|
||||||
controlNetAddedFromImage,
|
controlNetAddedFromImage,
|
||||||
controlNetRemoved,
|
controlNetRemoved,
|
||||||
controlNetImageChanged,
|
controlNetImageChanged,
|
||||||
|
isControlNetImageProcessedToggled,
|
||||||
controlNetProcessedImageChanged,
|
controlNetProcessedImageChanged,
|
||||||
controlNetToggled,
|
controlNetToggled,
|
||||||
controlNetModelChanged,
|
controlNetModelChanged,
|
||||||
@ -157,3 +177,5 @@ export const {
|
|||||||
} = controlNetSlice.actions;
|
} = controlNetSlice.actions;
|
||||||
|
|
||||||
export default controlNetSlice.reducer;
|
export default controlNetSlice.reducer;
|
||||||
|
|
||||||
|
export const controlNetSelector = (state: RootState) => state.controlNet;
|
||||||
|
@ -39,6 +39,8 @@ import {
|
|||||||
} from '../store/actions';
|
} from '../store/actions';
|
||||||
import { useAppToaster } from 'app/components/Toaster';
|
import { useAppToaster } from 'app/components/Toaster';
|
||||||
import { ImageDTO } from 'services/api';
|
import { ImageDTO } from 'services/api';
|
||||||
|
import { useDraggable } from '@dnd-kit/core';
|
||||||
|
import { CSS } from '@dnd-kit/utilities';
|
||||||
|
|
||||||
export const selector = createSelector(
|
export const selector = createSelector(
|
||||||
[gallerySelector, systemSelector, lightboxSelector, activeTabNameSelector],
|
[gallerySelector, systemSelector, lightboxSelector, activeTabNameSelector],
|
||||||
@ -117,6 +119,13 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
|||||||
const { recallBothPrompts, recallSeed, recallAllParameters } =
|
const { recallBothPrompts, recallSeed, recallAllParameters } =
|
||||||
useRecallParameters();
|
useRecallParameters();
|
||||||
|
|
||||||
|
const { attributes, listeners, setNodeRef } = useDraggable({
|
||||||
|
id: image_name,
|
||||||
|
data: {
|
||||||
|
image,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const handleMouseOver = () => setIsHovered(true);
|
const handleMouseOver = () => setIsHovered(true);
|
||||||
const handleMouseOut = () => setIsHovered(false);
|
const handleMouseOut = () => setIsHovered(false);
|
||||||
|
|
||||||
@ -212,7 +221,12 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Box
|
||||||
|
ref={setNodeRef}
|
||||||
|
{...listeners}
|
||||||
|
{...attributes}
|
||||||
|
sx={{ w: 'full', h: 'full' }}
|
||||||
|
>
|
||||||
<ContextMenu<HTMLDivElement>
|
<ContextMenu<HTMLDivElement>
|
||||||
menuProps={{ size: 'sm', isLazy: true }}
|
menuProps={{ size: 'sm', isLazy: true }}
|
||||||
renderMenu={() => (
|
renderMenu={() => (
|
||||||
@ -291,8 +305,8 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
|||||||
onMouseOver={handleMouseOver}
|
onMouseOver={handleMouseOver}
|
||||||
onMouseOut={handleMouseOut}
|
onMouseOut={handleMouseOut}
|
||||||
userSelect="none"
|
userSelect="none"
|
||||||
draggable={true}
|
// draggable={true}
|
||||||
onDragStart={handleDragStart}
|
// onDragStart={handleDragStart}
|
||||||
onClick={handleSelectImage}
|
onClick={handleSelectImage}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
sx={{
|
sx={{
|
||||||
@ -373,7 +387,7 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
|||||||
onClose={onDeleteDialogClose}
|
onClose={onDeleteDialogClose}
|
||||||
handleDelete={handleDelete}
|
handleDelete={handleDelete}
|
||||||
/>
|
/>
|
||||||
</>
|
</Box>
|
||||||
);
|
);
|
||||||
}, memoEqualityCheck);
|
}, memoEqualityCheck);
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIButton from 'common/components/IAIButton';
|
import IAIButton from 'common/components/IAIButton';
|
||||||
import IAICheckbox from 'common/components/IAICheckbox';
|
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
|
||||||
import IAIIconButton from 'common/components/IAIIconButton';
|
import IAIIconButton from 'common/components/IAIIconButton';
|
||||||
import IAIPopover from 'common/components/IAIPopover';
|
import IAIPopover from 'common/components/IAIPopover';
|
||||||
import IAISlider from 'common/components/IAISlider';
|
import IAISlider from 'common/components/IAISlider';
|
||||||
@ -233,7 +233,7 @@ const ImageGalleryContent = () => {
|
|||||||
withReset
|
withReset
|
||||||
handleReset={() => dispatch(setGalleryImageMinimumWidth(64))}
|
handleReset={() => dispatch(setGalleryImageMinimumWidth(64))}
|
||||||
/>
|
/>
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={t('gallery.maintainAspectRatio')}
|
label={t('gallery.maintainAspectRatio')}
|
||||||
isChecked={galleryImageObjectFit === 'contain'}
|
isChecked={galleryImageObjectFit === 'contain'}
|
||||||
onChange={() =>
|
onChange={() =>
|
||||||
@ -244,14 +244,14 @@ const ImageGalleryContent = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={t('gallery.autoSwitchNewImages')}
|
label={t('gallery.autoSwitchNewImages')}
|
||||||
isChecked={shouldAutoSwitchToNewImages}
|
isChecked={shouldAutoSwitchToNewImages}
|
||||||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||||
dispatch(setShouldAutoSwitchToNewImages(e.target.checked))
|
dispatch(setShouldAutoSwitchToNewImages(e.target.checked))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={t('gallery.singleColumnLayout')}
|
label={t('gallery.singleColumnLayout')}
|
||||||
isChecked={shouldUseSingleGalleryColumn}
|
isChecked={shouldUseSingleGalleryColumn}
|
||||||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||||
|
@ -1,39 +1,26 @@
|
|||||||
import { Box, Image } from '@chakra-ui/react';
|
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import SelectImagePlaceholder from 'common/components/SelectImagePlaceholder';
|
|
||||||
import { useGetUrl } from 'common/util/getUrl';
|
|
||||||
import useGetImageByName from 'features/gallery/hooks/useGetImageByName';
|
|
||||||
|
|
||||||
import { fieldValueChanged } from 'features/nodes/store/nodesSlice';
|
import { fieldValueChanged } from 'features/nodes/store/nodesSlice';
|
||||||
import {
|
import {
|
||||||
ImageInputFieldTemplate,
|
ImageInputFieldTemplate,
|
||||||
ImageInputFieldValue,
|
ImageInputFieldValue,
|
||||||
} from 'features/nodes/types/types';
|
} from 'features/nodes/types/types';
|
||||||
import { DragEvent, memo, useCallback, useState } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
|
|
||||||
import { FieldComponentProps } from './types';
|
import { FieldComponentProps } from './types';
|
||||||
|
import IAISelectableImage from 'features/controlNet/components/parameters/IAISelectableImage';
|
||||||
|
import { ImageDTO } from 'services/api';
|
||||||
|
import { Flex } from '@chakra-ui/react';
|
||||||
|
|
||||||
const ImageInputFieldComponent = (
|
const ImageInputFieldComponent = (
|
||||||
props: FieldComponentProps<ImageInputFieldValue, ImageInputFieldTemplate>
|
props: FieldComponentProps<ImageInputFieldValue, ImageInputFieldTemplate>
|
||||||
) => {
|
) => {
|
||||||
const { nodeId, field } = props;
|
const { nodeId, field } = props;
|
||||||
|
|
||||||
const getImageByName = useGetImageByName();
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const [url, setUrl] = useState<string | undefined>(field.value?.image_url);
|
|
||||||
const { getUrl } = useGetUrl();
|
|
||||||
|
|
||||||
const handleDrop = useCallback(
|
|
||||||
(e: DragEvent<HTMLDivElement>) => {
|
|
||||||
const name = e.dataTransfer.getData('invokeai/imageName');
|
|
||||||
const image = getImageByName(name);
|
|
||||||
|
|
||||||
if (!image) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setUrl(image.image_url);
|
|
||||||
|
|
||||||
|
const handleChange = useCallback(
|
||||||
|
(image: ImageDTO) => {
|
||||||
dispatch(
|
dispatch(
|
||||||
fieldValueChanged({
|
fieldValueChanged({
|
||||||
nodeId,
|
nodeId,
|
||||||
@ -42,13 +29,20 @@ const ImageInputFieldComponent = (
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[getImageByName, dispatch, field.name, nodeId]
|
[dispatch, field.name, nodeId]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box onDrop={handleDrop}>
|
<Flex
|
||||||
<Image src={getUrl(url)} fallback={<SelectImagePlaceholder />} />
|
sx={{
|
||||||
</Box>
|
w: 'full',
|
||||||
|
h: 'full',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IAISelectableImage image={field.value} onChange={handleChange} />
|
||||||
|
</Flex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,61 +1,59 @@
|
|||||||
import { Flex, Text, useDisclosure } from '@chakra-ui/react';
|
import { Flex, useDisclosure } from '@chakra-ui/react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import IAICollapse from 'common/components/IAICollapse';
|
import IAICollapse from 'common/components/IAICollapse';
|
||||||
import { memo, useCallback, useState } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import IAICustomSelect from 'common/components/IAICustomSelect';
|
|
||||||
import IAIIconButton from 'common/components/IAIIconButton';
|
import IAIIconButton from 'common/components/IAIIconButton';
|
||||||
import { FaPlus } from 'react-icons/fa';
|
import { FaPlus } from 'react-icons/fa';
|
||||||
import CannyProcessor from 'features/controlNet/components/processors/CannyProcessor';
|
|
||||||
import ControlNet from 'features/controlNet/components/ControlNet';
|
import ControlNet from 'features/controlNet/components/ControlNet';
|
||||||
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
|
import {
|
||||||
|
controlNetAdded,
|
||||||
|
controlNetSelector,
|
||||||
|
} from 'features/controlNet/store/controlNetSlice';
|
||||||
|
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||||
|
import { map } from 'lodash-es';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
const CONTROLNET_MODELS = [
|
const selector = createSelector(
|
||||||
'lllyasviel/sd-controlnet-canny',
|
controlNetSelector,
|
||||||
'lllyasviel/sd-controlnet-depth',
|
(controlNet) => {
|
||||||
'lllyasviel/sd-controlnet-hed',
|
const { controlNets } = controlNet;
|
||||||
'lllyasviel/sd-controlnet-seg',
|
|
||||||
'lllyasviel/sd-controlnet-openpose',
|
return { controlNets };
|
||||||
'lllyasviel/sd-controlnet-scribble',
|
},
|
||||||
'lllyasviel/sd-controlnet-normal',
|
defaultSelectorOptions
|
||||||
'lllyasviel/sd-controlnet-mlsd',
|
);
|
||||||
];
|
|
||||||
|
|
||||||
const ParamControlNetCollapse = () => {
|
const ParamControlNetCollapse = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { isOpen, onToggle } = useDisclosure();
|
const { isOpen, onToggle } = useDisclosure();
|
||||||
const [model, setModel] = useState<string>(CONTROLNET_MODELS[0]);
|
const { controlNets } = useAppSelector(selector);
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const handleSetControlNet = useCallback(
|
const handleClickedAddControlNet = useCallback(() => {
|
||||||
(model: string | null | undefined) => {
|
dispatch(controlNetAdded({ controlNetId: uuidv4() }));
|
||||||
if (model) {
|
}, [dispatch]);
|
||||||
setModel(model);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ControlNet />
|
<IAICollapse
|
||||||
// <IAICollapse
|
label={'ControlNet'}
|
||||||
// label={'ControlNet'}
|
// label={t('parameters.seamCorrectionHeader')}
|
||||||
// // label={t('parameters.seamCorrectionHeader')}
|
isOpen={isOpen}
|
||||||
// isOpen={isOpen}
|
onToggle={onToggle}
|
||||||
// onToggle={onToggle}
|
>
|
||||||
// >
|
<Flex sx={{ alignItems: 'flex-end' }}>
|
||||||
// <Flex sx={{ alignItems: 'flex-end' }}>
|
<IAIIconButton
|
||||||
// <IAICustomSelect
|
size="sm"
|
||||||
// label="ControlNet Model"
|
aria-label="Add ControlNet"
|
||||||
// items={CONTROLNET_MODELS}
|
onClick={handleClickedAddControlNet}
|
||||||
// selectedItem={model}
|
icon={<FaPlus />}
|
||||||
// setSelectedItem={handleSetControlNet}
|
/>
|
||||||
// />
|
</Flex>
|
||||||
// <IAIIconButton
|
{map(controlNets, (c) => (
|
||||||
// size="sm"
|
<ControlNet key={c.controlNetId} controlNet={c} />
|
||||||
// aria-label="Add ControlNet"
|
))}
|
||||||
// icon={<FaPlus />}
|
</IAICollapse>
|
||||||
// />
|
|
||||||
// </Flex>
|
|
||||||
// <CannyProcessor />
|
|
||||||
// </IAICollapse>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -28,7 +28,6 @@ const InitialImageDisplay = () => {
|
|||||||
gap: 4,
|
gap: 4,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<InitialImageButtons />
|
|
||||||
<InitialImagePreview />
|
<InitialImagePreview />
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -2,7 +2,10 @@ import { Flex, Icon, Image } from '@chakra-ui/react';
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { useGetUrl } from 'common/util/getUrl';
|
import { useGetUrl } from 'common/util/getUrl';
|
||||||
import { clearInitialImage } from 'features/parameters/store/generationSlice';
|
import {
|
||||||
|
clearInitialImage,
|
||||||
|
initialImageChanged,
|
||||||
|
} from 'features/parameters/store/generationSlice';
|
||||||
import { DragEvent, useCallback } from 'react';
|
import { DragEvent, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import ImageMetadataOverlay from 'common/components/ImageMetadataOverlay';
|
import ImageMetadataOverlay from 'common/components/ImageMetadataOverlay';
|
||||||
@ -13,6 +16,8 @@ import ImageFallbackSpinner from 'features/gallery/components/ImageFallbackSpinn
|
|||||||
import { FaImage } from 'react-icons/fa';
|
import { FaImage } from 'react-icons/fa';
|
||||||
import { configSelector } from '../../../../system/store/configSelectors';
|
import { configSelector } from '../../../../system/store/configSelectors';
|
||||||
import { useAppToaster } from 'app/components/Toaster';
|
import { useAppToaster } from 'app/components/Toaster';
|
||||||
|
import IAISelectableImage from 'features/controlNet/components/parameters/IAISelectableImage';
|
||||||
|
import { ImageDTO } from 'services/api';
|
||||||
|
|
||||||
const selector = createSelector(
|
const selector = createSelector(
|
||||||
[generationSelector],
|
[generationSelector],
|
||||||
@ -51,14 +56,17 @@ const InitialImagePreview = () => {
|
|||||||
}
|
}
|
||||||
}, [dispatch, t, toaster, shouldFetchImages]);
|
}, [dispatch, t, toaster, shouldFetchImages]);
|
||||||
|
|
||||||
const handleDrop = useCallback(
|
const handleChange = useCallback(
|
||||||
(e: DragEvent<HTMLDivElement>) => {
|
(image: ImageDTO) => {
|
||||||
const name = e.dataTransfer.getData('invokeai/imageName');
|
dispatch(initialImageChanged(image));
|
||||||
dispatch(initialImageSelected(name));
|
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleReset = useCallback(() => {
|
||||||
|
dispatch(clearInitialImage());
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
sx={{
|
sx={{
|
||||||
@ -68,9 +76,14 @@ const InitialImagePreview = () => {
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
}}
|
}}
|
||||||
onDrop={handleDrop}
|
// onDrop={handleDrop}
|
||||||
>
|
>
|
||||||
{initialImage?.image_url && (
|
<IAISelectableImage
|
||||||
|
image={initialImage}
|
||||||
|
onChange={handleChange}
|
||||||
|
onReset={handleReset}
|
||||||
|
/>
|
||||||
|
{/* {initialImage?.image_url && (
|
||||||
<>
|
<>
|
||||||
<Image
|
<Image
|
||||||
src={getUrl(initialImage?.image_url)}
|
src={getUrl(initialImage?.image_url)}
|
||||||
@ -97,7 +110,7 @@ const InitialImagePreview = () => {
|
|||||||
color: 'base.500',
|
color: 'base.500',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)} */}
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -7,25 +7,6 @@ export type ImageNameAndOrigin = {
|
|||||||
image_origin: ResourceOrigin;
|
image_origin: ResourceOrigin;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isImageDTO = (image: any): image is ImageDTO => {
|
|
||||||
return (
|
|
||||||
image &&
|
|
||||||
isObject(image) &&
|
|
||||||
'image_name' in image &&
|
|
||||||
image?.image_name !== undefined &&
|
|
||||||
'image_origin' in image &&
|
|
||||||
image?.image_origin !== undefined &&
|
|
||||||
'image_url' in image &&
|
|
||||||
image?.image_url !== undefined &&
|
|
||||||
'thumbnail_url' in image &&
|
|
||||||
image?.thumbnail_url !== undefined &&
|
|
||||||
'image_category' in image &&
|
|
||||||
image?.image_category !== undefined &&
|
|
||||||
'created_at' in image &&
|
|
||||||
image?.created_at !== undefined
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const initialImageSelected = createAction<ImageDTO | string | undefined>(
|
export const initialImageSelected = createAction<ImageDTO | string | undefined>(
|
||||||
'generation/initialImageSelected'
|
'generation/initialImageSelected'
|
||||||
);
|
);
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
|
|
||||||
import IAIButton from 'common/components/IAIButton';
|
import IAIButton from 'common/components/IAIButton';
|
||||||
import IAICheckbox from 'common/components/IAICheckbox';
|
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
|
||||||
import IAIInput from 'common/components/IAIInput';
|
import IAIInput from 'common/components/IAIInput';
|
||||||
import IAINumberInput from 'common/components/IAINumberInput';
|
import IAINumberInput from 'common/components/IAINumberInput';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@ -74,12 +74,12 @@ export default function AddCheckpointModel() {
|
|||||||
return (
|
return (
|
||||||
<VStack gap={2} alignItems="flex-start">
|
<VStack gap={2} alignItems="flex-start">
|
||||||
<Flex columnGap={4}>
|
<Flex columnGap={4}>
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
isChecked={!addManually}
|
isChecked={!addManually}
|
||||||
label={t('modelManager.scanForModels')}
|
label={t('modelManager.scanForModels')}
|
||||||
onChange={() => setAddmanually(!addManually)}
|
onChange={() => setAddmanually(!addManually)}
|
||||||
/>
|
/>
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={t('modelManager.addManually')}
|
label={t('modelManager.addManually')}
|
||||||
isChecked={addManually}
|
isChecked={addManually}
|
||||||
onChange={() => setAddmanually(!addManually)}
|
onChange={() => setAddmanually(!addManually)}
|
||||||
|
@ -24,7 +24,7 @@ import { useState } from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import * as InvokeAI from 'app/types/invokeai';
|
import * as InvokeAI from 'app/types/invokeai';
|
||||||
import IAISlider from 'common/components/IAISlider';
|
import IAISlider from 'common/components/IAISlider';
|
||||||
import IAICheckbox from 'common/components/IAICheckbox';
|
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
|
||||||
|
|
||||||
export default function MergeModels() {
|
export default function MergeModels() {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
@ -286,7 +286,7 @@ export default function MergeModels() {
|
|||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={t('modelManager.ignoreMismatch')}
|
label={t('modelManager.ignoreMismatch')}
|
||||||
isChecked={modelMergeForce}
|
isChecked={modelMergeForce}
|
||||||
onChange={(e) => setModelMergeForce(e.target.checked)}
|
onChange={(e) => setModelMergeForce(e.target.checked)}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import IAIButton from 'common/components/IAIButton';
|
import IAIButton from 'common/components/IAIButton';
|
||||||
import IAICheckbox from 'common/components/IAICheckbox';
|
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
|
||||||
import IAIIconButton from 'common/components/IAIIconButton';
|
import IAIIconButton from 'common/components/IAIIconButton';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
@ -81,13 +81,13 @@ function SearchModelEntry({
|
|||||||
borderRadius={4}
|
borderRadius={4}
|
||||||
>
|
>
|
||||||
<Flex gap={4} alignItems="center" justifyContent="space-between">
|
<Flex gap={4} alignItems="center" justifyContent="space-between">
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
value={model.name}
|
value={model.name}
|
||||||
label={<Text fontWeight={500}>{model.name}</Text>}
|
label={<Text fontWeight={500}>{model.name}</Text>}
|
||||||
isChecked={modelsToAdd.includes(model.name)}
|
isChecked={modelsToAdd.includes(model.name)}
|
||||||
isDisabled={existingModels.includes(model.location)}
|
isDisabled={existingModels.includes(model.location)}
|
||||||
onChange={foundModelsChangeHandler}
|
onChange={foundModelsChangeHandler}
|
||||||
></IAICheckbox>
|
></IAISimpleCheckbox>
|
||||||
{existingModels.includes(model.location) && (
|
{existingModels.includes(model.location) && (
|
||||||
<Badge colorScheme="accent">{t('modelManager.modelExists')}</Badge>
|
<Badge colorScheme="accent">{t('modelManager.modelExists')}</Badge>
|
||||||
)}
|
)}
|
||||||
@ -324,7 +324,7 @@ export default function SearchModels() {
|
|||||||
>
|
>
|
||||||
{t('modelManager.deselectAll')}
|
{t('modelManager.deselectAll')}
|
||||||
</IAIButton>
|
</IAIButton>
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={t('modelManager.showExisting')}
|
label={t('modelManager.showExisting')}
|
||||||
isChecked={shouldShowExistingModelsInSearch}
|
isChecked={shouldShowExistingModelsInSearch}
|
||||||
onChange={() =>
|
onChange={() =>
|
||||||
|
@ -8,6 +8,7 @@ import ParamNoiseCollapse from 'features/parameters/components/Parameters/Noise/
|
|||||||
import ParamSymmetryCollapse from 'features/parameters/components/Parameters/Symmetry/ParamSymmetryCollapse';
|
import ParamSymmetryCollapse from 'features/parameters/components/Parameters/Symmetry/ParamSymmetryCollapse';
|
||||||
import ParamSeamlessCollapse from 'features/parameters/components/Parameters/Seamless/ParamSeamlessCollapse';
|
import ParamSeamlessCollapse from 'features/parameters/components/Parameters/Seamless/ParamSeamlessCollapse';
|
||||||
import ImageToImageTabCoreParameters from './ImageToImageTabCoreParameters';
|
import ImageToImageTabCoreParameters from './ImageToImageTabCoreParameters';
|
||||||
|
import ParamControlNetCollapse from 'features/parameters/components/Parameters/ControlNet/ParamControlNetCollapse';
|
||||||
|
|
||||||
const ImageToImageTabParameters = () => {
|
const ImageToImageTabParameters = () => {
|
||||||
return (
|
return (
|
||||||
@ -17,6 +18,7 @@ const ImageToImageTabParameters = () => {
|
|||||||
<ProcessButtons />
|
<ProcessButtons />
|
||||||
<ImageToImageTabCoreParameters />
|
<ImageToImageTabCoreParameters />
|
||||||
<ParamSeedCollapse />
|
<ParamSeedCollapse />
|
||||||
|
<ParamControlNetCollapse />
|
||||||
<ParamVariationCollapse />
|
<ParamVariationCollapse />
|
||||||
<ParamNoiseCollapse />
|
<ParamNoiseCollapse />
|
||||||
<ParamSymmetryCollapse />
|
<ParamSymmetryCollapse />
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { RootState } from 'app/store/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAICheckbox from 'common/components/IAICheckbox';
|
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
|
||||||
import { setShouldDarkenOutsideBoundingBox } from 'features/canvas/store/canvasSlice';
|
import { setShouldDarkenOutsideBoundingBox } from 'features/canvas/store/canvasSlice';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ export default function UnifiedCanvasDarkenOutsideSelection() {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={t('unifiedCanvas.betaDarkenOutside')}
|
label={t('unifiedCanvas.betaDarkenOutside')}
|
||||||
isChecked={shouldDarkenOutsideBoundingBox}
|
isChecked={shouldDarkenOutsideBoundingBox}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { RootState } from 'app/store/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAICheckbox from 'common/components/IAICheckbox';
|
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
|
||||||
import { setIsMaskEnabled } from 'features/canvas/store/canvasSlice';
|
import { setIsMaskEnabled } from 'features/canvas/store/canvasSlice';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ export default function UnifiedCanvasEnableMask() {
|
|||||||
dispatch(setIsMaskEnabled(!isMaskEnabled));
|
dispatch(setIsMaskEnabled(!isMaskEnabled));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={`${t('unifiedCanvas.enableMask')} (H)`}
|
label={`${t('unifiedCanvas.enableMask')} (H)`}
|
||||||
isChecked={isMaskEnabled}
|
isChecked={isMaskEnabled}
|
||||||
onChange={handleToggleEnableMask}
|
onChange={handleToggleEnableMask}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { RootState } from 'app/store/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAICheckbox from 'common/components/IAICheckbox';
|
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
|
||||||
import { setShouldRestrictStrokesToBox } from 'features/canvas/store/canvasSlice';
|
import { setShouldRestrictStrokesToBox } from 'features/canvas/store/canvasSlice';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ export default function UnifiedCanvasLimitStrokesToBox() {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={t('unifiedCanvas.betaLimitToBox')}
|
label={t('unifiedCanvas.betaLimitToBox')}
|
||||||
isChecked={shouldRestrictStrokesToBox}
|
isChecked={shouldRestrictStrokesToBox}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { RootState } from 'app/store/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAICheckbox from 'common/components/IAICheckbox';
|
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
|
||||||
import { setShouldPreserveMaskedArea } from 'features/canvas/store/canvasSlice';
|
import { setShouldPreserveMaskedArea } from 'features/canvas/store/canvasSlice';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ export default function UnifiedCanvasPreserveMask() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={t('unifiedCanvas.betaPreserveMasked')}
|
label={t('unifiedCanvas.betaPreserveMasked')}
|
||||||
isChecked={shouldPreserveMaskedArea}
|
isChecked={shouldPreserveMaskedArea}
|
||||||
onChange={(e) => dispatch(setShouldPreserveMaskedArea(e.target.checked))}
|
onChange={(e) => dispatch(setShouldPreserveMaskedArea(e.target.checked))}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Flex } from '@chakra-ui/react';
|
import { Flex } from '@chakra-ui/react';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAICheckbox from 'common/components/IAICheckbox';
|
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
|
||||||
import IAIIconButton from 'common/components/IAIIconButton';
|
import IAIIconButton from 'common/components/IAIIconButton';
|
||||||
import IAIPopover from 'common/components/IAIPopover';
|
import IAIPopover from 'common/components/IAIPopover';
|
||||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||||
@ -73,33 +73,33 @@ const UnifiedCanvasSettings = () => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Flex direction="column" gap={2}>
|
<Flex direction="column" gap={2}>
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={t('unifiedCanvas.showIntermediates')}
|
label={t('unifiedCanvas.showIntermediates')}
|
||||||
isChecked={shouldShowIntermediates}
|
isChecked={shouldShowIntermediates}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
dispatch(setShouldShowIntermediates(e.target.checked))
|
dispatch(setShouldShowIntermediates(e.target.checked))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={t('unifiedCanvas.autoSaveToGallery')}
|
label={t('unifiedCanvas.autoSaveToGallery')}
|
||||||
isChecked={shouldAutoSave}
|
isChecked={shouldAutoSave}
|
||||||
onChange={(e) => dispatch(setShouldAutoSave(e.target.checked))}
|
onChange={(e) => dispatch(setShouldAutoSave(e.target.checked))}
|
||||||
/>
|
/>
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={t('unifiedCanvas.saveBoxRegionOnly')}
|
label={t('unifiedCanvas.saveBoxRegionOnly')}
|
||||||
isChecked={shouldCropToBoundingBoxOnSave}
|
isChecked={shouldCropToBoundingBoxOnSave}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
dispatch(setShouldCropToBoundingBoxOnSave(e.target.checked))
|
dispatch(setShouldCropToBoundingBoxOnSave(e.target.checked))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={t('unifiedCanvas.showCanvasDebugInfo')}
|
label={t('unifiedCanvas.showCanvasDebugInfo')}
|
||||||
isChecked={shouldShowCanvasDebugInfo}
|
isChecked={shouldShowCanvasDebugInfo}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
dispatch(setShouldShowCanvasDebugInfo(e.target.checked))
|
dispatch(setShouldShowCanvasDebugInfo(e.target.checked))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={t('unifiedCanvas.antialiasing')}
|
label={t('unifiedCanvas.antialiasing')}
|
||||||
isChecked={shouldAntialias}
|
isChecked={shouldAntialias}
|
||||||
onChange={(e) => dispatch(setShouldAntialias(e.target.checked))}
|
onChange={(e) => dispatch(setShouldAntialias(e.target.checked))}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { RootState } from 'app/store/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAICheckbox from 'common/components/IAICheckbox';
|
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
|
||||||
import { setShouldShowGrid } from 'features/canvas/store/canvasSlice';
|
import { setShouldShowGrid } from 'features/canvas/store/canvasSlice';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ export default function UnifiedCanvasShowGrid() {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={t('unifiedCanvas.showGrid')}
|
label={t('unifiedCanvas.showGrid')}
|
||||||
isChecked={shouldShowGrid}
|
isChecked={shouldShowGrid}
|
||||||
onChange={(e) => dispatch(setShouldShowGrid(e.target.checked))}
|
onChange={(e) => dispatch(setShouldShowGrid(e.target.checked))}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { RootState } from 'app/store/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAICheckbox from 'common/components/IAICheckbox';
|
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
|
||||||
import { setShouldSnapToGrid } from 'features/canvas/store/canvasSlice';
|
import { setShouldSnapToGrid } from 'features/canvas/store/canvasSlice';
|
||||||
import { ChangeEvent } from 'react';
|
import { ChangeEvent } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -17,7 +17,7 @@ export default function UnifiedCanvasSnapToGrid() {
|
|||||||
dispatch(setShouldSnapToGrid(e.target.checked));
|
dispatch(setShouldSnapToGrid(e.target.checked));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAICheckbox
|
<IAISimpleCheckbox
|
||||||
label={`${t('unifiedCanvas.snapToGrid')} (N)`}
|
label={`${t('unifiedCanvas.snapToGrid')} (N)`}
|
||||||
isChecked={shouldSnapToGrid}
|
isChecked={shouldSnapToGrid}
|
||||||
onChange={handleChangeShouldSnapToGrid}
|
onChange={handleChangeShouldSnapToGrid}
|
||||||
|
@ -10,8 +10,25 @@ import {
|
|||||||
ImageField,
|
ImageField,
|
||||||
LatentsOutput,
|
LatentsOutput,
|
||||||
ResourceOrigin,
|
ResourceOrigin,
|
||||||
|
ImageDTO,
|
||||||
} from 'services/api';
|
} from 'services/api';
|
||||||
|
|
||||||
|
export const isImageDTO = (obj: unknown): obj is ImageDTO => {
|
||||||
|
return (
|
||||||
|
isObject(obj) &&
|
||||||
|
'image_name' in obj &&
|
||||||
|
isString(obj?.image_name) &&
|
||||||
|
'thumbnail_url' in obj &&
|
||||||
|
isString(obj?.thumbnail_url) &&
|
||||||
|
'image_url' in obj &&
|
||||||
|
isString(obj?.image_url) &&
|
||||||
|
'image_origin' in obj &&
|
||||||
|
isString(obj?.image_origin) &&
|
||||||
|
'created_at' in obj &&
|
||||||
|
isString(obj?.created_at)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const isImageOutput = (
|
export const isImageOutput = (
|
||||||
output: GraphExecutionState['results'][string]
|
output: GraphExecutionState['results'][string]
|
||||||
): output is ImageOutput => output.type === 'image_output';
|
): output is ImageOutput => output.type === 'image_output';
|
||||||
|
@ -937,6 +937,29 @@
|
|||||||
gonzales-pe "^4.3.0"
|
gonzales-pe "^4.3.0"
|
||||||
node-source-walk "^5.0.1"
|
node-source-walk "^5.0.1"
|
||||||
|
|
||||||
|
"@dnd-kit/accessibility@^3.0.0":
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@dnd-kit/accessibility/-/accessibility-3.0.1.tgz#3ccbefdfca595b0a23a5dc57d3de96bc6935641c"
|
||||||
|
integrity sha512-HXRrwS9YUYQO9lFRc/49uO/VICbM+O+ZRpFDe9Pd1rwVv2PCNkRiTZRdxrDgng/UkvdC3Re9r2vwPpXXrWeFzg==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.0.0"
|
||||||
|
|
||||||
|
"@dnd-kit/core@^6.0.8":
|
||||||
|
version "6.0.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@dnd-kit/core/-/core-6.0.8.tgz#040ae13fea9787ee078e5f0361f3b49b07f3f005"
|
||||||
|
integrity sha512-lYaoP8yHTQSLlZe6Rr9qogouGUz9oRUj4AHhDQGQzq/hqaJRpFo65X+JKsdHf8oUFBzx5A+SJPUvxAwTF2OabA==
|
||||||
|
dependencies:
|
||||||
|
"@dnd-kit/accessibility" "^3.0.0"
|
||||||
|
"@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"
|
||||||
|
integrity sha512-OOXqISfvBw/1REtkSK2N3Fi2EQiLMlWUlqnOK/UpOISqBZPWpE6TqL+jcPtMOkE8TqYGiURvRdPSI9hltNUjEA==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.0.0"
|
||||||
|
|
||||||
"@emotion/babel-plugin@^11.10.8":
|
"@emotion/babel-plugin@^11.10.8":
|
||||||
version "11.10.8"
|
version "11.10.8"
|
||||||
resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.10.8.tgz#bae325c902937665d00684038fd5294223ef9e1d"
|
resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.10.8.tgz#bae325c902937665d00684038fd5294223ef9e1d"
|
||||||
|
Loading…
Reference in New Issue
Block a user