mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
feat(ui): more work on controlnet mini
This commit is contained in:
@@ -58,11 +58,7 @@ const ControlNet = (props: ControlNetProps) => {
|
||||
|
||||
return (
|
||||
<Flex sx={{ flexDir: 'column', gap: 3 }}>
|
||||
<ControlNetImagePreview
|
||||
controlNetId={controlNetId}
|
||||
controlImage={controlImage}
|
||||
processedControlImage={processedControlImage}
|
||||
/>
|
||||
<ControlNetImagePreview controlNet={props.controlNet} />
|
||||
<ParamControlNetModel controlNetId={controlNetId} model={model} />
|
||||
<Tabs
|
||||
isFitted
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { memo, useCallback, useState } from 'react';
|
||||
import { memo, useCallback, useRef, useState } from 'react';
|
||||
import { ImageDTO } from 'services/api';
|
||||
import {
|
||||
ControlNet,
|
||||
controlNetImageChanged,
|
||||
controlNetSelector,
|
||||
} from '../store/controlNetSlice';
|
||||
@@ -11,6 +12,7 @@ import { createSelector } from '@reduxjs/toolkit';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { IAIImageFallback } from 'common/components/IAIImageFallback';
|
||||
import { useHoverDirty } from 'react-use';
|
||||
|
||||
const selector = createSelector(
|
||||
controlNetSelector,
|
||||
@@ -22,18 +24,21 @@ const selector = createSelector(
|
||||
);
|
||||
|
||||
type Props = {
|
||||
controlNetId: string;
|
||||
controlImage: ImageDTO | null;
|
||||
processedControlImage: ImageDTO | null;
|
||||
controlNet: ControlNet;
|
||||
};
|
||||
|
||||
const ControlNetImagePreview = (props: Props) => {
|
||||
const { controlNetId, controlImage, processedControlImage } = props;
|
||||
const {
|
||||
controlNetId,
|
||||
controlImage,
|
||||
processedControlImage,
|
||||
isControlImageProcessed,
|
||||
} = props.controlNet;
|
||||
const dispatch = useAppDispatch();
|
||||
const { isProcessingControlImage } = useAppSelector(selector);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const [shouldShowProcessedImage, setShouldShowProcessedImage] =
|
||||
useState(true);
|
||||
const isMouseOverImage = useHoverDirty(containerRef);
|
||||
|
||||
const handleControlImageChanged = useCallback(
|
||||
(controlImage: ImageDTO) => {
|
||||
@@ -46,12 +51,15 @@ const ControlNetImagePreview = (props: Props) => {
|
||||
Number(controlImage?.width) > Number(processedControlImage?.width) ||
|
||||
Number(controlImage?.height) > Number(processedControlImage?.height);
|
||||
|
||||
const shouldShowProcessedImage =
|
||||
controlImage &&
|
||||
processedControlImage &&
|
||||
!isMouseOverImage &&
|
||||
!isProcessingControlImage &&
|
||||
!isControlImageProcessed;
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{ position: 'relative', h: 'inherit' }}
|
||||
onMouseOver={() => setShouldShowProcessedImage(false)}
|
||||
onMouseOut={() => setShouldShowProcessedImage(true)}
|
||||
>
|
||||
<Box ref={containerRef} sx={{ position: 'relative', w: 'full', h: 'full' }}>
|
||||
<IAIDndImage
|
||||
image={controlImage}
|
||||
onDrop={handleControlImageChanged}
|
||||
|
||||
@@ -1,29 +1,26 @@
|
||||
import { memo, useCallback } from 'react';
|
||||
import {
|
||||
ControlNet,
|
||||
controlNetProcessedImageChanged,
|
||||
controlNetAdded,
|
||||
controlNetRemoved,
|
||||
controlNetToggled,
|
||||
isControlNetImageProcessedToggled,
|
||||
} from '../store/controlNetSlice';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import ParamControlNetModel from './parameters/ParamControlNetModel';
|
||||
import ParamControlNetWeight from './parameters/ParamControlNetWeight';
|
||||
import {
|
||||
Box,
|
||||
Checkbox,
|
||||
Flex,
|
||||
Tab,
|
||||
TabList,
|
||||
TabPanel,
|
||||
TabPanels,
|
||||
Tabs,
|
||||
Text,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
HStack,
|
||||
} from '@chakra-ui/react';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import { FaUndo } from 'react-icons/fa';
|
||||
import ParamControlNetProcessorSelect from './parameters/ParamControlNetProcessorSelect';
|
||||
import ControlNetProcessorComponent from './ControlNetProcessorComponent';
|
||||
import ControlNetPreprocessButton from './ControlNetPreprocessButton';
|
||||
import { FaCopy, FaTrash } from 'react-icons/fa';
|
||||
import ParamControlNetBeginEnd from './parameters/ParamControlNetBeginEnd';
|
||||
import ControlNetImagePreview from './ControlNetImagePreview';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
type ControlNetProps = {
|
||||
controlNet: ControlNet;
|
||||
@@ -43,56 +40,111 @@ const ControlNet = (props: ControlNetProps) => {
|
||||
processorNode,
|
||||
} = props.controlNet;
|
||||
const dispatch = useAppDispatch();
|
||||
const handleReset = useCallback(() => {
|
||||
dispatch(
|
||||
controlNetProcessedImageChanged({
|
||||
controlNetId,
|
||||
processedControlImage: null,
|
||||
})
|
||||
);
|
||||
|
||||
const handleDelete = useCallback(() => {
|
||||
dispatch(controlNetRemoved(controlNetId));
|
||||
}, [controlNetId, dispatch]);
|
||||
|
||||
const handleControlNetRemoved = useCallback(() => {
|
||||
dispatch(controlNetRemoved(controlNetId));
|
||||
const handleDuplicate = useCallback(() => {
|
||||
dispatch(
|
||||
controlNetAdded({ controlNetId: uuidv4(), controlNet: props.controlNet })
|
||||
);
|
||||
}, [dispatch, props.controlNet]);
|
||||
|
||||
const handleToggleIsEnabled = useCallback(() => {
|
||||
dispatch(controlNetToggled(controlNetId));
|
||||
}, [controlNetId, dispatch]);
|
||||
|
||||
const handleToggleIsPreprocessed = useCallback(() => {
|
||||
dispatch(isControlNetImageProcessedToggled(controlNetId));
|
||||
}, [controlNetId, dispatch]);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
gap: 4,
|
||||
p: 2,
|
||||
paddingInlineEnd: 4,
|
||||
bg: 'base.850',
|
||||
borderRadius: 'base',
|
||||
flexDir: 'column',
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
<HStack>
|
||||
<ParamControlNetModel controlNetId={controlNetId} model={model} />
|
||||
<IAIIconButton
|
||||
size="sm"
|
||||
tooltip="Duplicate ControlNet"
|
||||
aria-label="Duplicate ControlNet"
|
||||
onClick={handleDuplicate}
|
||||
icon={<FaCopy />}
|
||||
/>
|
||||
<IAIIconButton
|
||||
size="sm"
|
||||
tooltip="Delete ControlNet"
|
||||
aria-label="Delete ControlNet"
|
||||
colorScheme="error"
|
||||
onClick={handleDelete}
|
||||
icon={<FaTrash />}
|
||||
/>
|
||||
</HStack>
|
||||
<Flex
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
h: 36,
|
||||
w: 36,
|
||||
gap: 4,
|
||||
paddingInlineEnd: 2,
|
||||
}}
|
||||
>
|
||||
<ControlNetImagePreview
|
||||
controlNetId={controlNetId}
|
||||
controlImage={controlImage}
|
||||
processedControlImage={processedControlImage}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex sx={{ flexDir: 'column', gap: 2, w: 'full', h: 'full' }}>
|
||||
<ParamControlNetModel controlNetId={controlNetId} model={model} />
|
||||
<ParamControlNetWeight
|
||||
controlNetId={controlNetId}
|
||||
weight={weight}
|
||||
mini
|
||||
/>
|
||||
<ParamControlNetBeginEnd
|
||||
controlNetId={controlNetId}
|
||||
beginStepPct={beginStepPct}
|
||||
endStepPct={endStepPct}
|
||||
mini
|
||||
/>
|
||||
<Flex
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
h: 32,
|
||||
w: 32,
|
||||
aspectRatio: '1/1',
|
||||
}}
|
||||
>
|
||||
<ControlNetImagePreview controlNet={props.controlNet} />
|
||||
</Flex>
|
||||
<Flex
|
||||
sx={{
|
||||
flexDir: 'column',
|
||||
gap: 2,
|
||||
w: 'full',
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
<ParamControlNetWeight
|
||||
controlNetId={controlNetId}
|
||||
weight={weight}
|
||||
mini
|
||||
/>
|
||||
<ParamControlNetBeginEnd
|
||||
controlNetId={controlNetId}
|
||||
beginStepPct={beginStepPct}
|
||||
endStepPct={endStepPct}
|
||||
mini
|
||||
/>
|
||||
<Flex
|
||||
sx={{
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
<FormControl>
|
||||
<HStack>
|
||||
<Checkbox
|
||||
isChecked={isEnabled}
|
||||
onChange={handleToggleIsEnabled}
|
||||
/>
|
||||
<FormLabel>Enabled</FormLabel>
|
||||
</HStack>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<HStack>
|
||||
<Checkbox
|
||||
isChecked={isControlImageProcessed}
|
||||
onChange={handleToggleIsPreprocessed}
|
||||
/>
|
||||
<FormLabel>Preprocessed</FormLabel>
|
||||
</HStack>
|
||||
</FormControl>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
|
||||
@@ -50,7 +50,7 @@ const ParamControlNetBeginEnd = (props: Props) => {
|
||||
|
||||
return (
|
||||
<FormControl>
|
||||
<FormLabel>Begin & End Step %</FormLabel>
|
||||
<FormLabel>Begin / End Step Percentage</FormLabel>
|
||||
<HStack w="100%" gap={2} alignItems="center">
|
||||
<RangeSlider
|
||||
aria-label={['Begin Step %', 'End Step %']}
|
||||
@@ -72,7 +72,6 @@ const ParamControlNetBeginEnd = (props: Props) => {
|
||||
</Tooltip>
|
||||
{!mini && (
|
||||
<>
|
||||
{' '}
|
||||
<RangeSliderMark
|
||||
value={0}
|
||||
sx={{
|
||||
|
||||
@@ -72,11 +72,11 @@ export const controlNetSlice = createSlice({
|
||||
},
|
||||
controlNetAdded: (
|
||||
state,
|
||||
action: PayloadAction<{ controlNetId: string }>
|
||||
action: PayloadAction<{ controlNetId: string; controlNet?: ControlNet }>
|
||||
) => {
|
||||
const { controlNetId } = action.payload;
|
||||
const { controlNetId, controlNet } = action.payload;
|
||||
state.controlNets[controlNetId] = {
|
||||
...initialControlNet,
|
||||
...(controlNet ?? initialControlNet),
|
||||
controlNetId,
|
||||
};
|
||||
},
|
||||
@@ -116,11 +116,9 @@ export const controlNetSlice = createSlice({
|
||||
},
|
||||
isControlNetImageProcessedToggled: (
|
||||
state,
|
||||
action: PayloadAction<{
|
||||
controlNetId: string;
|
||||
}>
|
||||
action: PayloadAction<string>
|
||||
) => {
|
||||
const { controlNetId } = action.payload;
|
||||
const controlNetId = action.payload;
|
||||
state.controlNets[controlNetId].isControlImageProcessed =
|
||||
!state.controlNets[controlNetId].isControlImageProcessed;
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user