[WebUI] They see me slidin .. they hatin... (#2614)

Porting over as many usable options to slider as possible.

- Ported Face Restoration settings to Sliders.
- Ported Upscale Settings to Sliders.
- Ported Variation Amount to Sliders.
- Ported Noise Threshold to Sliders <-- Optimized slider so the values
actually make sense.
- Ported Perlin Noise to Sliders.
- Added a suboption hook for the High Res Strength Slider.
- Fixed a couple of small issues with the Slider component.
- Ported Main Options to Sliders.
This commit is contained in:
blessedcoolant 2023-02-17 21:58:35 +13:00 committed by GitHub
commit 41bc160cb8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 1239 additions and 938 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -5,8 +5,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-12bd70ca.js"></script> <script type="module" crossorigin src="./assets/index-9237ac63.js"></script>
<link rel="stylesheet" href="./assets/index-c1af841f.css"> <link rel="stylesheet" href="./assets/index-14cb2922.css">
</head> </head>
<body> <body>

View File

@ -1,10 +1,12 @@
{ {
"general": "General",
"images": "Images", "images": "Images",
"steps": "Steps", "steps": "Steps",
"cfgScale": "CFG Scale", "cfgScale": "CFG Scale",
"width": "Width", "width": "Width",
"height": "Height", "height": "Height",
"sampler": "Sampler", "sampler": "Sampler",
"imageToImage": "Image To Image",
"seed": "Seed", "seed": "Seed",
"randomizeSeed": "Randomize Seed", "randomizeSeed": "Randomize Seed",
"shuffle": "Shuffle", "shuffle": "Shuffle",

View File

@ -1,10 +1,12 @@
{ {
"general": "General",
"images": "Images", "images": "Images",
"steps": "Steps", "steps": "Steps",
"cfgScale": "CFG Scale", "cfgScale": "CFG Scale",
"width": "Width", "width": "Width",
"height": "Height", "height": "Height",
"sampler": "Sampler", "sampler": "Sampler",
"imageToImage": "Image To Image",
"seed": "Seed", "seed": "Seed",
"randomizeSeed": "Randomize Seed", "randomizeSeed": "Randomize Seed",
"shuffle": "Shuffle", "shuffle": "Shuffle",

View File

@ -5,6 +5,7 @@
"confirmOnDelete": "Confirm On Delete", "confirmOnDelete": "Confirm On Delete",
"displayHelpIcons": "Display Help Icons", "displayHelpIcons": "Display Help Icons",
"useCanvasBeta": "Use Canvas Beta Layout", "useCanvasBeta": "Use Canvas Beta Layout",
"useSlidersForAll": "Use Sliders For All Options",
"enableImageDebugging": "Enable Image Debugging", "enableImageDebugging": "Enable Image Debugging",
"resetWebUI": "Reset Web UI", "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.", "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.",

View File

@ -1,10 +1,12 @@
{ {
"general": "General",
"images": "Images", "images": "Images",
"steps": "Steps", "steps": "Steps",
"cfgScale": "CFG Scale", "cfgScale": "CFG Scale",
"width": "Width", "width": "Width",
"height": "Height", "height": "Height",
"sampler": "Sampler", "sampler": "Sampler",
"imageToImage": "Image To Image",
"seed": "Seed", "seed": "Seed",
"randomizeSeed": "Randomize Seed", "randomizeSeed": "Randomize Seed",
"shuffle": "Shuffle", "shuffle": "Shuffle",

View File

@ -1,10 +1,12 @@
{ {
"general": "General",
"images": "Images", "images": "Images",
"steps": "Steps", "steps": "Steps",
"cfgScale": "CFG Scale", "cfgScale": "CFG Scale",
"width": "Width", "width": "Width",
"height": "Height", "height": "Height",
"sampler": "Sampler", "sampler": "Sampler",
"imageToImage": "Image To Image",
"seed": "Seed", "seed": "Seed",
"randomizeSeed": "Randomize Seed", "randomizeSeed": "Randomize Seed",
"shuffle": "Shuffle", "shuffle": "Shuffle",

View File

@ -5,6 +5,7 @@
"confirmOnDelete": "Confirm On Delete", "confirmOnDelete": "Confirm On Delete",
"displayHelpIcons": "Display Help Icons", "displayHelpIcons": "Display Help Icons",
"useCanvasBeta": "Use Canvas Beta Layout", "useCanvasBeta": "Use Canvas Beta Layout",
"useSlidersForAll": "Use Sliders For All Options",
"enableImageDebugging": "Enable Image Debugging", "enableImageDebugging": "Enable Image Debugging",
"resetWebUI": "Reset Web UI", "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.", "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.",

View File

@ -6,7 +6,6 @@
min-width: max-content; min-width: max-content;
margin: 0; margin: 0;
font-weight: bold; font-weight: bold;
font-size: 0.9rem;
color: var(--text-color-secondary); color: var(--text-color-secondary);
} }

View File

@ -78,7 +78,7 @@ export default function IAISlider(props: IAIFullSliderProps) {
tooltipSuffix = '', tooltipSuffix = '',
withSliderMarks = false, withSliderMarks = false,
sliderMarkLeftOffset = 0, sliderMarkLeftOffset = 0,
sliderMarkRightOffset = -7, sliderMarkRightOffset = -1,
withInput = false, withInput = false,
isInteger = false, isInteger = false,
inputWidth = '5.5rem', inputWidth = '5.5rem',
@ -164,6 +164,7 @@ export default function IAISlider(props: IAIFullSliderProps) {
> >
<FormLabel <FormLabel
className="invokeai__slider-component-label" className="invokeai__slider-component-label"
fontSize="sm"
{...sliderFormLabelProps} {...sliderFormLabelProps}
> >
{label} {label}

View File

@ -0,0 +1,55 @@
import { Box } from '@chakra-ui/react';
interface SubItemHookProps {
active?: boolean;
width?: string | number;
height?: string | number;
side?: 'left' | 'right';
}
export default function SubItemHook(props: SubItemHookProps) {
const {
active = true,
width = '1rem',
height = '1.3rem',
side = 'right',
} = props;
return (
<>
{side === 'right' && (
<Box
width={width}
height={height}
margin="-0.5rem 0.5rem 0 0.5rem"
borderLeft={
active
? '3px solid var(--subhook-color)'
: '3px solid var(--tab-hover-color)'
}
borderBottom={
active
? '3px solid var(--subhook-color)'
: '3px solid var(--tab-hover-color)'
}
/>
)}
{side === 'left' && (
<Box
width={width}
height={height}
margin="-0.5rem 0.5rem 0 0.5rem"
borderRight={
active
? '3px solid var(--subhook-color)'
: '3px solid var(--tab-hover-color)'
}
borderBottom={
active
? '3px solid var(--subhook-color)'
: '3px solid var(--tab-hover-color)'
}
/>
)}
</>
);
}

View File

@ -170,6 +170,9 @@ export const frontendToBackendParameters = (
let esrganParameters: false | BackendEsrGanParameters = false; let esrganParameters: false | BackendEsrGanParameters = false;
let facetoolParameters: false | BackendFacetoolParameters = false; let facetoolParameters: false | BackendFacetoolParameters = false;
// Multiplying it by 10000 so the Slider can have values between 0 and 1 which makes more sense
generationParameters.threshold = threshold * 1000;
if (negativePrompt !== '') { if (negativePrompt !== '') {
generationParameters.prompt = `${prompt} [${negativePrompt}]`; generationParameters.prompt = `${prompt} [${negativePrompt}]`;
} }

View File

@ -68,7 +68,7 @@ const BoundingBoxSettings = () => {
}; };
return ( return (
<Flex direction="column" gap="1rem"> <Flex direction="column" gap={2}>
<IAISlider <IAISlider
label={t('parameters:width')} label={t('parameters:width')}
min={64} min={64}
@ -82,6 +82,7 @@ const BoundingBoxSettings = () => {
inputReadOnly inputReadOnly
withReset withReset
handleReset={handleResetWidth} handleReset={handleResetWidth}
sliderMarkRightOffset={-7}
/> />
<IAISlider <IAISlider
label={t('parameters:height')} label={t('parameters:height')}
@ -96,6 +97,7 @@ const BoundingBoxSettings = () => {
inputReadOnly inputReadOnly
withReset withReset
handleReset={handleResetHeight} handleReset={handleResetHeight}
sliderMarkRightOffset={-7}
/> />
</Flex> </Flex>
); );

View File

@ -107,7 +107,7 @@ const InfillAndScalingSettings = () => {
}; };
return ( return (
<Flex direction="column" gap="1rem"> <Flex direction="column" gap={4}>
<IAISelect <IAISelect
label={t('parameters:scaleBeforeProcessing')} label={t('parameters:scaleBeforeProcessing')}
validValues={BOUNDING_BOX_SCALES_DICT} validValues={BOUNDING_BOX_SCALES_DICT}
@ -130,6 +130,7 @@ const InfillAndScalingSettings = () => {
inputReadOnly inputReadOnly
withReset withReset
handleReset={handleResetScaledWidth} handleReset={handleResetScaledWidth}
sliderMarkRightOffset={-7}
/> />
<IAISlider <IAISlider
isInputDisabled={!isManual} isInputDisabled={!isManual}
@ -147,6 +148,7 @@ const InfillAndScalingSettings = () => {
inputReadOnly inputReadOnly
withReset withReset
handleReset={handleResetScaledHeight} handleReset={handleResetScaledHeight}
sliderMarkRightOffset={-7}
/> />
<IAISelect <IAISelect
label={t('parameters:infillMethod')} label={t('parameters:infillMethod')}

View File

@ -6,7 +6,7 @@ import SeamStrength from './SeamStrength';
const SeamCorrectionSettings = () => { const SeamCorrectionSettings = () => {
return ( return (
<Flex direction="column" gap="1rem"> <Flex direction="column" gap={2}>
<SeamSize /> <SeamSize />
<SeamBlur /> <SeamBlur />
<SeamStrength /> <SeamStrength />

View File

@ -0,0 +1,36 @@
import type { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISlider from 'common/components/IAISlider';
import { setCodeformerFidelity } from 'features/parameters/store/postprocessingSlice';
import { useTranslation } from 'react-i18next';
export default function CodeformerFidelity() {
const isGFPGANAvailable = useAppSelector(
(state: RootState) => state.system.isGFPGANAvailable
);
const codeformerFidelity = useAppSelector(
(state: RootState) => state.postprocessing.codeformerFidelity
);
const { t } = useTranslation();
const dispatch = useAppDispatch();
return (
<IAISlider
isSliderDisabled={!isGFPGANAvailable}
isInputDisabled={!isGFPGANAvailable}
isResetDisabled={!isGFPGANAvailable}
label={t('parameters:codeformerFidelity')}
step={0.05}
min={0}
max={1}
onChange={(v) => dispatch(setCodeformerFidelity(v))}
handleReset={() => dispatch(setCodeformerFidelity(1))}
value={codeformerFidelity}
withReset
withSliderMarks
withInput
/>
);
}

View File

@ -1,99 +1,23 @@
import { Flex } from '@chakra-ui/react'; import { Flex } from '@chakra-ui/react';
import { useAppSelector } from 'app/storeHooks';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import type { RootState } from 'app/store';
import FaceRestoreType from './FaceRestoreType';
import { FacetoolType } from 'features/parameters/store/postprocessingSlice'; import FaceRestoreStrength from './FaceRestoreStrength';
import CodeformerFidelity from './CodeformerFidelity';
import {
setCodeformerFidelity,
setFacetoolStrength,
setFacetoolType,
} from 'features/parameters/store/postprocessingSlice';
import { createSelector } from '@reduxjs/toolkit';
import { FACETOOL_TYPES } from 'app/constants';
import IAINumberInput from 'common/components/IAINumberInput';
import IAISelect from 'common/components/IAISelect';
import { postprocessingSelector } from 'features/parameters/store/postprocessingSelectors';
import { systemSelector } from 'features/system/store/systemSelectors';
import { isEqual } from 'lodash';
import { ChangeEvent } from 'react';
import { useTranslation } from 'react-i18next';
const optionsSelector = createSelector(
[postprocessingSelector, systemSelector],
(
{ facetoolStrength, facetoolType, codeformerFidelity },
{ isGFPGANAvailable }
) => {
return {
facetoolStrength,
facetoolType,
codeformerFidelity,
isGFPGANAvailable,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);
/** /**
* Displays face-fixing/GFPGAN options (strength). * Displays face-fixing/GFPGAN options (strength).
*/ */
const FaceRestoreSettings = () => { const FaceRestoreSettings = () => {
const dispatch = useAppDispatch(); const facetoolType = useAppSelector(
const { (state: RootState) => state.postprocessing.facetoolType
facetoolStrength, );
facetoolType,
codeformerFidelity,
isGFPGANAvailable,
} = useAppSelector(optionsSelector);
const handleChangeStrength = (v: number) => dispatch(setFacetoolStrength(v));
const handleChangeCodeformerFidelity = (v: number) =>
dispatch(setCodeformerFidelity(v));
const handleChangeFacetoolType = (e: ChangeEvent<HTMLSelectElement>) =>
dispatch(setFacetoolType(e.target.value as FacetoolType));
const { t } = useTranslation();
return ( return (
<Flex direction="column" gap={2}> <Flex direction="column" gap={2} minWidth="20rem">
<IAISelect <FaceRestoreType />
label={t('parameters:type')} <FaceRestoreStrength />
validValues={FACETOOL_TYPES.concat()} {facetoolType === 'codeformer' && <CodeformerFidelity />}
value={facetoolType}
onChange={handleChangeFacetoolType}
/>
<IAINumberInput
isDisabled={!isGFPGANAvailable}
label={t('parameters:strength')}
step={0.05}
min={0}
max={1}
onChange={handleChangeStrength}
value={facetoolStrength}
width="90px"
isInteger={false}
/>
{facetoolType === 'codeformer' && (
<IAINumberInput
isDisabled={!isGFPGANAvailable}
label={t('parameters:codeformerFidelity')}
step={0.05}
min={0}
max={1}
onChange={handleChangeCodeformerFidelity}
value={codeformerFidelity}
width="90px"
isInteger={false}
/>
)}
</Flex> </Flex>
); );
}; };

View File

@ -0,0 +1,36 @@
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISlider from 'common/components/IAISlider';
import { setFacetoolStrength } from 'features/parameters/store/postprocessingSlice';
import { useTranslation } from 'react-i18next';
export default function FaceRestoreStrength() {
const isGFPGANAvailable = useAppSelector(
(state: RootState) => state.system.isGFPGANAvailable
);
const facetoolStrength = useAppSelector(
(state: RootState) => state.postprocessing.facetoolStrength
);
const { t } = useTranslation();
const dispatch = useAppDispatch();
return (
<IAISlider
isSliderDisabled={!isGFPGANAvailable}
isInputDisabled={!isGFPGANAvailable}
isResetDisabled={!isGFPGANAvailable}
label={t('parameters:strength')}
step={0.05}
min={0}
max={1}
onChange={(v) => dispatch(setFacetoolStrength(v))}
handleReset={() => dispatch(setFacetoolStrength(0.75))}
value={facetoolStrength}
withReset
withSliderMarks
withInput
/>
);
}

View File

@ -0,0 +1,31 @@
import { FACETOOL_TYPES } from 'app/constants';
import { type RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISelect from 'common/components/IAISelect';
import {
type FacetoolType,
setFacetoolType,
} from 'features/parameters/store/postprocessingSlice';
import { type ChangeEvent } from 'react';
import { useTranslation } from 'react-i18next';
export default function FaceRestoreType() {
const facetoolType = useAppSelector(
(state: RootState) => state.postprocessing.facetoolType
);
const dispatch = useAppDispatch();
const { t } = useTranslation();
const handleChangeFacetoolType = (e: ChangeEvent<HTMLSelectElement>) =>
dispatch(setFacetoolType(e.target.value as FacetoolType));
return (
<IAISelect
label={t('parameters:type')}
validValues={FACETOOL_TYPES.concat()}
value={facetoolType}
onChange={handleChangeFacetoolType}
/>
);
}

View File

@ -4,6 +4,7 @@ import type { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISlider from 'common/components/IAISlider'; import IAISlider from 'common/components/IAISlider';
import IAISwitch from 'common/components/IAISwitch'; import IAISwitch from 'common/components/IAISwitch';
import SubItemHook from 'common/components/SubItemHook';
import { postprocessingSelector } from 'features/parameters/store/postprocessingSelectors'; import { postprocessingSelector } from 'features/parameters/store/postprocessingSelectors';
import { import {
setHiresFix, setHiresFix,
@ -39,23 +40,27 @@ const HiresStrength = () => {
}; };
return ( return (
<IAISlider <Flex>
label={t('parameters:hiresStrength')} <SubItemHook active={hiresFix} />
step={0.01} <IAISlider
min={0.01} label={t('parameters:hiresStrength')}
max={0.99} step={0.01}
onChange={handleHiresStrength} min={0.01}
value={hiresStrength} max={0.99}
isInteger={false} onChange={handleHiresStrength}
withInput value={hiresStrength}
withSliderMarks isInteger={false}
inputWidth="5.5rem" withInput
withReset withSliderMarks
handleReset={handleHiResStrengthReset} inputWidth={'5.5rem'}
isSliderDisabled={!hiresFix} withReset
isInputDisabled={!hiresFix} handleReset={handleHiResStrengthReset}
isResetDisabled={!hiresFix} isSliderDisabled={!hiresFix}
/> isInputDisabled={!hiresFix}
isResetDisabled={!hiresFix}
sliderMarkRightOffset={-7}
/>
</Flex>
); );
}; };
@ -75,7 +80,7 @@ const HiresSettings = () => {
dispatch(setHiresFix(e.target.checked)); dispatch(setHiresFix(e.target.checked));
return ( return (
<Flex gap={2} direction="column"> <Flex rowGap="0.8rem" direction={'column'}>
<IAISwitch <IAISwitch
label={t('parameters:hiresOptim')} label={t('parameters:hiresOptim')}
fontSize="md" fontSize="md"

View File

@ -1,6 +1,6 @@
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAINumberInput from 'common/components/IAINumberInput'; import IAISlider from 'common/components/IAISlider';
import { setPerlin } from 'features/parameters/store/generationSlice'; import { setPerlin } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -9,17 +9,18 @@ export default function Perlin() {
const perlin = useAppSelector((state: RootState) => state.generation.perlin); const perlin = useAppSelector((state: RootState) => state.generation.perlin);
const { t } = useTranslation(); const { t } = useTranslation();
const handleChangePerlin = (v: number) => dispatch(setPerlin(v));
return ( return (
<IAINumberInput <IAISlider
label={t('parameters:perlinNoise')} label={t('parameters:perlinNoise')}
min={0} min={0}
max={1} max={1}
step={0.05} step={0.05}
onChange={handleChangePerlin} onChange={(v) => dispatch(setPerlin(v))}
handleReset={() => dispatch(setPerlin(0))}
value={perlin} value={perlin}
isInteger={false} withInput
withReset
withSliderMarks
/> />
); );
} }

View File

@ -1,6 +1,6 @@
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAINumberInput from 'common/components/IAINumberInput'; import IAISlider from 'common/components/IAISlider';
import { setThreshold } from 'features/parameters/store/generationSlice'; import { setThreshold } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -11,17 +11,19 @@ export default function Threshold() {
); );
const { t } = useTranslation(); const { t } = useTranslation();
const handleChangeThreshold = (v: number) => dispatch(setThreshold(v));
return ( return (
<IAINumberInput <IAISlider
label={t('parameters:noiseThreshold')} label={t('parameters:noiseThreshold')}
min={0} min={0}
max={1000} max={1}
step={0.1} step={0.005}
onChange={handleChangeThreshold} onChange={(v) => dispatch(setThreshold(v))}
handleReset={() => dispatch(setThreshold(0))}
value={threshold} value={threshold}
isInteger={false} withInput
withReset
withSliderMarks
inputWidth="6rem"
/> />
); );
} }

View File

@ -0,0 +1,38 @@
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISlider from 'common/components/IAISlider';
import { setUpscalingDenoising } from 'features/parameters/store/postprocessingSlice';
import { useTranslation } from 'react-i18next';
export default function UpscaleDenoisingStrength() {
const isESRGANAvailable = useAppSelector(
(state: RootState) => state.system.isESRGANAvailable
);
const upscalingDenoising = useAppSelector(
(state: RootState) => state.postprocessing.upscalingDenoising
);
const { t } = useTranslation();
const dispatch = useAppDispatch();
return (
<IAISlider
label={t('parameters:denoisingStrength')}
value={upscalingDenoising}
min={0}
max={1}
step={0.01}
onChange={(v) => {
dispatch(setUpscalingDenoising(v));
}}
handleReset={() => dispatch(setUpscalingDenoising(0.75))}
withSliderMarks
withInput
withReset
isSliderDisabled={!isESRGANAvailable}
isInputDisabled={!isESRGANAvailable}
isResetDisabled={!isESRGANAvailable}
/>
);
}

View File

@ -0,0 +1,36 @@
import { UPSCALING_LEVELS } from 'app/constants';
import type { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISelect from 'common/components/IAISelect';
import {
setUpscalingLevel,
type UpscalingLevel,
} from 'features/parameters/store/postprocessingSlice';
import type { ChangeEvent } from 'react';
import { useTranslation } from 'react-i18next';
export default function UpscaleScale() {
const isESRGANAvailable = useAppSelector(
(state: RootState) => state.system.isESRGANAvailable
);
const upscalingLevel = useAppSelector(
(state: RootState) => state.postprocessing.upscalingLevel
);
const { t } = useTranslation();
const dispatch = useAppDispatch();
const handleChangeLevel = (e: ChangeEvent<HTMLSelectElement>) =>
dispatch(setUpscalingLevel(Number(e.target.value) as UpscalingLevel));
return (
<IAISelect
isDisabled={!isESRGANAvailable}
label={t('parameters:scale')}
value={upscalingLevel}
onChange={handleChangeLevel}
validValues={UPSCALING_LEVELS}
/>
);
}

View File

@ -1,104 +1,17 @@
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import {
setUpscalingDenoising,
setUpscalingLevel,
setUpscalingStrength,
UpscalingLevel,
} from 'features/parameters/store/postprocessingSlice';
import { createSelector } from '@reduxjs/toolkit';
import { UPSCALING_LEVELS } from 'app/constants';
import IAISelect from 'common/components/IAISelect';
import { postprocessingSelector } from 'features/parameters/store/postprocessingSelectors';
import { systemSelector } from 'features/system/store/systemSelectors';
import { isEqual } from 'lodash';
import { ChangeEvent } from 'react';
import { useTranslation } from 'react-i18next';
import IAISlider from 'common/components/IAISlider';
import { Flex } from '@chakra-ui/react'; import { Flex } from '@chakra-ui/react';
import UpscaleDenoisingStrength from './UpscaleDenoisingStrength';
const parametersSelector = createSelector( import UpscaleStrength from './UpscaleStrength';
[postprocessingSelector, systemSelector], import UpscaleScale from './UpscaleScale';
(
{ upscalingLevel, upscalingStrength, upscalingDenoising },
{ isESRGANAvailable }
) => {
return {
upscalingLevel,
upscalingDenoising,
upscalingStrength,
isESRGANAvailable,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);
/** /**
* Displays upscaling/ESRGAN options (level and strength). * Displays upscaling/ESRGAN options (level and strength).
*/ */
const UpscaleSettings = () => { const UpscaleSettings = () => {
const dispatch = useAppDispatch();
const {
upscalingLevel,
upscalingStrength,
upscalingDenoising,
isESRGANAvailable,
} = useAppSelector(parametersSelector);
const { t } = useTranslation();
const handleChangeLevel = (e: ChangeEvent<HTMLSelectElement>) =>
dispatch(setUpscalingLevel(Number(e.target.value) as UpscalingLevel));
const handleChangeStrength = (v: number) => dispatch(setUpscalingStrength(v));
return ( return (
<Flex flexDir="column" rowGap="1rem" minWidth="20rem"> <Flex flexDir="column" rowGap={2} minWidth="20rem">
<IAISelect <UpscaleScale />
isDisabled={!isESRGANAvailable} <UpscaleDenoisingStrength />
label={t('parameters:scale')} <UpscaleStrength />
value={upscalingLevel}
onChange={handleChangeLevel}
validValues={UPSCALING_LEVELS}
/>
<IAISlider
label={t('parameters:denoisingStrength')}
value={upscalingDenoising}
min={0}
max={1}
step={0.01}
onChange={(v) => {
dispatch(setUpscalingDenoising(v));
}}
handleReset={() => dispatch(setUpscalingDenoising(0.75))}
withSliderMarks
withInput
withReset
isSliderDisabled={!isESRGANAvailable}
isInputDisabled={!isESRGANAvailable}
isResetDisabled={!isESRGANAvailable}
/>
<IAISlider
label={`${t('parameters:upscale')} ${t('parameters:strength')}`}
value={upscalingStrength}
min={0}
max={1}
step={0.05}
onChange={handleChangeStrength}
handleReset={() => dispatch(setUpscalingStrength(0.75))}
withSliderMarks
withInput
withReset
isSliderDisabled={!isESRGANAvailable}
isInputDisabled={!isESRGANAvailable}
isResetDisabled={!isESRGANAvailable}
/>
</Flex> </Flex>
); );
}; };

View File

@ -0,0 +1,35 @@
import type { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISlider from 'common/components/IAISlider';
import { setUpscalingStrength } from 'features/parameters/store/postprocessingSlice';
import { useTranslation } from 'react-i18next';
export default function UpscaleStrength() {
const isESRGANAvailable = useAppSelector(
(state: RootState) => state.system.isESRGANAvailable
);
const upscalingStrength = useAppSelector(
(state: RootState) => state.postprocessing.upscalingStrength
);
const { t } = useTranslation();
const dispatch = useAppDispatch();
return (
<IAISlider
label={`${t('parameters:upscale')} ${t('parameters:strength')}`}
value={upscalingStrength}
min={0}
max={1}
step={0.05}
onChange={(v) => dispatch(setUpscalingStrength(v))}
handleReset={() => dispatch(setUpscalingStrength(0.75))}
withSliderMarks
withInput
withReset
isSliderDisabled={!isESRGANAvailable}
isInputDisabled={!isESRGANAvailable}
isResetDisabled={!isESRGANAvailable}
/>
);
}

View File

@ -1,6 +1,6 @@
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAINumberInput from 'common/components/IAINumberInput'; import IAISlider from 'common/components/IAISlider';
import { setVariationAmount } from 'features/parameters/store/generationSlice'; import { setVariationAmount } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -16,19 +16,22 @@ export default function VariationAmount() {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const handleChangevariationAmount = (v: number) =>
dispatch(setVariationAmount(v));
return ( return (
<IAINumberInput <IAISlider
label={t('parameters:variationAmount')} label={t('parameters:variationAmount')}
value={variationAmount} value={variationAmount}
step={0.01} step={0.01}
min={0} min={0}
max={1} max={1}
isDisabled={!shouldGenerateVariations} isSliderDisabled={!shouldGenerateVariations}
onChange={handleChangevariationAmount} isInputDisabled={!shouldGenerateVariations}
isInteger={false} isResetDisabled={!shouldGenerateVariations}
onChange={(v) => dispatch(setVariationAmount(v))}
handleReset={() => dispatch(setVariationAmount(0.1))}
withInput
withReset
withSliderMarks
/> />
); );
} }

View File

@ -1,6 +1,7 @@
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAINumberInput from 'common/components/IAINumberInput'; import IAINumberInput from 'common/components/IAINumberInput';
import IAISlider from 'common/components/IAISlider';
import { setCfgScale } from 'features/parameters/store/generationSlice'; import { setCfgScale } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -9,11 +10,29 @@ export default function MainCFGScale() {
const cfgScale = useAppSelector( const cfgScale = useAppSelector(
(state: RootState) => state.generation.cfgScale (state: RootState) => state.generation.cfgScale
); );
const shouldUseSliders = useAppSelector(
(state: RootState) => state.ui.shouldUseSliders
);
const { t } = useTranslation(); const { t } = useTranslation();
const handleChangeCfgScale = (v: number) => dispatch(setCfgScale(v)); const handleChangeCfgScale = (v: number) => dispatch(setCfgScale(v));
return ( return shouldUseSliders ? (
<IAISlider
label={t('parameters:cfgScale')}
step={0.5}
min={1.01}
max={30}
onChange={handleChangeCfgScale}
handleReset={() => dispatch(setCfgScale(7.5))}
value={cfgScale}
sliderMarkRightOffset={-5}
sliderNumberInputProps={{ max: 200 }}
withInput
withReset
withSliderMarks
/>
) : (
<IAINumberInput <IAINumberInput
label={t('parameters:cfgScale')} label={t('parameters:cfgScale')}
step={0.5} step={0.5}

View File

@ -2,29 +2,50 @@ import { HEIGHTS } from 'app/constants';
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISelect from 'common/components/IAISelect'; import IAISelect from 'common/components/IAISelect';
import IAISlider from 'common/components/IAISlider';
import { setHeight } from 'features/parameters/store/generationSlice'; import { setHeight } from 'features/parameters/store/generationSlice';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { ChangeEvent } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
export default function MainHeight() { export default function MainHeight() {
const height = useAppSelector((state: RootState) => state.generation.height); const height = useAppSelector((state: RootState) => state.generation.height);
const shouldUseSliders = useAppSelector(
(state: RootState) => state.ui.shouldUseSliders
);
const activeTabName = useAppSelector(activeTabNameSelector); const activeTabName = useAppSelector(activeTabNameSelector);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
const handleChangeHeight = (e: ChangeEvent<HTMLSelectElement>) => return shouldUseSliders ? (
dispatch(setHeight(Number(e.target.value))); <IAISlider
isSliderDisabled={activeTabName === 'unifiedCanvas'}
return ( isInputDisabled={activeTabName === 'unifiedCanvas'}
isResetDisabled={activeTabName === 'unifiedCanvas'}
label={t('parameters:height')}
value={height}
min={64}
step={64}
max={2048}
onChange={(v) => dispatch(setHeight(v))}
handleReset={() => dispatch(setHeight(512))}
withInput
withReset
withSliderMarks
sliderMarkRightOffset={-8}
inputWidth="6.2rem"
sliderNumberInputProps={{ max: 15360 }}
/>
) : (
<IAISelect <IAISelect
isDisabled={activeTabName === 'unifiedCanvas'} isDisabled={activeTabName === 'unifiedCanvas'}
label={t('parameters:height')} label={t('parameters:height')}
value={height} value={height}
flexGrow={1} flexGrow={1}
onChange={handleChangeHeight} onChange={(e) => dispatch(setHeight(Number(e.target.value)))}
validValues={HEIGHTS} validValues={HEIGHTS}
styleClass="main-settings-block" styleClass="main-settings-block"
width="5.5rem"
/> />
); );
} }

View File

@ -1,39 +1,41 @@
import { createSelector } from '@reduxjs/toolkit';
import type { RootState } from 'app/store'; import type { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAINumberInput from 'common/components/IAINumberInput'; import IAINumberInput from 'common/components/IAINumberInput';
import { import IAISlider from 'common/components/IAISlider';
GenerationState, import { setIterations } from 'features/parameters/store/generationSlice';
setIterations,
} from 'features/parameters/store/generationSlice';
import { isEqual } from 'lodash';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const mainIterationsSelector = createSelector(
[(state: RootState) => state.generation],
(parameters: GenerationState) => {
const { iterations } = parameters;
return {
iterations,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);
export default function MainIterations() { export default function MainIterations() {
const iterations = useAppSelector(
(state: RootState) => state.generation.iterations
);
const shouldUseSliders = useAppSelector(
(state: RootState) => state.ui.shouldUseSliders
);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { iterations } = useAppSelector(mainIterationsSelector);
const { t } = useTranslation(); const { t } = useTranslation();
const handleChangeIterations = (v: number) => dispatch(setIterations(v)); const handleChangeIterations = (v: number) => dispatch(setIterations(v));
return ( return shouldUseSliders ? (
<IAISlider
label={t('parameters:images')}
step={1}
min={1}
max={16}
onChange={handleChangeIterations}
handleReset={() => dispatch(setIterations(1))}
value={iterations}
withInput
withReset
withSliderMarks
sliderMarkRightOffset={-5}
sliderNumberInputProps={{ max: 9999 }}
/>
) : (
<IAINumberInput <IAINumberInput
label={t('parameters:images')} label={t('parameters:images')}
step={1} step={1}

View File

@ -1,3 +1,8 @@
import { Flex } from '@chakra-ui/react';
import { type RootState } from 'app/store';
import { useAppSelector } from 'app/storeHooks';
import { useTranslation } from 'react-i18next';
import ParametersAccordion from '../ParametersAccordion';
import MainCFGScale from './MainCFGScale'; import MainCFGScale from './MainCFGScale';
import MainHeight from './MainHeight'; import MainHeight from './MainHeight';
import MainIterations from './MainIterations'; import MainIterations from './MainIterations';
@ -8,20 +13,40 @@ import MainWidth from './MainWidth';
export const inputWidth = 'auto'; export const inputWidth = 'auto';
export default function MainSettings() { export default function MainSettings() {
return ( const { t } = useTranslation();
<div className="main-settings">
<div className="main-settings-list"> const shouldUseSliders = useAppSelector(
<div className="main-settings-row"> (state: RootState) => state.ui.shouldUseSliders
);
const accordionItems = {
main: {
header: `${t('parameters:general')}`,
feature: undefined,
content: shouldUseSliders ? (
<Flex flexDir="column" rowGap={2}>
<MainIterations /> <MainIterations />
<MainSteps /> <MainSteps />
<MainCFGScale /> <MainCFGScale />
</div>
<div className="main-settings-row">
<MainWidth /> <MainWidth />
<MainHeight /> <MainHeight />
<MainSampler /> <MainSampler />
</div> </Flex>
</div> ) : (
</div> <Flex flexDirection="column" rowGap={2}>
); <Flex gap={2}>
<MainIterations />
<MainSteps />
<MainCFGScale />
</Flex>
<Flex>
<MainWidth />
<MainHeight />
<MainSampler />
</Flex>
</Flex>
),
},
};
return <ParametersAccordion accordionInfo={accordionItems} />;
} }

View File

@ -27,6 +27,7 @@ export default function MainSampler() {
activeModel.format === 'diffusers' ? DIFFUSERS_SAMPLERS : SAMPLERS activeModel.format === 'diffusers' ? DIFFUSERS_SAMPLERS : SAMPLERS
} }
styleClass="main-settings-block" styleClass="main-settings-block"
minWidth="9rem"
/> />
); );
} }

View File

@ -1,17 +1,36 @@
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAINumberInput from 'common/components/IAINumberInput'; import IAINumberInput from 'common/components/IAINumberInput';
import IAISlider from 'common/components/IAISlider';
import { setSteps } from 'features/parameters/store/generationSlice'; import { setSteps } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
export default function MainSteps() { export default function MainSteps() {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const steps = useAppSelector((state: RootState) => state.generation.steps); const steps = useAppSelector((state: RootState) => state.generation.steps);
const shouldUseSliders = useAppSelector(
(state: RootState) => state.ui.shouldUseSliders
);
const { t } = useTranslation(); const { t } = useTranslation();
const handleChangeSteps = (v: number) => dispatch(setSteps(v)); const handleChangeSteps = (v: number) => dispatch(setSteps(v));
return ( return shouldUseSliders ? (
<IAISlider
label={t('parameters:steps')}
min={1}
step={1}
onChange={handleChangeSteps}
handleReset={() => dispatch(setSteps(20))}
value={steps}
withInput
withReset
withSliderMarks
sliderMarkRightOffset={-6}
sliderNumberInputProps={{ max: 9999 }}
/>
) : (
<IAINumberInput <IAINumberInput
label={t('parameters:steps')} label={t('parameters:steps')}
min={1} min={1}

View File

@ -2,30 +2,51 @@ import { WIDTHS } from 'app/constants';
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISelect from 'common/components/IAISelect'; import IAISelect from 'common/components/IAISelect';
import IAISlider from 'common/components/IAISlider';
import { setWidth } from 'features/parameters/store/generationSlice'; import { setWidth } from 'features/parameters/store/generationSlice';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { ChangeEvent } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
export default function MainWidth() { export default function MainWidth() {
const width = useAppSelector((state: RootState) => state.generation.width); const width = useAppSelector((state: RootState) => state.generation.width);
const shouldUseSliders = useAppSelector(
(state: RootState) => state.ui.shouldUseSliders
);
const activeTabName = useAppSelector(activeTabNameSelector); const activeTabName = useAppSelector(activeTabNameSelector);
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const handleChangeWidth = (e: ChangeEvent<HTMLSelectElement>) => return shouldUseSliders ? (
dispatch(setWidth(Number(e.target.value))); <IAISlider
isSliderDisabled={activeTabName === 'unifiedCanvas'}
return ( isInputDisabled={activeTabName === 'unifiedCanvas'}
isResetDisabled={activeTabName === 'unifiedCanvas'}
label={t('parameters:width')}
value={width}
min={64}
step={64}
max={2048}
onChange={(v) => dispatch(setWidth(v))}
handleReset={() => dispatch(setWidth(512))}
withInput
withReset
withSliderMarks
sliderMarkRightOffset={-8}
inputWidth="6.2rem"
inputReadOnly
sliderNumberInputProps={{ max: 15360 }}
/>
) : (
<IAISelect <IAISelect
isDisabled={activeTabName === 'unifiedCanvas'} isDisabled={activeTabName === 'unifiedCanvas'}
label={t('parameters:width')} label={t('parameters:width')}
value={width} value={width}
flexGrow={1} flexGrow={1}
onChange={handleChangeWidth} onChange={(e) => dispatch(setWidth(Number(e.target.value)))}
validValues={WIDTHS} validValues={WIDTHS}
styleClass="main-settings-block" styleClass="main-settings-block"
width="5.5rem"
/> />
); );
} }

View File

@ -22,7 +22,7 @@ export interface PostprocessingState {
const initialPostprocessingState: PostprocessingState = { const initialPostprocessingState: PostprocessingState = {
codeformerFidelity: 0.75, codeformerFidelity: 0.75,
facetoolStrength: 0.8, facetoolStrength: 0.75,
facetoolType: 'gfpgan', facetoolType: 'gfpgan',
hiresFix: false, hiresFix: false,
hiresStrength: 0.75, hiresStrength: 0.75,

View File

@ -14,7 +14,7 @@ import {
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { IN_PROGRESS_IMAGE_TYPES } from 'app/constants'; import { IN_PROGRESS_IMAGE_TYPES } from 'app/constants';
import { RootState } from 'app/store'; import { type RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAINumberInput from 'common/components/IAINumberInput'; import IAINumberInput from 'common/components/IAINumberInput';
import IAISelect from 'common/components/IAISelect'; import IAISelect from 'common/components/IAISelect';
@ -27,9 +27,14 @@ import {
setShouldConfirmOnDelete, setShouldConfirmOnDelete,
setShouldDisplayGuides, setShouldDisplayGuides,
setShouldDisplayInProgressType, setShouldDisplayInProgressType,
type SystemState,
} from 'features/system/store/systemSlice'; } from 'features/system/store/systemSlice';
import { uiSelector } from 'features/ui/store/uiSelectors'; import { uiSelector } from 'features/ui/store/uiSelectors';
import { setShouldUseCanvasBetaLayout } from 'features/ui/store/uiSlice'; import {
setShouldUseCanvasBetaLayout,
setShouldUseSliders,
} from 'features/ui/store/uiSlice';
import { type UIState } from 'features/ui/store/uiTypes';
import { isEqual, map } from 'lodash'; import { isEqual, map } from 'lodash';
import { persistor } from 'persistor'; import { persistor } from 'persistor';
import { ChangeEvent, cloneElement, ReactElement } from 'react'; import { ChangeEvent, cloneElement, ReactElement } from 'react';
@ -37,7 +42,7 @@ import { useTranslation } from 'react-i18next';
const selector = createSelector( const selector = createSelector(
[systemSelector, uiSelector], [systemSelector, uiSelector],
(system, ui) => { (system: SystemState, ui: UIState) => {
const { const {
shouldDisplayInProgressType, shouldDisplayInProgressType,
shouldConfirmOnDelete, shouldConfirmOnDelete,
@ -47,7 +52,7 @@ const selector = createSelector(
enableImageDebugging, enableImageDebugging,
} = system; } = system;
const { shouldUseCanvasBetaLayout } = ui; const { shouldUseCanvasBetaLayout, shouldUseSliders } = ui;
return { return {
shouldDisplayInProgressType, shouldDisplayInProgressType,
@ -57,6 +62,7 @@ const selector = createSelector(
saveIntermediatesInterval, saveIntermediatesInterval,
enableImageDebugging, enableImageDebugging,
shouldUseCanvasBetaLayout, shouldUseCanvasBetaLayout,
shouldUseSliders,
}; };
}, },
{ {
@ -100,6 +106,7 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
saveIntermediatesInterval, saveIntermediatesInterval,
enableImageDebugging, enableImageDebugging,
shouldUseCanvasBetaLayout, shouldUseCanvasBetaLayout,
shouldUseSliders,
} = useAppSelector(selector); } = useAppSelector(selector);
/** /**
@ -191,6 +198,14 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
dispatch(setShouldUseCanvasBetaLayout(e.target.checked)) dispatch(setShouldUseCanvasBetaLayout(e.target.checked))
} }
/> />
<IAISwitch
styleClass="settings-modal-item"
label={t('settings:useSlidersForAll')}
isChecked={shouldUseSliders}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
dispatch(setShouldUseSliders(e.target.checked))
}
/>
</div> </div>
<div className="settings-modal-items"> <div className="settings-modal-items">

View File

@ -0,0 +1,26 @@
import { Flex } from '@chakra-ui/react';
import ImageFit from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageFit';
import ImageToImageStrength from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageStrength';
import ParametersAccordion from 'features/parameters/components/ParametersAccordion';
import { useTranslation } from 'react-i18next';
export default function ImageToImageOptions() {
const { t } = useTranslation();
const imageToImageAccordionItems = {
imageToImage: {
header: `${t('parameters:imageToImage')}`,
feature: undefined,
content: (
<Flex gap={2} flexDir="column">
<ImageToImageStrength
label={t('parameters:img2imgStrength')}
styleClass="main-settings-block image-to-image-strength-main-option"
/>
<ImageFit />
</Flex>
),
},
};
return <ParametersAccordion accordionInfo={imageToImageAccordionItems} />;
}

View File

@ -2,8 +2,6 @@ import { Flex } from '@chakra-ui/react';
import { Feature } from 'app/features'; import { Feature } from 'app/features';
import FaceRestoreSettings from 'features/parameters/components/AdvancedParameters/FaceRestore/FaceRestoreSettings'; import FaceRestoreSettings from 'features/parameters/components/AdvancedParameters/FaceRestore/FaceRestoreSettings';
import FaceRestoreToggle from 'features/parameters/components/AdvancedParameters/FaceRestore/FaceRestoreToggle'; import FaceRestoreToggle from 'features/parameters/components/AdvancedParameters/FaceRestore/FaceRestoreToggle';
import ImageFit from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageFit';
import ImageToImageStrength from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageStrength';
import ImageToImageOutputSettings from 'features/parameters/components/AdvancedParameters/Output/ImageToImageOutputSettings'; import ImageToImageOutputSettings from 'features/parameters/components/AdvancedParameters/Output/ImageToImageOutputSettings';
import SeedSettings from 'features/parameters/components/AdvancedParameters/Seed/SeedSettings'; import SeedSettings from 'features/parameters/components/AdvancedParameters/Seed/SeedSettings';
import UpscaleSettings from 'features/parameters/components/AdvancedParameters/Upscale/UpscaleSettings'; import UpscaleSettings from 'features/parameters/components/AdvancedParameters/Upscale/UpscaleSettings';
@ -17,6 +15,7 @@ import NegativePromptInput from 'features/parameters/components/PromptInput/Nega
import PromptInput from 'features/parameters/components/PromptInput/PromptInput'; import PromptInput from 'features/parameters/components/PromptInput/PromptInput';
import InvokeOptionsPanel from 'features/ui/components/InvokeParametersPanel'; import InvokeOptionsPanel from 'features/ui/components/InvokeParametersPanel';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import ImageToImageOptions from './ImageToImageOptions';
export default function ImageToImagePanel() { export default function ImageToImagePanel() {
const { t } = useTranslation(); const { t } = useTranslation();
@ -60,11 +59,7 @@ export default function ImageToImagePanel() {
</Flex> </Flex>
<ProcessButtons /> <ProcessButtons />
<MainSettings /> <MainSettings />
<ImageToImageStrength <ImageToImageOptions />
label={t('parameters:img2imgStrength')}
styleClass="main-settings-block image-to-image-strength-main-option"
/>
<ImageFit />
<ParametersAccordion accordionInfo={imageToImageAccordions} /> <ParametersAccordion accordionInfo={imageToImageAccordions} />
</InvokeOptionsPanel> </InvokeOptionsPanel>
); );

View File

@ -32,7 +32,7 @@
.parameters-panel { .parameters-panel {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
row-gap: 1rem; row-gap: 0.5rem;
height: 100%; height: 100%;
@include HideScrollbar; @include HideScrollbar;
background-color: var(--background-color); background-color: var(--background-color);

View File

@ -20,6 +20,11 @@ export default function UnifiedCanvasPanel() {
const { t } = useTranslation(); const { t } = useTranslation();
const unifiedCanvasAccordions = { const unifiedCanvasAccordions = {
seed: {
header: `${t('parameters:seed')}`,
feature: Feature.SEED,
content: <SeedSettings />,
},
boundingBox: { boundingBox: {
header: `${t('parameters:boundingBoxHeader')}`, header: `${t('parameters:boundingBoxHeader')}`,
feature: Feature.BOUNDING_BOX, feature: Feature.BOUNDING_BOX,
@ -35,11 +40,6 @@ export default function UnifiedCanvasPanel() {
feature: Feature.INFILL_AND_SCALING, feature: Feature.INFILL_AND_SCALING,
content: <InfillAndScalingSettings />, content: <InfillAndScalingSettings />,
}, },
seed: {
header: `${t('parameters:seed')}`,
feature: Feature.SEED,
content: <SeedSettings />,
},
variations: { variations: {
header: `${t('parameters:variations')}`, header: `${t('parameters:variations')}`,
feature: Feature.VARIATIONS, feature: Feature.VARIATIONS,
@ -48,6 +48,19 @@ export default function UnifiedCanvasPanel() {
}, },
}; };
const unifiedCanvasImg2ImgAccordion = {
unifiedCanvasImg2Img: {
header: `${t('parameters:imageToImage')}`,
feature: undefined,
content: (
<ImageToImageStrength
label={t('parameters:img2imgStrength')}
styleClass="main-settings-block image-to-image-strength-main-option"
/>
),
},
};
return ( return (
<InvokeOptionsPanel> <InvokeOptionsPanel>
<Flex flexDir="column" rowGap="0.5rem"> <Flex flexDir="column" rowGap="0.5rem">
@ -56,10 +69,7 @@ export default function UnifiedCanvasPanel() {
</Flex> </Flex>
<ProcessButtons /> <ProcessButtons />
<MainSettings /> <MainSettings />
<ImageToImageStrength <ParametersAccordion accordionInfo={unifiedCanvasImg2ImgAccordion} />
label={t('parameters:img2imgStrength')}
styleClass="main-settings-block image-to-image-strength-main-option"
/>
<ParametersAccordion accordionInfo={unifiedCanvasAccordions} /> <ParametersAccordion accordionInfo={unifiedCanvasAccordions} />
</InvokeOptionsPanel> </InvokeOptionsPanel>
); );

View File

@ -14,6 +14,7 @@ const initialtabsState: UIState = {
shouldShowImageDetails: false, shouldShowImageDetails: false,
shouldUseCanvasBetaLayout: false, shouldUseCanvasBetaLayout: false,
shouldShowExistingModelsInSearch: false, shouldShowExistingModelsInSearch: false,
shouldUseSliders: false,
addNewModelUIOption: null, addNewModelUIOption: null,
}; };
@ -66,6 +67,9 @@ export const uiSlice = createSlice({
) => { ) => {
state.shouldShowExistingModelsInSearch = action.payload; state.shouldShowExistingModelsInSearch = action.payload;
}, },
setShouldUseSliders: (state, action: PayloadAction<boolean>) => {
state.shouldUseSliders = action.payload;
},
setAddNewModelUIOption: (state, action: PayloadAction<AddNewModelType>) => { setAddNewModelUIOption: (state, action: PayloadAction<AddNewModelType>) => {
state.addNewModelUIOption = action.payload; state.addNewModelUIOption = action.payload;
}, },
@ -83,6 +87,7 @@ export const {
setShouldShowImageDetails, setShouldShowImageDetails,
setShouldUseCanvasBetaLayout, setShouldUseCanvasBetaLayout,
setShouldShowExistingModelsInSearch, setShouldShowExistingModelsInSearch,
setShouldUseSliders,
setAddNewModelUIOption, setAddNewModelUIOption,
} = uiSlice.actions; } = uiSlice.actions;

View File

@ -11,5 +11,6 @@ export interface UIState {
shouldShowImageDetails: boolean; shouldShowImageDetails: boolean;
shouldUseCanvasBetaLayout: boolean; shouldUseCanvasBetaLayout: boolean;
shouldShowExistingModelsInSearch: boolean; shouldShowExistingModelsInSearch: boolean;
shouldUseSliders: boolean;
addNewModelUIOption: AddNewModelType; addNewModelUIOption: AddNewModelType;
} }

View File

@ -137,4 +137,7 @@
// Scrollbar // Scrollbar
--scrollbar-color: var(--accent-color); --scrollbar-color: var(--accent-color);
--scrollbar-color-hover: var(--accent-color-bright); --scrollbar-color-hover: var(--accent-color-bright);
// SubHook
--subhook-color: var(--accent-color);
} }

View File

@ -135,4 +135,7 @@
// Scrollbar // Scrollbar
--scrollbar-color: var(--accent-color); --scrollbar-color: var(--accent-color);
--scrollbar-color-hover: var(--accent-color-bright); --scrollbar-color-hover: var(--accent-color-bright);
// SubHook
--subhook-color: var(--accent-color);
} }

View File

@ -132,4 +132,7 @@
// Scrollbar // Scrollbar
--scrollbar-color: rgb(180, 180, 184); --scrollbar-color: rgb(180, 180, 184);
--scrollbar-color-hover: rgb(150, 150, 154); --scrollbar-color-hover: rgb(150, 150, 154);
// SubHook
--subhook-color: rgb(0, 0, 0);
} }

File diff suppressed because one or more lines are too long