Merge branch 'main' into feat/nodes-phase-5

This commit is contained in:
psychedelicious 2023-08-30 12:13:08 +10:00 committed by GitHub
commit 71591d0bee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 467 additions and 182 deletions

View File

@ -506,12 +506,13 @@
"hiresStrength": "High Res Strength",
"imageFit": "Fit Initial Image To Output Size",
"codeformerFidelity": "Fidelity",
"compositingSettingsHeader": "Compositing Settings",
"maskAdjustmentsHeader": "Mask Adjustments",
"maskBlur": "Mask Blur",
"maskBlurMethod": "Mask Blur Method",
"maskBlur": "Blur",
"maskBlurMethod": "Blur Method",
"coherencePassHeader": "Coherence Pass",
"coherenceSteps": "Coherence Pass Steps",
"coherenceStrength": "Coherence Pass Strength",
"coherenceSteps": "Steps",
"coherenceStrength": "Strength",
"seamLowThreshold": "Low",
"seamHighThreshold": "High",
"scaleBeforeProcessing": "Scale Before Processing",
@ -569,6 +570,7 @@
"useSlidersForAll": "Use Sliders For All Options",
"showProgressInViewer": "Show Progress Images in Viewer",
"antialiasProgressImages": "Antialias Progress Images",
"autoChangeDimensions": "Update W/H To Model Defaults On Change",
"resetWebUI": "Reset Web UI",
"resetWebUIDesc1": "Resetting the web UI only resets the browser's local cache of your images and remembered settings. It does not delete any images from disk.",
"resetWebUIDesc2": "If images aren't showing up in the gallery or something else isn't working, please try resetting before submitting an issue on GitHub.",
@ -712,7 +714,8 @@
"ui": {
"showProgressImages": "Show Progress Images",
"hideProgressImages": "Hide Progress Images",
"swapSizes": "Swap Sizes"
"swapSizes": "Swap Sizes",
"lockRatio": "Lock Ratio"
},
"nodes": {
"reloadNodeTemplates": "Reload Node Templates",

View File

@ -1,9 +1,12 @@
import { logger } from 'app/logging/logger';
import { setBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
import { controlNetRemoved } from 'features/controlNet/store/controlNetSlice';
import { loraRemoved } from 'features/lora/store/loraSlice';
import { modelSelected } from 'features/parameters/store/actions';
import {
modelChanged,
setHeight,
setWidth,
vaeSelected,
} from 'features/parameters/store/generationSlice';
import { zMainOrOnnxModel } from 'features/parameters/types/parameterSchemas';
@ -74,6 +77,22 @@ export const addModelSelectedListener = () => {
}
}
// Update Width / Height / Bounding Box Dimensions on Model Change
if (
state.generation.model?.base_model !== newModel.base_model &&
state.ui.shouldAutoChangeDimensions
) {
if (['sdxl', 'sdxl-refiner'].includes(newModel.base_model)) {
dispatch(setWidth(1024));
dispatch(setHeight(1024));
dispatch(setBoundingBoxDimensions({ width: 1024, height: 1024 }));
} else {
dispatch(setWidth(512));
dispatch(setHeight(512));
dispatch(setBoundingBoxDimensions({ width: 512, height: 512 }));
}
}
dispatch(modelChanged(newModel));
},
});

View File

@ -6,11 +6,11 @@ import {
configureStore,
} from '@reduxjs/toolkit';
import canvasReducer from 'features/canvas/store/canvasSlice';
import changeBoardModalReducer from 'features/changeBoardModal/store/slice';
import controlNetReducer from 'features/controlNet/store/controlNetSlice';
import deleteImageModalReducer from 'features/deleteImageModal/store/slice';
import dynamicPromptsReducer from 'features/dynamicPrompts/store/dynamicPromptsSlice';
import galleryReducer from 'features/gallery/store/gallerySlice';
import deleteImageModalReducer from 'features/deleteImageModal/store/slice';
import changeBoardModalReducer from 'features/changeBoardModal/store/slice';
import loraReducer from 'features/lora/store/loraSlice';
import nodesReducer from 'features/nodes/store/nodesSlice';
import generationReducer from 'features/parameters/store/generationSlice';

View File

