mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Adds hints when unable to invoke
- Popover on Invoke button indicates why exactly it is disabled, e.g. prompt is empty, something else is processing, etc. - There may be more than one reason; all are displayed.
This commit is contained in:
parent
0eef74bc00
commit
871a8a5375
517
frontend/dist/assets/index.3a0c23f9.js
vendored
Normal file
517
frontend/dist/assets/index.3a0c23f9.js
vendored
Normal file
File diff suppressed because one or more lines are too long
517
frontend/dist/assets/index.784f40b4.js
vendored
517
frontend/dist/assets/index.784f40b4.js
vendored
File diff suppressed because one or more lines are too long
1
frontend/dist/assets/index.c1f2c49a.css
vendored
Normal file
1
frontend/dist/assets/index.c1f2c49a.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
frontend/dist/assets/index.e29f3ddc.css
vendored
1
frontend/dist/assets/index.e29f3ddc.css
vendored
File diff suppressed because one or more lines are too long
4
frontend/dist/index.html
vendored
4
frontend/dist/index.html
vendored
@ -6,8 +6,8 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>InvokeAI - A Stable Diffusion Toolkit</title>
|
<title>InvokeAI - A Stable Diffusion Toolkit</title>
|
||||||
<link rel="shortcut icon" type="icon" href="./assets/favicon.0d253ced.ico" />
|
<link rel="shortcut icon" type="icon" href="./assets/favicon.0d253ced.ico" />
|
||||||
<script type="module" crossorigin src="./assets/index.784f40b4.js"></script>
|
<script type="module" crossorigin src="./assets/index.3a0c23f9.js"></script>
|
||||||
<link rel="stylesheet" href="./assets/index.e29f3ddc.css">
|
<link rel="stylesheet" href="./assets/index.c1f2c49a.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect } from 'react';
|
||||||
import ProgressBar from '../features/system/ProgressBar';
|
import ProgressBar from '../features/system/ProgressBar';
|
||||||
import SiteHeader from '../features/system/SiteHeader';
|
import SiteHeader from '../features/system/SiteHeader';
|
||||||
import Console from '../features/system/Console';
|
import Console from '../features/system/Console';
|
||||||
import Loading from '../Loading';
|
|
||||||
import { useAppDispatch } from './store';
|
import { useAppDispatch } from './store';
|
||||||
import { requestSystemConfig } from './socketio/actions';
|
import { requestSystemConfig } from './socketio/actions';
|
||||||
import { keepGUIAlive } from './utils';
|
import { keepGUIAlive } from './utils';
|
||||||
@ -16,9 +15,10 @@ import { createSelector } from '@reduxjs/toolkit';
|
|||||||
import { GalleryState } from '../features/gallery/gallerySlice';
|
import { GalleryState } from '../features/gallery/gallerySlice';
|
||||||
import { OptionsState } from '../features/options/optionsSlice';
|
import { OptionsState } from '../features/options/optionsSlice';
|
||||||
import { activeTabNameSelector } from '../features/options/optionsSelectors';
|
import { activeTabNameSelector } from '../features/options/optionsSelectors';
|
||||||
import { SystemState } from '../features/system/systemSlice';
|
import { readinessChanged, SystemState } from '../features/system/systemSlice';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { Model } from './invokeai';
|
import { Model } from './invokeai';
|
||||||
|
import { readinessSelector } from './selectors/readinessSelector';
|
||||||
|
|
||||||
keepGUIAlive();
|
keepGUIAlive();
|
||||||
|
|
||||||
@ -79,17 +79,20 @@ const appSelector = createSelector(
|
|||||||
const App = () => {
|
const App = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const [isReady, setIsReady] = useState<boolean>(false);
|
// const [isReady, setIsReady] = useState<boolean>(false);
|
||||||
|
const { isReady, reasonsWhyNotReady } = useAppSelector(readinessSelector);
|
||||||
const { shouldShowGalleryButton, shouldShowOptionsPanelButton } =
|
const { shouldShowGalleryButton, shouldShowOptionsPanelButton } =
|
||||||
useAppSelector(appSelector);
|
useAppSelector(appSelector);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(requestSystemConfig());
|
dispatch(requestSystemConfig());
|
||||||
setIsReady(true);
|
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
return isReady ? (
|
useEffect(() => {
|
||||||
|
dispatch(readinessChanged({ isReady, reasonsWhyNotReady }));
|
||||||
|
}, [dispatch, isReady, reasonsWhyNotReady]);
|
||||||
|
|
||||||
|
return (
|
||||||
<div className="App">
|
<div className="App">
|
||||||
<ImageUploader>
|
<ImageUploader>
|
||||||
<ProgressBar />
|
<ProgressBar />
|
||||||
@ -104,8 +107,6 @@ const App = () => {
|
|||||||
{shouldShowOptionsPanelButton && <FloatingOptionsPanelButtons />}
|
{shouldShowOptionsPanelButton && <FloatingOptionsPanelButtons />}
|
||||||
</ImageUploader>
|
</ImageUploader>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
|
||||||
<Loading />
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { RootState } from '../../app/store';
|
import { RootState } from '../store';
|
||||||
import { activeTabNameSelector } from '../../features/options/optionsSelectors';
|
import { activeTabNameSelector } from '../../features/options/optionsSelectors';
|
||||||
import { OptionsState } from '../../features/options/optionsSlice';
|
import { OptionsState } from '../../features/options/optionsSlice';
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ export const readinessSelector = createSelector(
|
|||||||
prompt,
|
prompt,
|
||||||
shouldGenerateVariations,
|
shouldGenerateVariations,
|
||||||
seedWeights,
|
seedWeights,
|
||||||
maskPath,
|
// maskPath,
|
||||||
initialImage,
|
initialImage,
|
||||||
seed,
|
seed,
|
||||||
} = options;
|
} = options;
|
||||||
@ -34,33 +34,49 @@ export const readinessSelector = createSelector(
|
|||||||
|
|
||||||
const { imageToInpaint } = inpainting;
|
const { imageToInpaint } = inpainting;
|
||||||
|
|
||||||
|
let isReady = true;
|
||||||
|
const reasonsWhyNotReady: string[] = [];
|
||||||
|
|
||||||
// Cannot generate without a prompt
|
// Cannot generate without a prompt
|
||||||
if (!prompt || Boolean(prompt.match(/^[\s\r\n]+$/))) {
|
if (!prompt || Boolean(prompt.match(/^[\s\r\n]+$/))) {
|
||||||
return false;
|
isReady = false;
|
||||||
|
reasonsWhyNotReady.push('Missing a prompt.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activeTabName === 'img2img' && !initialImage) {
|
if (activeTabName === 'img2img' && !initialImage) {
|
||||||
return false;
|
isReady = false;
|
||||||
|
reasonsWhyNotReady.push(
|
||||||
|
'On ImageToImage tab, but no initial image is selected.'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activeTabName === 'inpainting' && !imageToInpaint) {
|
if (activeTabName === 'inpainting' && !imageToInpaint) {
|
||||||
return false;
|
isReady = false;
|
||||||
|
reasonsWhyNotReady.push(
|
||||||
|
'On Inpainting tab, but no initial image is selected.'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cannot generate with a mask without img2img
|
// // We don't use mask paths now.
|
||||||
if (maskPath && !initialImage) {
|
// // Cannot generate with a mask without img2img
|
||||||
return false;
|
// if (maskPath && !initialImage) {
|
||||||
}
|
// isReady = false;
|
||||||
|
// reasonsWhyNotReady.push(
|
||||||
|
// 'On ImageToImage tab, but no mask is provided.'
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
// TODO: job queue
|
// TODO: job queue
|
||||||
// Cannot generate if already processing an image
|
// Cannot generate if already processing an image
|
||||||
if (isProcessing) {
|
if (isProcessing) {
|
||||||
return false;
|
isReady = false;
|
||||||
|
reasonsWhyNotReady.push('System is already processing something.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cannot generate if not connected
|
// Cannot generate if not connected
|
||||||
if (!isConnected) {
|
if (!isConnected) {
|
||||||
return false;
|
isReady = false;
|
||||||
|
reasonsWhyNotReady.push('System is disconnected.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cannot generate variations without valid seed weights
|
// Cannot generate variations without valid seed weights
|
||||||
@ -68,11 +84,12 @@ export const readinessSelector = createSelector(
|
|||||||
shouldGenerateVariations &&
|
shouldGenerateVariations &&
|
||||||
(!(validateSeedWeights(seedWeights) || seedWeights === '') || seed === -1)
|
(!(validateSeedWeights(seedWeights) || seedWeights === '') || seed === -1)
|
||||||
) {
|
) {
|
||||||
return false;
|
isReady = false;
|
||||||
|
reasonsWhyNotReady.push('Seed-weight pairs are badly formatted.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// All good
|
// All good
|
||||||
return true;
|
return { isReady, reasonsWhyNotReady };
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
memoizeOptions: {
|
memoizeOptions: {
|
||||||
|
@ -4,12 +4,14 @@ import {
|
|||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
Box,
|
Box,
|
||||||
|
BoxProps,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { PopoverProps } from '@chakra-ui/react';
|
import { PopoverProps } from '@chakra-ui/react';
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
type IAIPopoverProps = PopoverProps & {
|
type IAIPopoverProps = PopoverProps & {
|
||||||
triggerComponent: ReactNode;
|
triggerComponent: ReactNode;
|
||||||
|
triggerContainerProps?: BoxProps;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
styleClass?: string;
|
styleClass?: string;
|
||||||
hasArrow?: boolean;
|
hasArrow?: boolean;
|
||||||
@ -18,15 +20,17 @@ type IAIPopoverProps = PopoverProps & {
|
|||||||
const IAIPopover = (props: IAIPopoverProps) => {
|
const IAIPopover = (props: IAIPopoverProps) => {
|
||||||
const {
|
const {
|
||||||
triggerComponent,
|
triggerComponent,
|
||||||
|
triggerContainerProps,
|
||||||
children,
|
children,
|
||||||
styleClass,
|
styleClass,
|
||||||
hasArrow = true,
|
hasArrow = true,
|
||||||
...rest
|
...rest
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover {...rest}>
|
<Popover {...rest}>
|
||||||
<PopoverTrigger>
|
<PopoverTrigger>
|
||||||
<Box>{triggerComponent}</Box>
|
<Box {...triggerContainerProps}>{triggerComponent}</Box>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className={`invokeai__popover-content ${styleClass}`}>
|
<PopoverContent className={`invokeai__popover-content ${styleClass}`}>
|
||||||
{hasArrow && <PopoverArrow className={'invokeai__popover-arrow'} />}
|
{hasArrow && <PopoverArrow className={'invokeai__popover-arrow'} />}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { ListItem, UnorderedList } from '@chakra-ui/react';
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
import { FaPlay } from 'react-icons/fa';
|
import { FaPlay } from 'react-icons/fa';
|
||||||
import { readinessSelector } from '../../../app/selectors/readinessSelector';
|
import { readinessSelector } from '../../../app/selectors/readinessSelector';
|
||||||
@ -7,6 +8,7 @@ import IAIButton, {
|
|||||||
IAIButtonProps,
|
IAIButtonProps,
|
||||||
} from '../../../common/components/IAIButton';
|
} from '../../../common/components/IAIButton';
|
||||||
import IAIIconButton from '../../../common/components/IAIIconButton';
|
import IAIIconButton from '../../../common/components/IAIIconButton';
|
||||||
|
import IAIPopover from '../../../common/components/IAIPopover';
|
||||||
import { activeTabNameSelector } from '../optionsSelectors';
|
import { activeTabNameSelector } from '../optionsSelectors';
|
||||||
|
|
||||||
interface InvokeButton extends Omit<IAIButtonProps, 'label'> {
|
interface InvokeButton extends Omit<IAIButtonProps, 'label'> {
|
||||||
@ -16,7 +18,7 @@ interface InvokeButton extends Omit<IAIButtonProps, 'label'> {
|
|||||||
export default function InvokeButton(props: InvokeButton) {
|
export default function InvokeButton(props: InvokeButton) {
|
||||||
const { iconButton = false, ...rest } = props;
|
const { iconButton = false, ...rest } = props;
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const isReady = useAppSelector(readinessSelector);
|
const { isReady, reasonsWhyNotReady } = useAppSelector(readinessSelector);
|
||||||
const activeTabName = useAppSelector(activeTabNameSelector);
|
const activeTabName = useAppSelector(activeTabNameSelector);
|
||||||
|
|
||||||
const handleClickGenerate = () => {
|
const handleClickGenerate = () => {
|
||||||
@ -33,7 +35,7 @@ export default function InvokeButton(props: InvokeButton) {
|
|||||||
[isReady, activeTabName]
|
[isReady, activeTabName]
|
||||||
);
|
);
|
||||||
|
|
||||||
return iconButton ? (
|
const buttonComponent = iconButton ? (
|
||||||
<IAIIconButton
|
<IAIIconButton
|
||||||
aria-label="Invoke"
|
aria-label="Invoke"
|
||||||
type="submit"
|
type="submit"
|
||||||
@ -56,4 +58,22 @@ export default function InvokeButton(props: InvokeButton) {
|
|||||||
{...rest}
|
{...rest}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return isReady ? (
|
||||||
|
buttonComponent
|
||||||
|
) : (
|
||||||
|
<IAIPopover
|
||||||
|
trigger="hover"
|
||||||
|
triggerContainerProps={{ style: { flexGrow: 4 } }}
|
||||||
|
triggerComponent={buttonComponent}
|
||||||
|
>
|
||||||
|
{reasonsWhyNotReady && (
|
||||||
|
<UnorderedList>
|
||||||
|
{reasonsWhyNotReady.map((reason, i) => (
|
||||||
|
<ListItem key={i}>{reason}</ListItem>
|
||||||
|
))}
|
||||||
|
</UnorderedList>
|
||||||
|
)}
|
||||||
|
</IAIPopover>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
.invoke-btn {
|
.invoke-btn {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
width: 100%;
|
||||||
svg {
|
svg {
|
||||||
width: 18px !important;
|
width: 18px !important;
|
||||||
height: 18px !important;
|
height: 18px !important;
|
||||||
|
@ -31,7 +31,7 @@ const promptInputSelector = createSelector(
|
|||||||
const PromptInput = () => {
|
const PromptInput = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { prompt, activeTabName } = useAppSelector(promptInputSelector);
|
const { prompt, activeTabName } = useAppSelector(promptInputSelector);
|
||||||
const isReady = useAppSelector(readinessSelector);
|
const { isReady } = useAppSelector(readinessSelector);
|
||||||
|
|
||||||
const promptRef = useRef<HTMLTextAreaElement>(null);
|
const promptRef = useRef<HTMLTextAreaElement>(null);
|
||||||
|
|
||||||
|
@ -15,6 +15,11 @@ export interface Log {
|
|||||||
[index: number]: LogEntry;
|
[index: number]: LogEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ReadinessPayload = {
|
||||||
|
isReady: boolean;
|
||||||
|
reasonsWhyNotReady: string[];
|
||||||
|
};
|
||||||
|
|
||||||
export interface SystemState
|
export interface SystemState
|
||||||
extends InvokeAI.SystemStatus,
|
extends InvokeAI.SystemStatus,
|
||||||
InvokeAI.SystemConfig {
|
InvokeAI.SystemConfig {
|
||||||
@ -36,6 +41,8 @@ export interface SystemState
|
|||||||
shouldDisplayGuides: boolean;
|
shouldDisplayGuides: boolean;
|
||||||
wasErrorSeen: boolean;
|
wasErrorSeen: boolean;
|
||||||
isCancelable: boolean;
|
isCancelable: boolean;
|
||||||
|
isReady: boolean;
|
||||||
|
reasonsWhyNotReady: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialSystemState = {
|
const initialSystemState = {
|
||||||
@ -65,6 +72,8 @@ const initialSystemState = {
|
|||||||
hasError: false,
|
hasError: false,
|
||||||
wasErrorSeen: true,
|
wasErrorSeen: true,
|
||||||
isCancelable: true,
|
isCancelable: true,
|
||||||
|
isReady: false,
|
||||||
|
reasonsWhyNotReady: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialState: SystemState = initialSystemState;
|
const initialState: SystemState = initialSystemState;
|
||||||
@ -178,6 +187,11 @@ export const systemSlice = createSlice({
|
|||||||
state.isProcessing = true;
|
state.isProcessing = true;
|
||||||
state.currentStatusHasSteps = false;
|
state.currentStatusHasSteps = false;
|
||||||
},
|
},
|
||||||
|
readinessChanged: (state, action: PayloadAction<ReadinessPayload>) => {
|
||||||
|
const { isReady, reasonsWhyNotReady } = action.payload;
|
||||||
|
state.isReady = isReady;
|
||||||
|
state.reasonsWhyNotReady = reasonsWhyNotReady;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -200,6 +214,7 @@ export const {
|
|||||||
setModelList,
|
setModelList,
|
||||||
setIsCancelable,
|
setIsCancelable,
|
||||||
modelChangeRequested,
|
modelChangeRequested,
|
||||||
|
readinessChanged,
|
||||||
} = systemSlice.actions;
|
} = systemSlice.actions;
|
||||||
|
|
||||||
export default systemSlice.reducer;
|
export default systemSlice.reducer;
|
||||||
|
@ -34,10 +34,7 @@ import InpaintingBoundingBoxPreview, {
|
|||||||
} from './components/InpaintingBoundingBoxPreview';
|
} from './components/InpaintingBoundingBoxPreview';
|
||||||
import { KonvaEventObject } from 'konva/lib/Node';
|
import { KonvaEventObject } from 'konva/lib/Node';
|
||||||
import KeyboardEventManager from './components/KeyboardEventManager';
|
import KeyboardEventManager from './components/KeyboardEventManager';
|
||||||
import { Icon, IconButton, Tooltip, useToast } from '@chakra-ui/react';
|
import { useToast } from '@chakra-ui/react';
|
||||||
import { FaLock, FaUnlock } from 'react-icons/fa';
|
|
||||||
import { MdInvertColors, MdInvertColorsOff } from 'react-icons/md';
|
|
||||||
import { BiHide, BiShow } from 'react-icons/bi';
|
|
||||||
import InpaintingCanvasStatusIcons from './InpaintingCanvasStatusIcons';
|
import InpaintingCanvasStatusIcons from './InpaintingCanvasStatusIcons';
|
||||||
|
|
||||||
// Use a closure allow other components to use these things... not ideal...
|
// Use a closure allow other components to use these things... not ideal...
|
||||||
@ -60,9 +57,6 @@ const InpaintingCanvas = () => {
|
|||||||
shouldShowBoundingBox,
|
shouldShowBoundingBox,
|
||||||
shouldShowBoundingBoxFill,
|
shouldShowBoundingBoxFill,
|
||||||
isDrawing,
|
isDrawing,
|
||||||
shouldLockBoundingBox,
|
|
||||||
boundingBoxDimensions,
|
|
||||||
// isTransformingBoundingBox,
|
|
||||||
isMouseOverBoundingBox,
|
isMouseOverBoundingBox,
|
||||||
isModifyingBoundingBox,
|
isModifyingBoundingBox,
|
||||||
stageCursor,
|
stageCursor,
|
||||||
|
@ -5,7 +5,7 @@ import { KonvaEventObject } from 'konva/lib/Node';
|
|||||||
import { Box } from 'konva/lib/shapes/Transformer';
|
import { Box } from 'konva/lib/shapes/Transformer';
|
||||||
import { Vector2d } from 'konva/lib/types';
|
import { Vector2d } from 'konva/lib/types';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useRef } from 'react';
|
||||||
import { Group, Rect, Transformer } from 'react-konva';
|
import { Group, Rect, Transformer } from 'react-konva';
|
||||||
import {
|
import {
|
||||||
RootState,
|
RootState,
|
||||||
@ -13,7 +13,6 @@ import {
|
|||||||
useAppSelector,
|
useAppSelector,
|
||||||
} from '../../../../app/store';
|
} from '../../../../app/store';
|
||||||
import { roundToMultiple } from '../../../../common/util/roundDownToMultiple';
|
import { roundToMultiple } from '../../../../common/util/roundDownToMultiple';
|
||||||
import { stageRef } from '../InpaintingCanvas';
|
|
||||||
import {
|
import {
|
||||||
InpaintingState,
|
InpaintingState,
|
||||||
setBoundingBoxCoordinate,
|
setBoundingBoxCoordinate,
|
||||||
|
@ -10,9 +10,7 @@ import { activeTabNameSelector } from '../../../options/optionsSelectors';
|
|||||||
import { OptionsState } from '../../../options/optionsSlice';
|
import { OptionsState } from '../../../options/optionsSlice';
|
||||||
import {
|
import {
|
||||||
InpaintingState,
|
InpaintingState,
|
||||||
setIsDrawing,
|
|
||||||
setIsSpacebarHeld,
|
setIsSpacebarHeld,
|
||||||
setNeedsCache,
|
|
||||||
setShouldLockBoundingBox,
|
setShouldLockBoundingBox,
|
||||||
toggleTool,
|
toggleTool,
|
||||||
} from '../inpaintingSlice';
|
} from '../inpaintingSlice';
|
||||||
|
Loading…
Reference in New Issue
Block a user