@ -86,8 +86,8 @@ const IAICollapse = (props: IAIToggleCollapseProps) => {
<Collapse in={isOpen} animateOpacity style={{ overflow: 'unset' }}>
<Box
sx={{
p: 2,
pt: 3,
p: 4,
pb: 4,
borderBottomRadius: 'base',
bg: 'base.150',
_dark: {

View File

@ -5,16 +5,20 @@ import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIDndImage from 'common/components/IAIDndImage';
import { setBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
import {
TypesafeDraggableData,
TypesafeDroppableData,
} from 'features/dnd/types';
import { setHeight, setWidth } from 'features/parameters/store/generationSlice';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { memo, useCallback, useMemo, useState } from 'react';
import { FaSave, FaUndo } from 'react-icons/fa';
import { FaRulerVertical, FaSave, FaUndo } from 'react-icons/fa';
import {
useAddImageToBoardMutation,
useChangeImageIsIntermediateMutation,
useGetImageDTOQuery,
useRemoveImageFromBoardMutation,
} from 'services/api/endpoints/images';
import { PostUploadAction } from 'services/api/types';
import IAIDndImageIcon from '../../../common/components/IAIDndImageIcon';
@ -54,6 +58,7 @@ const ControlNetImagePreview = ({ isSmall, controlNet }: Props) => {
const dispatch = useAppDispatch();
const { pendingControlImages, autoAddBoardId } = useAppSelector(selector);
const activeTabName = useAppSelector(activeTabNameSelector);
const [isMouseOverImage, setIsMouseOverImage] = useState(false);
@ -67,23 +72,54 @@ const ControlNetImagePreview = ({ isSmall, controlNet }: Props) => {
const [changeIsIntermediate] = useChangeImageIsIntermediateMutation();
const [addToBoard] = useAddImageToBoardMutation();
const [removeFromBoard] = useRemoveImageFromBoardMutation();
const handleResetControlImage = useCallback(() => {
dispatch(controlNetImageChanged({ controlNetId, controlImage: null }));
}, [controlNetId, dispatch]);
const handleSaveControlImage = useCallback(() => {
const handleSaveControlImage = useCallback(async () => {
if (!processedControlImage) {
return;
}
changeIsIntermediate({
await changeIsIntermediate({
imageDTO: processedControlImage,
is_intermediate: false,
});
}).unwrap();
addToBoard({ imageDTO: processedControlImage, board_id: autoAddBoardId });
}, [processedControlImage, autoAddBoardId, changeIsIntermediate, addToBoard]);
if (autoAddBoardId !== 'none') {
addToBoard({
imageDTO: processedControlImage,
board_id: autoAddBoardId,
});
} else {
removeFromBoard({ imageDTO: processedControlImage });
}
}, [
processedControlImage,
changeIsIntermediate,
autoAddBoardId,
addToBoard,
removeFromBoard,
]);
const handleSetControlImageToDimensions = useCallback(() => {
if (!processedControlImage) {
return;
}
if (activeTabName === 'unifiedCanvas') {
dispatch(
setBoundingBoxDimensions({
width: processedControlImage.width,
height: processedControlImage.height,
})
);
} else {
dispatch(setWidth(processedControlImage.width));
dispatch(setHeight(processedControlImage.height));
}
}, [processedControlImage, activeTabName, dispatch]);
const handleMouseEnter = useCallback(() => {
setIsMouseOverImage(true);
@ -144,21 +180,7 @@ const ControlNetImagePreview = ({ isSmall, controlNet }: Props) => {
imageDTO={controlImage}
isDropDisabled={shouldShowProcessedImage || !isEnabled}
postUploadAction={postUploadAction}
>
<>
<IAIDndImageIcon
onClick={handleResetControlImage}
icon={controlImage ? <FaUndo /> : undefined}
tooltip="Reset Control Image"
/>
<IAIDndImageIcon
onClick={handleSaveControlImage}
icon={controlImage ? <FaSave size={16} /> : undefined}
tooltip="Save Control Image"
styleOverrides={{ marginTop: 6 }}
/>
</>
</IAIDndImage>
/>
<Box
sx={{
@ -179,22 +201,29 @@ const ControlNetImagePreview = ({ isSmall, controlNet }: Props) => {
imageDTO={processedControlImage}
isUploadDisabled={true}
isDropDisabled={!isEnabled}
>
<>
<IAIDndImageIcon
onClick={handleResetControlImage}
icon={controlImage ? <FaUndo /> : undefined}
tooltip="Reset Control Image"
/>
<IAIDndImageIcon
onClick={handleSaveControlImage}
icon={controlImage ? <FaSave size={16} /> : undefined}
tooltip="Save Control Image"
styleOverrides={{ marginTop: 6 }}
/>
</>
</IAIDndImage>
/>
</Box>
<>
<IAIDndImageIcon
onClick={handleResetControlImage}
icon={controlImage ? <FaUndo /> : undefined}
tooltip="Reset Control Image"
/>
<IAIDndImageIcon
onClick={handleSaveControlImage}
icon={controlImage ? <FaSave size={16} /> : undefined}
tooltip="Save Control Image"
styleOverrides={{ marginTop: 6 }}
/>
<IAIDndImageIcon
onClick={handleSetControlImageToDimensions}
icon={controlImage ? <FaRulerVertical size={16} /> : undefined}
tooltip="Set Control Image Dimensions To W/H"
styleOverrides={{ marginTop: 12 }}
/>
</>
{pendingControlImages.includes(controlNetId) && (
<Flex
sx={{

View File

@ -4,11 +4,11 @@ import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAICollapse from 'common/components/IAICollapse';
import { memo } from 'react';
import { useFeatureStatus } from '../../system/hooks/useFeatureStatus';
import ParamDynamicPromptsCombinatorial from './ParamDynamicPromptsCombinatorial';
import ParamDynamicPromptsToggle from './ParamDynamicPromptsEnabled';
import ParamDynamicPromptsMaxPrompts from './ParamDynamicPromptsMaxPrompts';
import { useFeatureStatus } from '../../system/hooks/useFeatureStatus';
import { memo } from 'react';
const selector = createSelector(
stateSelector,

View File

@ -39,7 +39,7 @@ const ImageGalleryContent = () => {
const { galleryView } = useAppSelector(selector);
const dispatch = useAppDispatch();
const { isOpen: isBoardListOpen, onToggle: onToggleBoardList } =
useDisclosure();
useDisclosure({ defaultIsOpen: true });
const handleClickImages = useCallback(() => {
dispatch(galleryViewChanged('images'));

View File

@ -14,8 +14,9 @@ const selector = createSelector(
[stateSelector, isStagingSelector],
({ canvas, generation }, isStaging) => {
const { boundingBoxDimensions } = canvas;
const { aspectRatio } = generation;
const { model, aspectRatio } = generation;
return {
model,
boundingBoxDimensions,
isStaging,
aspectRatio,
@ -26,11 +27,15 @@ const selector = createSelector(
const ParamBoundingBoxWidth = () => {
const dispatch = useAppDispatch();
const { boundingBoxDimensions, isStaging, aspectRatio } =
const { model, boundingBoxDimensions, isStaging, aspectRatio } =
useAppSelector(selector);
const { t } = useTranslation();
const initial = ['sdxl', 'sdxl-refiner'].includes(model?.base_model as string)
? 1024
: 512;
const handleChangeHeight = (v: number) => {
dispatch(
setBoundingBoxDimensions({
@ -53,15 +58,15 @@ const ParamBoundingBoxWidth = () => {
dispatch(
setBoundingBoxDimensions({
...boundingBoxDimensions,
height: Math.floor(512),
height: Math.floor(initial),
})
);
if (aspectRatio) {
const newWidth = roundToMultiple(512 * aspectRatio, 64);
const newWidth = roundToMultiple(initial * aspectRatio, 64);
dispatch(
setBoundingBoxDimensions({
width: newWidth,
height: Math.floor(512),
height: Math.floor(initial),
})
);
}
@ -71,7 +76,7 @@ const ParamBoundingBoxWidth = () => {
<IAISlider
label={t('parameters.boundingBoxHeight')}
min={64}
max={1024}
max={1536}
step={64}
value={boundingBoxDimensions.height}
onChange={handleChangeHeight}

View File

@ -1,17 +1,83 @@
import { Flex, Spacer, Text } from '@chakra-ui/react';
import { useAppDispatch } from 'app/store/storeHooks';
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIIconButton from 'common/components/IAIIconButton';
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
import { flipBoundingBoxAxes } from 'features/canvas/store/canvasSlice';
import { generationSelector } from 'features/parameters/store/generationSelectors';
import {
setAspectRatio,
setShouldLockAspectRatio,
} from 'features/parameters/store/generationSlice';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { FaLock } from 'react-icons/fa';
import { MdOutlineSwapVert } from 'react-icons/md';
import ParamAspectRatio from '../../Core/ParamAspectRatio';
import ParamAspectRatio, {
mappedAspectRatios,
} from '../../Core/ParamAspectRatio';
import ParamBoundingBoxHeight from './ParamBoundingBoxHeight';
import ParamBoundingBoxWidth from './ParamBoundingBoxWidth';
const sizeOptsSelector = createSelector(
[generationSelector, canvasSelector],
(generation, canvas) => {
const { shouldFitToWidthHeight, shouldLockAspectRatio } = generation;
const { boundingBoxDimensions } = canvas;
return {
shouldFitToWidthHeight,
shouldLockAspectRatio,
boundingBoxDimensions,
};
}
);
export default function ParamBoundingBoxSize() {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const { shouldLockAspectRatio, boundingBoxDimensions } =
useAppSelector(sizeOptsSelector);
const handleLockRatio = useCallback(() => {
if (shouldLockAspectRatio) {
dispatch(setShouldLockAspectRatio(false));
if (
!mappedAspectRatios.includes(
boundingBoxDimensions.width / boundingBoxDimensions.height
)
) {
dispatch(setAspectRatio(null));
} else {
dispatch(
setAspectRatio(
boundingBoxDimensions.width / boundingBoxDimensions.height
)
);
}
} else {
dispatch(setShouldLockAspectRatio(true));
dispatch(
setAspectRatio(
boundingBoxDimensions.width / boundingBoxDimensions.height
)
);
}
}, [shouldLockAspectRatio, boundingBoxDimensions, dispatch]);
const handleToggleSize = useCallback(() => {
dispatch(flipBoundingBoxAxes());
dispatch(setAspectRatio(null));
if (shouldLockAspectRatio) {
dispatch(
setAspectRatio(
boundingBoxDimensions.height / boundingBoxDimensions.width
)
);
}
}, [dispatch, shouldLockAspectRatio, boundingBoxDimensions]);
return (
<Flex
sx={{
@ -20,7 +86,7 @@ export default function ParamBoundingBoxSize() {
borderRadius: 4,
flexDirection: 'column',
w: 'full',
bg: 'base.150',
bg: 'base.100',
_dark: {
bg: 'base.750',
},
@ -47,7 +113,15 @@ export default function ParamBoundingBoxSize() {
size="sm"
icon={<MdOutlineSwapVert />}
fontSize={20}
onClick={() => dispatch(flipBoundingBoxAxes())}
onClick={handleToggleSize}
/>
<IAIIconButton
tooltip={t('ui.lockRatio')}
aria-label={t('ui.lockRatio')}
size="sm"
icon={<FaLock />}
isChecked={shouldLockAspectRatio}
onClick={handleLockRatio}
/>
</Flex>
<ParamBoundingBoxWidth />

View File

@ -14,8 +14,9 @@ const selector = createSelector(
[stateSelector, isStagingSelector],
({ canvas, generation }, isStaging) => {
const { boundingBoxDimensions } = canvas;
const { aspectRatio } = generation;
const { model, aspectRatio } = generation;
return {
model,
boundingBoxDimensions,
isStaging,
aspectRatio,
@ -26,9 +27,13 @@ const selector = createSelector(
const ParamBoundingBoxWidth = () => {
const dispatch = useAppDispatch();
const { boundingBoxDimensions, isStaging, aspectRatio } =
const { model, boundingBoxDimensions, isStaging, aspectRatio } =
useAppSelector(selector);
const initial = ['sdxl', 'sdxl-refiner'].includes(model?.base_model as string)
? 1024
: 512;
const { t } = useTranslation();
const handleChangeWidth = (v: number) => {
@ -53,14 +58,14 @@ const ParamBoundingBoxWidth = () => {
dispatch(
setBoundingBoxDimensions({
...boundingBoxDimensions,
width: Math.floor(512),
width: Math.floor(initial),
})
);
if (aspectRatio) {
const newHeight = roundToMultiple(512 / aspectRatio, 64);
const newHeight = roundToMultiple(initial / aspectRatio, 64);
dispatch(
setBoundingBoxDimensions({
width: Math.floor(512),
width: Math.floor(initial),
height: newHeight,
})
);
@ -71,7 +76,7 @@ const ParamBoundingBoxWidth = () => {
<IAISlider
label={t('parameters.boundingBoxWidth')}
min={64}
max={1024}
max={1536}
step={64}
value={boundingBoxDimensions.width}
onChange={handleChangeWidth}

View File

@ -0,0 +1,31 @@
import { Divider, Flex } from '@chakra-ui/react';
import IAICollapse from 'common/components/IAICollapse';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import SubParametersWrapper from '../../SubParametersWrapper';
import ParamCanvasCoherenceSteps from './CoherencePass/ParamCanvasCoherenceSteps';
import ParamCanvasCoherenceStrength from './CoherencePass/ParamCanvasCoherenceStrength';
import ParamMaskBlur from './MaskAdjustment/ParamMaskBlur';
import ParamMaskBlurMethod from './MaskAdjustment/ParamMaskBlurMethod';
const ParamCompositingSettingsCollapse = () => {
const { t } = useTranslation();
return (
<IAICollapse label={t('parameters.compositingSettingsHeader')}>
<Flex sx={{ flexDirection: 'column', gap: 2 }}>
<SubParametersWrapper label={t('parameters.maskAdjustmentsHeader')}>
<ParamMaskBlur />
<ParamMaskBlurMethod />
</SubParametersWrapper>
<Divider />
<SubParametersWrapper label={t('parameters.coherencePassHeader')}>
<ParamCanvasCoherenceSteps />
<ParamCanvasCoherenceStrength />
</SubParametersWrapper>
</Flex>
</IAICollapse>
);
};
export default memo(ParamCompositingSettingsCollapse);

View File

@ -1,8 +1,9 @@
import { Flex } from '@chakra-ui/react';
import { Divider, Flex } from '@chakra-ui/react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import IAICollapse from 'common/components/IAICollapse';
import SubParametersWrapper from '../../SubParametersWrapper';
import ParamInfillMethod from './ParamInfillMethod';
import ParamInfillTilesize from './ParamInfillTilesize';
import ParamScaleBeforeProcessing from './ParamScaleBeforeProcessing';
@ -15,11 +16,16 @@ const ParamInfillCollapse = () => {
return (
<IAICollapse label={t('parameters.infillScalingHeader')}>
<Flex sx={{ gap: 2, flexDirection: 'column' }}>
<ParamInfillMethod />
<ParamInfillTilesize />
<ParamScaleBeforeProcessing />
<ParamScaledWidth />
<ParamScaledHeight />
<SubParametersWrapper>
<ParamInfillMethod />
<ParamInfillTilesize />
</SubParametersWrapper>
<Divider />
<SubParametersWrapper>
<ParamScaleBeforeProcessing />
<ParamScaledWidth />
<ParamScaledHeight />
</SubParametersWrapper>
</Flex>
</IAICollapse>
);

View File

@ -5,16 +5,17 @@ import IAISlider from 'common/components/IAISlider';
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
import { setScaledBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
import { generationSelector } from 'features/parameters/store/generationSelectors';
import { systemSelector } from 'features/system/store/systemSelectors';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
const selector = createSelector(
[generationSelector, systemSelector, canvasSelector],
(parameters, system, canvas) => {
[generationSelector, canvasSelector],
(generation, canvas) => {
const { scaledBoundingBoxDimensions, boundingBoxScaleMethod } = canvas;
const { model } = generation;
return {
model,
scaledBoundingBoxDimensions,
isManual: boundingBoxScaleMethod === 'manual',
};
@ -24,7 +25,12 @@ const selector = createSelector(
const ParamScaledHeight = () => {
const dispatch = useAppDispatch();
const { isManual, scaledBoundingBoxDimensions } = useAppSelector(selector);
const { model, isManual, scaledBoundingBoxDimensions } =
useAppSelector(selector);
const initial = ['sdxl', 'sdxl-refiner'].includes(model?.base_model as string)
? 1024
: 512;
const { t } = useTranslation();
@ -41,7 +47,7 @@ const ParamScaledHeight = () => {
dispatch(
setScaledBoundingBoxDimensions({
...scaledBoundingBoxDimensions,
height: Math.floor(512),
height: Math.floor(initial),
})
);
};
@ -51,7 +57,7 @@ const ParamScaledHeight = () => {
isDisabled={!isManual}
label={t('parameters.scaledHeight')}
min={64}
max={1024}
max={1536}
step={64}
value={scaledBoundingBoxDimensions.height}
onChange={handleChangeScaledHeight}

View File

@ -4,15 +4,18 @@ import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAISlider from 'common/components/IAISlider';
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
import { setScaledBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
import { generationSelector } from 'features/parameters/store/generationSelectors';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
const selector = createSelector(
[canvasSelector],
(canvas) => {
[canvasSelector, generationSelector],
(canvas, generation) => {
const { boundingBoxScaleMethod, scaledBoundingBoxDimensions } = canvas;
const { model } = generation;
return {
model,
scaledBoundingBoxDimensions,
isManual: boundingBoxScaleMethod === 'manual',
};
@ -22,7 +25,12 @@ const selector = createSelector(
const ParamScaledWidth = () => {
const dispatch = useAppDispatch();
const { isManual, scaledBoundingBoxDimensions } = useAppSelector(selector);
const { model, isManual, scaledBoundingBoxDimensions } =
useAppSelector(selector);
const initial = ['sdxl', 'sdxl-refiner'].includes(model?.base_model as string)
? 1024
: 512;
const { t } = useTranslation();
@ -39,7 +47,7 @@ const ParamScaledWidth = () => {
dispatch(
setScaledBoundingBoxDimensions({
...scaledBoundingBoxDimensions,
width: Math.floor(512),
width: Math.floor(initial),
})
);
};
@ -49,7 +57,7 @@ const ParamScaledWidth = () => {
isDisabled={!isManual}
label={t('parameters.scaledWidth')}
min={64}
max={1024}
max={1536}
step={64}
value={scaledBoundingBoxDimensions.width}
onChange={handleChangeScaledWidth}

View File

@ -1,21 +0,0 @@
import { Flex } from '@chakra-ui/react';
import IAICollapse from 'common/components/IAICollapse';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import ParamMaskBlur from './ParamMaskBlur';
import ParamMaskBlurMethod from './ParamMaskBlurMethod';
const ParamMaskAdjustmentCollapse = () => {
const { t } = useTranslation();
return (
<IAICollapse label={t('parameters.maskAdjustmentsHeader')}>
<Flex sx={{ flexDirection: 'column', gap: 2 }}>
<ParamMaskBlur />
<ParamMaskBlurMethod />
</Flex>
</IAICollapse>
);
};
export default memo(ParamMaskAdjustmentCollapse);

View File

@ -1,21 +0,0 @@
import { Flex } from '@chakra-ui/react';
import IAICollapse from 'common/components/IAICollapse';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import ParamCanvasCoherenceSteps from './ParamCanvasCoherenceSteps';
import ParamCanvasCoherenceStrength from './ParamCanvasCoherenceStrength';
const ParamCanvasCoherencePassCollapse = () => {
const { t } = useTranslation();
return (
<IAICollapse label={t('parameters.coherencePassHeader')}>
<Flex sx={{ flexDirection: 'column', gap: 2, paddingBottom: 2 }}>
<ParamCanvasCoherenceSteps />
<ParamCanvasCoherenceStrength />
</Flex>
</IAICollapse>
);
};
export default memo(ParamCanvasCoherencePassCollapse);

View File

@ -2,7 +2,10 @@ import { ButtonGroup, Flex } from '@chakra-ui/react';
import { RootState } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIButton from 'common/components/IAIButton';
import { setAspectRatio } from 'features/parameters/store/generationSlice';
import {
setAspectRatio,
setShouldLockAspectRatio,
} from 'features/parameters/store/generationSlice';
import { activeTabNameSelector } from '../../../../ui/store/uiSelectors';
const aspectRatios = [
@ -12,6 +15,8 @@ const aspectRatios = [
{ name: '1:1', value: 1 / 1 },
];
export const mappedAspectRatios = aspectRatios.map((ar) => ar.value);
export default function ParamAspectRatio() {
const aspectRatio = useAppSelector(
(state: RootState) => state.generation.aspectRatio
@ -34,7 +39,10 @@ export default function ParamAspectRatio() {
isDisabled={
activeTabName === 'img2img' ? !shouldFitToWidthHeight : false
}
onClick={() => dispatch(setAspectRatio(ratio.value))}
onClick={() => {
dispatch(setAspectRatio(ratio.value));
dispatch(setShouldLockAspectRatio(false));
}}
>
{ratio.name}
</IAIButton>

View File

@ -11,16 +11,15 @@ import { useTranslation } from 'react-i18next';
const selector = createSelector(
[stateSelector],
({ generation, hotkeys, config }) => {
const { initial, min, sliderMax, inputMax, fineStep, coarseStep } =
config.sd.height;
const { height } = generation;
const { min, sliderMax, inputMax, fineStep, coarseStep } = config.sd.height;
const { model, height } = generation;
const { aspectRatio } = generation;
const step = hotkeys.shift ? fineStep : coarseStep;
return {
model,
height,
initial,
min,
sliderMax,
inputMax,
@ -37,11 +36,15 @@ type ParamHeightProps = Omit<
>;
const ParamHeight = (props: ParamHeightProps) => {
const { height, initial, min, sliderMax, inputMax, step, aspectRatio } =
const { model, height, min, sliderMax, inputMax, step, aspectRatio } =
useAppSelector(selector);
const dispatch = useAppDispatch();
const { t } = useTranslation();
const initial = ['sdxl', 'sdxl-refiner'].includes(model?.base_model as string)
? 1024
: 512;
const handleChange = useCallback(
(v: number) => {
dispatch(setHeight(v));

View File

@ -1,22 +1,71 @@
import { Flex, Spacer, Text } from '@chakra-ui/react';
import { RootState } from 'app/store/store';
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIIconButton from 'common/components/IAIIconButton';
import { toggleSize } from 'features/parameters/store/generationSlice';
import { generationSelector } from 'features/parameters/store/generationSelectors';
import {
setAspectRatio,
setShouldLockAspectRatio,
toggleSize,
} from 'features/parameters/store/generationSlice';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { FaLock } from 'react-icons/fa';
import { MdOutlineSwapVert } from 'react-icons/md';
import ParamAspectRatio from './ParamAspectRatio';
import { activeTabNameSelector } from '../../../../ui/store/uiSelectors';
import ParamAspectRatio, { mappedAspectRatios } from './ParamAspectRatio';
import ParamHeight from './ParamHeight';
import ParamWidth from './ParamWidth';
import { activeTabNameSelector } from '../../../../ui/store/uiSelectors';
const sizeOptsSelector = createSelector(
[generationSelector, activeTabNameSelector],
(generation, activeTabName) => {
const { shouldFitToWidthHeight, shouldLockAspectRatio, width, height } =
generation;
return {
activeTabName,
shouldFitToWidthHeight,
shouldLockAspectRatio,
width,
height,
};
}
);
export default function ParamSize() {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const shouldFitToWidthHeight = useAppSelector(
(state: RootState) => state.generation.shouldFitToWidthHeight
);
const activeTabName = useAppSelector(activeTabNameSelector);
const {
activeTabName,
shouldFitToWidthHeight,
shouldLockAspectRatio,
width,
height,
} = useAppSelector(sizeOptsSelector);
const handleLockRatio = useCallback(() => {
if (shouldLockAspectRatio) {
dispatch(setShouldLockAspectRatio(false));
if (!mappedAspectRatios.includes(width / height)) {
dispatch(setAspectRatio(null));
} else {
dispatch(setAspectRatio(width / height));
}
} else {
dispatch(setShouldLockAspectRatio(true));
dispatch(setAspectRatio(width / height));
}
}, [shouldLockAspectRatio, width, height, dispatch]);
const handleToggleSize = useCallback(() => {
dispatch(toggleSize());
dispatch(setAspectRatio(null));
if (shouldLockAspectRatio) {
dispatch(setAspectRatio(height / width));
}
}, [dispatch, shouldLockAspectRatio, width, height]);
return (
<Flex
sx={{
@ -55,7 +104,18 @@ export default function ParamSize() {
isDisabled={
activeTabName === 'img2img' ? !shouldFitToWidthHeight : false
}
onClick={() => dispatch(toggleSize())}
onClick={handleToggleSize}
/>
<IAIIconButton
tooltip={t('ui.lockRatio')}
aria-label={t('ui.lockRatio')}
size="sm"
icon={<FaLock />}
isChecked={shouldLockAspectRatio}
isDisabled={
activeTabName === 'img2img' ? !shouldFitToWidthHeight : false
}
onClick={handleLockRatio}
/>
</Flex>
<Flex gap={2} alignItems="center">

View File

@ -11,15 +11,14 @@ import { useTranslation } from 'react-i18next';
const selector = createSelector(
[stateSelector],
({ generation, hotkeys, config }) => {
const { initial, min, sliderMax, inputMax, fineStep, coarseStep } =
config.sd.width;
const { width, aspectRatio } = generation;
const { min, sliderMax, inputMax, fineStep, coarseStep } = config.sd.width;
const { model, width, aspectRatio } = generation;
const step = hotkeys.shift ? fineStep : coarseStep;
return {
model,
width,
initial,
min,
sliderMax,
inputMax,
@ -33,11 +32,15 @@ const selector = createSelector(
type ParamWidthProps = Omit<IAIFullSliderProps, 'label' | 'value' | 'onChange'>;
const ParamWidth = (props: ParamWidthProps) => {
const { width, initial, min, sliderMax, inputMax, step, aspectRatio } =
const { model, width, min, sliderMax, inputMax, step, aspectRatio } =
useAppSelector(selector);
const dispatch = useAppDispatch();
const { t } = useTranslation();
const initial = ['sdxl', 'sdxl-refiner'].includes(model?.base_model as string)
? 1024
: 512;
const handleChange = useCallback(
(v: number) => {
dispatch(setWidth(v));

View File

@ -6,6 +6,7 @@ import IAISlider from 'common/components/IAISlider';
import { setImg2imgStrength } from 'features/parameters/store/generationSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import SubParametersWrapper from '../SubParametersWrapper';
const selector = createSelector(
[stateSelector],
@ -44,20 +45,22 @@ const ImageToImageStrength = () => {
}, [dispatch, initial]);
return (
<IAISlider
label={`${t('parameters.denoisingStrength')}`}
step={step}
min={min}
max={sliderMax}
onChange={handleChange}
handleReset={handleReset}
value={img2imgStrength}
isInteger={false}
withInput
withSliderMarks
withReset
sliderNumberInputProps={{ max: inputMax }}
/>
<SubParametersWrapper>
<IAISlider
label={`${t('parameters.denoisingStrength')}`}
step={step}
min={min}
max={sliderMax}
onChange={handleChange}
handleReset={handleReset}
value={img2imgStrength}
isInteger={false}
withInput
withSliderMarks
withReset
sliderNumberInputProps={{ max: inputMax }}
/>
</SubParametersWrapper>
);
};

View File

@ -0,0 +1,39 @@
import { Flex, Text } from '@chakra-ui/react';
import { ReactNode, memo } from 'react';
type SubParameterWrapperProps = {
children: ReactNode | ReactNode[];
label?: string;
};
const SubParametersWrapper = (props: SubParameterWrapperProps) => (
<Flex
sx={{
flexDir: 'column',
gap: 2,
bg: 'base.100',
px: 4,
pt: 2,
pb: 4,
borderRadius: 'base',
_dark: {
bg: 'base.750',
},
}}
>
{props.label && (
<Text
fontSize="sm"
fontWeight="bold"
sx={{ color: 'base.600', _dark: { color: 'base.300' } }}
>
{props.label}
</Text>
)}
{props.children}
</Flex>
);
SubParametersWrapper.displayName = 'SubSettingsWrapper';
export default memo(SubParametersWrapper);

View File

@ -62,6 +62,7 @@ export interface GenerationState {
shouldUseCpuNoise: boolean;
shouldShowAdvancedOptions: boolean;
aspectRatio: number | null;
shouldLockAspectRatio: boolean;
}
export const initialGenerationState: GenerationState = {
@ -101,6 +102,7 @@ export const initialGenerationState: GenerationState = {
shouldUseCpuNoise: true,
shouldShowAdvancedOptions: false,
aspectRatio: null,
shouldLockAspectRatio: false,
};
const initialState: GenerationState = initialGenerationState;
@ -272,6 +274,9 @@ export const generationSlice = createSlice({
state.height = roundToMultiple(state.width / newAspectRatio, 8);
}
},
setShouldLockAspectRatio: (state, action: PayloadAction<boolean>) => {
state.shouldLockAspectRatio = action.payload;
},
},
extraReducers: (builder) => {
builder.addCase(configChanged, (state, action) => {
@ -342,6 +347,7 @@ export const {
shouldUseCpuNoiseChanged,
setShouldShowAdvancedOptions,
setAspectRatio,
setShouldLockAspectRatio,
vaePrecisionChanged,
} = generationSlice.actions;

View File

@ -3,6 +3,7 @@ import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAISlider from 'common/components/IAISlider';
import SubParametersWrapper from 'features/parameters/components/Parameters/SubParametersWrapper';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { setSDXLImg2ImgDenoisingStrength } from '../store/sdxlSlice';
@ -34,19 +35,21 @@ const ParamSDXLImg2ImgDenoisingStrength = () => {
}, [dispatch]);
return (
<IAISlider
label={`${t('parameters.denoisingStrength')}`}
step={0.01}
min={0}
max={1}
onChange={handleChange}
handleReset={handleReset}
value={sdxlImg2ImgDenoisingStrength}
isInteger={false}
withInput
withSliderMarks
withReset
/>
<SubParametersWrapper>
<IAISlider
label={`${t('parameters.denoisingStrength')}`}
step={0.01}
min={0}
max={1}
onChange={handleChange}
handleReset={handleReset}
value={sdxlImg2ImgDenoisingStrength}
isInteger={false}
withInput
withSliderMarks
withReset
/>
</SubParametersWrapper>
);
};

View File

@ -4,6 +4,7 @@ import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAICollapse from 'common/components/IAICollapse';
import { memo } from 'react';
import ParamSDXLRefinerCFGScale from './SDXLRefiner/ParamSDXLRefinerCFGScale';
import ParamSDXLRefinerModelSelect from './SDXLRefiner/ParamSDXLRefinerModelSelect';
import ParamSDXLRefinerNegativeAestheticScore from './SDXLRefiner/ParamSDXLRefinerNegativeAestheticScore';
@ -12,7 +13,6 @@ import ParamSDXLRefinerScheduler from './SDXLRefiner/ParamSDXLRefinerScheduler';
import ParamSDXLRefinerStart from './SDXLRefiner/ParamSDXLRefinerStart';
import ParamSDXLRefinerSteps from './SDXLRefiner/ParamSDXLRefinerSteps';
import ParamUseSDXLRefiner from './SDXLRefiner/ParamUseSDXLRefiner';
import { memo } from 'react';
const selector = createSelector(
stateSelector,

View File

@ -1,8 +1,7 @@
import ParamDynamicPromptsCollapse from 'features/dynamicPrompts/components/ParamDynamicPromptsCollapse';
import ParamLoraCollapse from 'features/lora/components/ParamLoraCollapse';
import ParamCompositingSettingsCollapse from 'features/parameters/components/Parameters/Canvas/Compositing/ParamCompositingSettingsCollapse';
import ParamInfillAndScalingCollapse from 'features/parameters/components/Parameters/Canvas/InfillAndScaling/ParamInfillAndScalingCollapse';
import ParamMaskAdjustmentCollapse from 'features/parameters/components/Parameters/Canvas/MaskAdjustment/ParamMaskAdjustmentCollapse';
import ParamCanvasCoherencePassCollapse from 'features/parameters/components/Parameters/Canvas/SeamPainting/ParamCanvasCoherencePassCollapse';
import ParamControlNetCollapse from 'features/parameters/components/Parameters/ControlNet/ParamControlNetCollapse';
import ParamNoiseCollapse from 'features/parameters/components/Parameters/Noise/ParamNoiseCollapse';
import ParamSeamlessCollapse from 'features/parameters/components/Parameters/Seamless/ParamSeamlessCollapse';
@ -20,9 +19,8 @@ export default function SDXLUnifiedCanvasTabParameters() {
<ParamLoraCollapse />
<ParamDynamicPromptsCollapse />
<ParamNoiseCollapse />
<ParamMaskAdjustmentCollapse />
<ParamInfillAndScalingCollapse />
<ParamCanvasCoherencePassCollapse />
<ParamCompositingSettingsCollapse />
<ParamSeamlessCollapse />
</>
);

View File

@ -30,6 +30,7 @@ import {
shouldUseWatermarkerChanged,
} from 'features/system/store/systemSlice';
import {
setShouldAutoChangeDimensions,
setShouldShowProgressInViewer,
setShouldUseSliders,
} from 'features/ui/store/uiSlice';
@ -68,7 +69,11 @@ const selector = createSelector(
shouldUseWatermarker,
} = system;
const { shouldUseSliders, shouldShowProgressInViewer } = ui;
const {
shouldUseSliders,
shouldShowProgressInViewer,
shouldAutoChangeDimensions,
} = ui;
const { shouldShowAdvancedOptions } = generation;
@ -83,6 +88,7 @@ const selector = createSelector(
shouldShowAdvancedOptions,
shouldUseNSFWChecker,
shouldUseWatermarker,
shouldAutoChangeDimensions,
};
},
{
@ -158,6 +164,7 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => {
shouldShowAdvancedOptions,
shouldUseNSFWChecker,
shouldUseWatermarker,
shouldAutoChangeDimensions,
} = useAppSelector(selector);
const handleClickResetWebUI = useCallback(() => {
@ -297,6 +304,13 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => {
)
}
/>
<SettingSwitch
label={t('settings.autoChangeDimensions')}
isChecked={shouldAutoChangeDimensions}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
dispatch(setShouldAutoChangeDimensions(e.target.checked))
}
/>
{shouldShowLocalizationToggle && (
<IAIMantineSelect
disabled={!isLocalizationEnabled}

View File

@ -1,9 +1,8 @@
import ParamDynamicPromptsCollapse from 'features/dynamicPrompts/components/ParamDynamicPromptsCollapse';
import ParamLoraCollapse from 'features/lora/components/ParamLoraCollapse';
import ParamAdvancedCollapse from 'features/parameters/components/Parameters/Advanced/ParamAdvancedCollapse';
import ParamCompositingSettingsCollapse from 'features/parameters/components/Parameters/Canvas/Compositing/ParamCompositingSettingsCollapse';
import ParamInfillAndScalingCollapse from 'features/parameters/components/Parameters/Canvas/InfillAndScaling/ParamInfillAndScalingCollapse';
import ParamMaskAdjustmentCollapse from 'features/parameters/components/Parameters/Canvas/MaskAdjustment/ParamMaskAdjustmentCollapse';
import ParamCanvasCoherencePassCollapse from 'features/parameters/components/Parameters/Canvas/SeamPainting/ParamCanvasCoherencePassCollapse';
import ParamControlNetCollapse from 'features/parameters/components/Parameters/ControlNet/ParamControlNetCollapse';
import ParamPromptArea from 'features/parameters/components/Parameters/Prompt/ParamPromptArea';
import ParamSeamlessCollapse from 'features/parameters/components/Parameters/Seamless/ParamSeamlessCollapse';
@ -20,9 +19,8 @@ const UnifiedCanvasParameters = () => {
<ParamLoraCollapse />
<ParamDynamicPromptsCollapse />
<ParamSymmetryCollapse />
<ParamMaskAdjustmentCollapse />
<ParamInfillAndScalingCollapse />
<ParamCanvasCoherencePassCollapse />
<ParamCompositingSettingsCollapse />
<ParamSeamlessCollapse />
<ParamAdvancedCollapse />
</>

View File

@ -15,6 +15,7 @@ export const initialUIState: UIState = {
shouldHidePreview: false,
shouldShowProgressInViewer: true,
shouldShowEmbeddingPicker: false,
shouldAutoChangeDimensions: false,
favoriteSchedulers: [],
globalContextMenuCloseTrigger: 0,
panels: {},
@ -57,6 +58,9 @@ export const uiSlice = createSlice({
toggleEmbeddingPicker: (state) => {
state.shouldShowEmbeddingPicker = !state.shouldShowEmbeddingPicker;
},
setShouldAutoChangeDimensions: (state, action: PayloadAction<boolean>) => {
state.shouldAutoChangeDimensions = action.payload;
},
contextMenusClosed: (state) => {
state.globalContextMenuCloseTrigger += 1;
},
@ -84,6 +88,7 @@ export const {
setShouldShowProgressInViewer,
favoriteSchedulersChanged,
toggleEmbeddingPicker,
setShouldAutoChangeDimensions,
contextMenusClosed,
panelsChanged,
} = uiSlice.actions;

View File

@ -21,6 +21,7 @@ export interface UIState {
shouldHidePreview: boolean;
shouldShowProgressInViewer: boolean;
shouldShowEmbeddingPicker: boolean;
shouldAutoChangeDimensions: boolean;
favoriteSchedulers: SchedulerParam[];
globalContextMenuCloseTrigger: number;
panels: Record<string, string>;