mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-02-19 03:24:37 -05:00
Reorganises internal state
`options` slice was huge and managed a mix of generation parameters and general app settings. It has been split up: - Generation parameters are now in `generationSlice`. - Postprocessing parameters are now in `postprocessingSlice` - UI related things are now in `uiSlice` There is probably more to be done, like `gallerySlice` perhaps should only manage internal gallery state, and not if the gallery is displayed. Full-slice selectors have been made for each slice. Other organisational tweaks.
This commit is contained in:
committed by
blessedcoolant
parent
ffe0e81ec9
commit
d74c4009cb
@@ -0,0 +1,54 @@
|
||||
@use '../../../../styles/Mixins/' as *;
|
||||
|
||||
.advanced-parameters {
|
||||
padding-top: 0.5rem;
|
||||
display: grid;
|
||||
row-gap: 0.5rem;
|
||||
}
|
||||
|
||||
.advanced-parameters-item {
|
||||
display: grid;
|
||||
max-width: $options-bar-max-width;
|
||||
border: none;
|
||||
border-top: 0px;
|
||||
border-radius: 0.4rem;
|
||||
background-color: var(--tab-panel-bg);
|
||||
|
||||
&[aria-expanded='true'] {
|
||||
background-color: var(--tab-hover-color);
|
||||
border-radius: 0 0 0.4rem 0.4rem;
|
||||
}
|
||||
}
|
||||
|
||||
.advanced-parameters-panel {
|
||||
background-color: var(--tab-panel-bg);
|
||||
border-radius: 0 0 0.4rem 0.4rem;
|
||||
padding: 1rem;
|
||||
|
||||
button {
|
||||
background-color: var(--btn-base-color);
|
||||
&:hover {
|
||||
background-color: var(--btn-base-color-hover);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
&:hover {
|
||||
background-color: var(--btn-base-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.advanced-parameters-header {
|
||||
border-radius: 0.4rem;
|
||||
font-weight: bold;
|
||||
|
||||
&[aria-expanded='true'] {
|
||||
background-color: var(--tab-hover-color);
|
||||
border-radius: 0.4rem 0.4rem 0 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--tab-hover-color);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import {
|
||||
AccordionButton,
|
||||
AccordionIcon,
|
||||
AccordionItem,
|
||||
AccordionPanel,
|
||||
Box,
|
||||
Flex,
|
||||
} from '@chakra-ui/react';
|
||||
import { ReactNode } from 'react';
|
||||
import { Feature } from 'app/features';
|
||||
import GuideIcon from 'common/components/GuideIcon';
|
||||
|
||||
export interface InvokeAccordionItemProps {
|
||||
header: string;
|
||||
content: ReactNode;
|
||||
feature?: Feature;
|
||||
additionalHeaderComponents?: ReactNode;
|
||||
}
|
||||
|
||||
export default function InvokeAccordionItem(props: InvokeAccordionItemProps) {
|
||||
const { header, feature, content, additionalHeaderComponents } = props;
|
||||
|
||||
return (
|
||||
<AccordionItem className="advanced-parameters-item">
|
||||
<AccordionButton className="advanced-parameters-header">
|
||||
<Flex width={'100%'} gap={'0.5rem'} align={'center'}>
|
||||
<Box flexGrow={1} textAlign={'left'}>
|
||||
{header}
|
||||
</Box>
|
||||
{additionalHeaderComponents}
|
||||
{feature && <GuideIcon feature={feature} />}
|
||||
<AccordionIcon />
|
||||
</Flex>
|
||||
</AccordionButton>
|
||||
<AccordionPanel className="advanced-parameters-panel">
|
||||
{content}
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
.inpainting-bounding-box-settings {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 0.4rem;
|
||||
border: 2px solid var(--tab-color);
|
||||
}
|
||||
|
||||
.inpainting-bounding-box-header {
|
||||
background-color: var(--tab-color);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 0.3rem 0.3rem 0 0;
|
||||
align-items: center;
|
||||
|
||||
button {
|
||||
width: 0.5rem;
|
||||
height: 1.2rem;
|
||||
background: none;
|
||||
&:hover {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
// font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.inpainting-bounding-box-settings-items {
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 1rem;
|
||||
|
||||
.inpainting-bounding-box-reset-icon-btn {
|
||||
background-color: var(--btn-base-color);
|
||||
&:hover {
|
||||
background-color: var(--btn-base-color-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.inpainting-bounding-box-dimensions-slider-numberinput {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, auto);
|
||||
column-gap: 1rem;
|
||||
}
|
||||
|
||||
.inpainting-bounding-box-darken {
|
||||
width: max-content;
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAISlider from 'common/components/IAISlider';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { setBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
|
||||
import _ from 'lodash';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const selector = createSelector(
|
||||
canvasSelector,
|
||||
(canvas) => {
|
||||
const { boundingBoxDimensions, boundingBoxScaleMethod: boundingBoxScale } =
|
||||
canvas;
|
||||
return {
|
||||
boundingBoxDimensions,
|
||||
boundingBoxScale,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: _.isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const BoundingBoxSettings = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { boundingBoxDimensions } = useAppSelector(selector);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleChangeWidth = (v: number) => {
|
||||
dispatch(
|
||||
setBoundingBoxDimensions({
|
||||
...boundingBoxDimensions,
|
||||
width: Math.floor(v),
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleChangeHeight = (v: number) => {
|
||||
dispatch(
|
||||
setBoundingBoxDimensions({
|
||||
...boundingBoxDimensions,
|
||||
height: Math.floor(v),
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleResetWidth = () => {
|
||||
dispatch(
|
||||
setBoundingBoxDimensions({
|
||||
...boundingBoxDimensions,
|
||||
width: Math.floor(512),
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleResetHeight = () => {
|
||||
dispatch(
|
||||
setBoundingBoxDimensions({
|
||||
...boundingBoxDimensions,
|
||||
height: Math.floor(512),
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Flex direction="column" gap="1rem">
|
||||
<IAISlider
|
||||
label={t('parameters:width')}
|
||||
min={64}
|
||||
max={1024}
|
||||
step={64}
|
||||
value={boundingBoxDimensions.width}
|
||||
onChange={handleChangeWidth}
|
||||
sliderNumberInputProps={{ max: 4096 }}
|
||||
withSliderMarks
|
||||
withInput
|
||||
inputReadOnly
|
||||
withReset
|
||||
handleReset={handleResetWidth}
|
||||
/>
|
||||
<IAISlider
|
||||
label={t('parameters:height')}
|
||||
min={64}
|
||||
max={1024}
|
||||
step={64}
|
||||
value={boundingBoxDimensions.height}
|
||||
onChange={handleChangeHeight}
|
||||
sliderNumberInputProps={{ max: 4096 }}
|
||||
withSliderMarks
|
||||
withInput
|
||||
inputReadOnly
|
||||
withReset
|
||||
handleReset={handleResetHeight}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default BoundingBoxSettings;
|
||||
|
||||
export const BoundingBoxSettingsHeader = () => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Box flex="1" textAlign="left">
|
||||
{t('parameters:boundingBoxHeader')}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,180 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAISelect from 'common/components/IAISelect';
|
||||
import IAISlider from 'common/components/IAISlider';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import {
|
||||
setBoundingBoxScaleMethod,
|
||||
setScaledBoundingBoxDimensions,
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import {
|
||||
BoundingBoxScale,
|
||||
BOUNDING_BOX_SCALES_DICT,
|
||||
} from 'features/canvas/store/canvasTypes';
|
||||
import { generationSelector } from 'features/parameters/store/generationSelectors';
|
||||
import {
|
||||
setInfillMethod,
|
||||
setTileSize,
|
||||
} from 'features/parameters/store/generationSlice';
|
||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||
import _ from 'lodash';
|
||||
import { ChangeEvent } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const selector = createSelector(
|
||||
[generationSelector, systemSelector, canvasSelector],
|
||||
(parameters, system, canvas) => {
|
||||
const { tileSize, infillMethod } = parameters;
|
||||
|
||||
const { infill_methods: availableInfillMethods } = system;
|
||||
|
||||
const {
|
||||
boundingBoxScaleMethod: boundingBoxScale,
|
||||
scaledBoundingBoxDimensions,
|
||||
} = canvas;
|
||||
|
||||
return {
|
||||
boundingBoxScale,
|
||||
scaledBoundingBoxDimensions,
|
||||
tileSize,
|
||||
infillMethod,
|
||||
availableInfillMethods,
|
||||
isManual: boundingBoxScale === 'manual',
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: _.isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const InfillAndScalingSettings = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const {
|
||||
tileSize,
|
||||
infillMethod,
|
||||
availableInfillMethods,
|
||||
boundingBoxScale,
|
||||
isManual,
|
||||
scaledBoundingBoxDimensions,
|
||||
} = useAppSelector(selector);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleChangeScaledWidth = (v: number) => {
|
||||
dispatch(
|
||||
setScaledBoundingBoxDimensions({
|
||||
...scaledBoundingBoxDimensions,
|
||||
width: Math.floor(v),
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleChangeScaledHeight = (v: number) => {
|
||||
dispatch(
|
||||
setScaledBoundingBoxDimensions({
|
||||
...scaledBoundingBoxDimensions,
|
||||
height: Math.floor(v),
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleResetScaledWidth = () => {
|
||||
dispatch(
|
||||
setScaledBoundingBoxDimensions({
|
||||
...scaledBoundingBoxDimensions,
|
||||
width: Math.floor(512),
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleResetScaledHeight = () => {
|
||||
dispatch(
|
||||
setScaledBoundingBoxDimensions({
|
||||
...scaledBoundingBoxDimensions,
|
||||
height: Math.floor(512),
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleChangeBoundingBoxScaleMethod = (
|
||||
e: ChangeEvent<HTMLSelectElement>
|
||||
) => {
|
||||
dispatch(setBoundingBoxScaleMethod(e.target.value as BoundingBoxScale));
|
||||
};
|
||||
|
||||
return (
|
||||
<Flex direction="column" gap="1rem">
|
||||
<IAISelect
|
||||
label={t('parameters:scaleBeforeProcessing')}
|
||||
validValues={BOUNDING_BOX_SCALES_DICT}
|
||||
value={boundingBoxScale}
|
||||
onChange={handleChangeBoundingBoxScaleMethod}
|
||||
/>
|
||||
<IAISlider
|
||||
isInputDisabled={!isManual}
|
||||
isResetDisabled={!isManual}
|
||||
isSliderDisabled={!isManual}
|
||||
label={t('parameters:scaledWidth')}
|
||||
min={64}
|
||||
max={1024}
|
||||
step={64}
|
||||
value={scaledBoundingBoxDimensions.width}
|
||||
onChange={handleChangeScaledWidth}
|
||||
sliderNumberInputProps={{ max: 4096 }}
|
||||
withSliderMarks
|
||||
withInput
|
||||
inputReadOnly
|
||||
withReset
|
||||
handleReset={handleResetScaledWidth}
|
||||
/>
|
||||
<IAISlider
|
||||
isInputDisabled={!isManual}
|
||||
isResetDisabled={!isManual}
|
||||
isSliderDisabled={!isManual}
|
||||
label={t('parameters:scaledHeight')}
|
||||
min={64}
|
||||
max={1024}
|
||||
step={64}
|
||||
value={scaledBoundingBoxDimensions.height}
|
||||
onChange={handleChangeScaledHeight}
|
||||
sliderNumberInputProps={{ max: 4096 }}
|
||||
withSliderMarks
|
||||
withInput
|
||||
inputReadOnly
|
||||
withReset
|
||||
handleReset={handleResetScaledHeight}
|
||||
/>
|
||||
<IAISelect
|
||||
label={t('parameters:infillMethod')}
|
||||
value={infillMethod}
|
||||
validValues={availableInfillMethods}
|
||||
onChange={(e) => dispatch(setInfillMethod(e.target.value))}
|
||||
/>
|
||||
<IAISlider
|
||||
isInputDisabled={infillMethod !== 'tile'}
|
||||
isResetDisabled={infillMethod !== 'tile'}
|
||||
isSliderDisabled={infillMethod !== 'tile'}
|
||||
sliderMarkRightOffset={-4}
|
||||
label={t('parameters:tileSize')}
|
||||
min={16}
|
||||
max={64}
|
||||
sliderNumberInputProps={{ max: 256 }}
|
||||
value={tileSize}
|
||||
onChange={(v) => {
|
||||
dispatch(setTileSize(v));
|
||||
}}
|
||||
withInput
|
||||
withSliderMarks
|
||||
withReset
|
||||
handleReset={() => {
|
||||
dispatch(setTileSize(32));
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default InfillAndScalingSettings;
|
||||
@@ -0,0 +1,33 @@
|
||||
import type { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAISlider from 'common/components/IAISlider';
|
||||
import { setSeamBlur } from 'features/parameters/store/generationSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function SeamBlur() {
|
||||
const dispatch = useAppDispatch();
|
||||
const seamBlur = useAppSelector(
|
||||
(state: RootState) => state.generation.seamBlur
|
||||
);
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<IAISlider
|
||||
sliderMarkRightOffset={-4}
|
||||
label={t('parameters:seamBlur')}
|
||||
min={0}
|
||||
max={64}
|
||||
sliderNumberInputProps={{ max: 512 }}
|
||||
value={seamBlur}
|
||||
onChange={(v) => {
|
||||
dispatch(setSeamBlur(v));
|
||||
}}
|
||||
withInput
|
||||
withSliderMarks
|
||||
withReset
|
||||
handleReset={() => {
|
||||
dispatch(setSeamBlur(16));
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import SeamBlur from './SeamBlur';
|
||||
import SeamSize from './SeamSize';
|
||||
import SeamSteps from './SeamSteps';
|
||||
import SeamStrength from './SeamStrength';
|
||||
|
||||
const SeamCorrectionSettings = () => {
|
||||
return (
|
||||
<Flex direction="column" gap="1rem">
|
||||
<SeamSize />
|
||||
<SeamBlur />
|
||||
<SeamStrength />
|
||||
<SeamSteps />
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default SeamCorrectionSettings;
|
||||
@@ -0,0 +1,31 @@
|
||||
import type { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAISlider from 'common/components/IAISlider';
|
||||
import { setSeamSize } from 'features/parameters/store/generationSlice';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function SeamSize() {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const seamSize = useAppSelector((state: RootState) => state.generation.seamSize);
|
||||
|
||||
return (
|
||||
<IAISlider
|
||||
sliderMarkRightOffset={-6}
|
||||
label={t('parameters:seamSize')}
|
||||
min={1}
|
||||
max={256}
|
||||
sliderNumberInputProps={{ max: 512 }}
|
||||
value={seamSize}
|
||||
onChange={(v) => {
|
||||
dispatch(setSeamSize(v));
|
||||
}}
|
||||
withInput
|
||||
withSliderMarks
|
||||
withReset
|
||||
handleReset={() => dispatch(setSeamSize(96))}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import type { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAISlider from 'common/components/IAISlider';
|
||||
import { setSeamSteps } from 'features/parameters/store/generationSlice';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function SeamSteps() {
|
||||
const { t } = useTranslation();
|
||||
const seamSteps = useAppSelector(
|
||||
(state: RootState) => state.generation.seamSteps
|
||||
);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
return (
|
||||
<IAISlider
|
||||
sliderMarkRightOffset={-4}
|
||||
label={t('parameters:seamSteps')}
|
||||
min={1}
|
||||
max={100}
|
||||
sliderNumberInputProps={{ max: 999 }}
|
||||
value={seamSteps}
|
||||
onChange={(v) => {
|
||||
dispatch(setSeamSteps(v));
|
||||
}}
|
||||
withInput
|
||||
withSliderMarks
|
||||
withReset
|
||||
handleReset={() => {
|
||||
dispatch(setSeamSteps(30));
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAISlider from 'common/components/IAISlider';
|
||||
import { setSeamStrength } from 'features/parameters/store/generationSlice';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function SeamStrength() {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
const seamStrength = useAppSelector(
|
||||
(state: RootState) => state.generation.seamStrength
|
||||
);
|
||||
|
||||
return (
|
||||
<IAISlider
|
||||
sliderMarkRightOffset={-7}
|
||||
label={t('parameters:seamStrength')}
|
||||
min={0.01}
|
||||
max={0.99}
|
||||
step={0.01}
|
||||
value={seamStrength}
|
||||
onChange={(v) => {
|
||||
dispatch(setSeamStrength(v));
|
||||
}}
|
||||
withInput
|
||||
withSliderMarks
|
||||
withReset
|
||||
handleReset={() => {
|
||||
dispatch(setSeamStrength(0.7));
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
|
||||
import { FacetoolType } from 'features/parameters/store/postprocessingSlice';
|
||||
|
||||
import {
|
||||
setCodeformerFidelity,
|
||||
setFacetoolStrength,
|
||||
setFacetoolType,
|
||||
} from 'features/parameters/store/postprocessingSlice';
|
||||
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { isEqual } from 'lodash';
|
||||
import IAINumberInput from 'common/components/IAINumberInput';
|
||||
import IAISelect from 'common/components/IAISelect';
|
||||
import { FACETOOL_TYPES } from 'app/constants';
|
||||
import { ChangeEvent } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { postprocessingSelector } from 'features/parameters/store/postprocessingSelectors';
|
||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||
|
||||
const optionsSelector = createSelector(
|
||||
[postprocessingSelector, systemSelector],
|
||||
(
|
||||
{ facetoolStrength, facetoolType, codeformerFidelity },
|
||||
{ isGFPGANAvailable }
|
||||
) => {
|
||||
return {
|
||||
facetoolStrength,
|
||||
facetoolType,
|
||||
codeformerFidelity,
|
||||
isGFPGANAvailable,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Displays face-fixing/GFPGAN options (strength).
|
||||
*/
|
||||
const FaceRestoreSettings = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const {
|
||||
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 (
|
||||
<Flex direction={'column'} gap={2}>
|
||||
<IAISelect
|
||||
label={t('parameters:type')}
|
||||
validValues={FACETOOL_TYPES.concat()}
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
||||
export default FaceRestoreSettings;
|
||||
@@ -0,0 +1,28 @@
|
||||
import { ChangeEvent } from 'react';
|
||||
import { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAISwitch from 'common/components/IAISwitch';
|
||||
import { setShouldRunFacetool } from 'features/parameters/store/postprocessingSlice';
|
||||
|
||||
export default function FaceRestoreToggle() {
|
||||
const isGFPGANAvailable = useAppSelector(
|
||||
(state: RootState) => state.system.isGFPGANAvailable
|
||||
);
|
||||
|
||||
const shouldRunFacetool = useAppSelector(
|
||||
(state: RootState) => state.postprocessing.shouldRunFacetool
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleChangeShouldRunFacetool = (e: ChangeEvent<HTMLInputElement>) =>
|
||||
dispatch(setShouldRunFacetool(e.target.checked));
|
||||
|
||||
return (
|
||||
<IAISwitch
|
||||
isDisabled={!isGFPGANAvailable}
|
||||
isChecked={shouldRunFacetool}
|
||||
onChange={handleChangeShouldRunFacetool}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import React, { ChangeEvent } from 'react';
|
||||
import { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAISwitch from 'common/components/IAISwitch';
|
||||
import { setShouldFitToWidthHeight } from 'features/parameters/store/generationSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function ImageFit() {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const shouldFitToWidthHeight = useAppSelector(
|
||||
(state: RootState) => state.generation.shouldFitToWidthHeight
|
||||
);
|
||||
|
||||
const handleChangeFit = (e: ChangeEvent<HTMLInputElement>) =>
|
||||
dispatch(setShouldFitToWidthHeight(e.target.checked));
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<IAISwitch
|
||||
label={t('parameters:imageFit')}
|
||||
isChecked={shouldFitToWidthHeight}
|
||||
onChange={handleChangeFit}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import React from 'react';
|
||||
import { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAISlider from 'common/components/IAISlider';
|
||||
import { setImg2imgStrength } from 'features/parameters/store/generationSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
interface ImageToImageStrengthProps {
|
||||
label?: string;
|
||||
styleClass?: string;
|
||||
}
|
||||
|
||||
export default function ImageToImageStrength(props: ImageToImageStrengthProps) {
|
||||
const { t } = useTranslation();
|
||||
const { label = `${t('parameters:strength')}`, styleClass } = props;
|
||||
const img2imgStrength = useAppSelector(
|
||||
(state: RootState) => state.generation.img2imgStrength
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleChangeStrength = (v: number) => dispatch(setImg2imgStrength(v));
|
||||
|
||||
const handleImg2ImgStrengthReset = () => {
|
||||
dispatch(setImg2imgStrength(0.75));
|
||||
};
|
||||
|
||||
return (
|
||||
<IAISlider
|
||||
label={label}
|
||||
step={0.01}
|
||||
min={0.01}
|
||||
max={0.99}
|
||||
onChange={handleChangeStrength}
|
||||
value={img2imgStrength}
|
||||
isInteger={false}
|
||||
styleClass={styleClass}
|
||||
withInput
|
||||
withSliderMarks
|
||||
inputWidth={'5.5rem'}
|
||||
withReset
|
||||
handleReset={handleImg2ImgStrengthReset}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import type { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAISlider from 'common/components/IAISlider';
|
||||
import IAISwitch from 'common/components/IAISwitch';
|
||||
import { postprocessingSelector } from 'features/parameters/store/postprocessingSelectors';
|
||||
import {
|
||||
setHiresFix,
|
||||
setHiresStrength,
|
||||
} from 'features/parameters/store/postprocessingSlice';
|
||||
import { isEqual } from 'lodash';
|
||||
import { ChangeEvent } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const hiresStrengthSelector = createSelector(
|
||||
[postprocessingSelector],
|
||||
({ hiresFix, hiresStrength }) => ({ hiresFix, hiresStrength }),
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const HiresStrength = () => {
|
||||
const { hiresFix, hiresStrength } = useAppSelector(hiresStrengthSelector);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleHiresStrength = (v: number) => {
|
||||
dispatch(setHiresStrength(v));
|
||||
};
|
||||
|
||||
const handleHiResStrengthReset = () => {
|
||||
dispatch(setHiresStrength(0.75));
|
||||
};
|
||||
|
||||
return (
|
||||
<IAISlider
|
||||
label={t('parameters:hiresStrength')}
|
||||
step={0.01}
|
||||
min={0.01}
|
||||
max={0.99}
|
||||
onChange={handleHiresStrength}
|
||||
value={hiresStrength}
|
||||
isInteger={false}
|
||||
withInput
|
||||
withSliderMarks
|
||||
inputWidth={'5.5rem'}
|
||||
withReset
|
||||
handleReset={handleHiResStrengthReset}
|
||||
isSliderDisabled={!hiresFix}
|
||||
isInputDisabled={!hiresFix}
|
||||
isResetDisabled={!hiresFix}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Hires Fix Toggle
|
||||
*/
|
||||
const HiresSettings = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const hiresFix = useAppSelector(
|
||||
(state: RootState) => state.postprocessing.hiresFix
|
||||
);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleChangeHiresFix = (e: ChangeEvent<HTMLInputElement>) =>
|
||||
dispatch(setHiresFix(e.target.checked));
|
||||
|
||||
return (
|
||||
<Flex gap={2} direction={'column'}>
|
||||
<IAISwitch
|
||||
label={t('parameters:hiresOptim')}
|
||||
fontSize={'md'}
|
||||
isChecked={hiresFix}
|
||||
onChange={handleChangeHiresFix}
|
||||
/>
|
||||
<HiresStrength />
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default HiresSettings;
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import SeamlessSettings from './SeamlessSettings';
|
||||
|
||||
const ImageToImageOutputSettings = () => {
|
||||
return (
|
||||
<Flex gap={2} direction={'column'}>
|
||||
<SeamlessSettings />
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default ImageToImageOutputSettings;
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import HiresSettings from './HiresSettings';
|
||||
import SeamlessSettings from './SeamlessSettings';
|
||||
|
||||
const OutputSettings = () => {
|
||||
return (
|
||||
<Flex gap={2} direction={'column'}>
|
||||
<SeamlessSettings />
|
||||
<HiresSettings />
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default OutputSettings;
|
||||
@@ -0,0 +1,34 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { ChangeEvent } from 'react';
|
||||
import { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAISwitch from 'common/components/IAISwitch';
|
||||
import { setSeamless } from 'features/parameters/store/generationSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
/**
|
||||
* Seamless tiling toggle
|
||||
*/
|
||||
const SeamlessSettings = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const seamless = useAppSelector((state: RootState) => state.generation.seamless);
|
||||
|
||||
const handleChangeSeamless = (e: ChangeEvent<HTMLInputElement>) =>
|
||||
dispatch(setSeamless(e.target.checked));
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Flex gap={2} direction={'column'}>
|
||||
<IAISwitch
|
||||
label={t('parameters:seamlessTiling')}
|
||||
fontSize={'md'}
|
||||
isChecked={seamless}
|
||||
onChange={handleChangeSeamless}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default SeamlessSettings;
|
||||
@@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAINumberInput from 'common/components/IAINumberInput';
|
||||
import { setPerlin } from 'features/parameters/store/generationSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function Perlin() {
|
||||
const dispatch = useAppDispatch();
|
||||
const perlin = useAppSelector((state: RootState) => state.generation.perlin);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleChangePerlin = (v: number) => dispatch(setPerlin(v));
|
||||
|
||||
return (
|
||||
<IAINumberInput
|
||||
label={t('parameters:perlinNoise')}
|
||||
min={0}
|
||||
max={1}
|
||||
step={0.05}
|
||||
onChange={handleChangePerlin}
|
||||
value={perlin}
|
||||
isInteger={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { ChangeEvent } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAISwitch from 'common/components/IAISwitch';
|
||||
import { setShouldRandomizeSeed } from 'features/parameters/store/generationSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function RandomizeSeed() {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const shouldRandomizeSeed = useAppSelector(
|
||||
(state: RootState) => state.generation.shouldRandomizeSeed
|
||||
);
|
||||
|
||||
const handleChangeShouldRandomizeSeed = (e: ChangeEvent<HTMLInputElement>) =>
|
||||
dispatch(setShouldRandomizeSeed(e.target.checked));
|
||||
|
||||
return (
|
||||
<IAISwitch
|
||||
label={t('parameters:randomizeSeed')}
|
||||
isChecked={shouldRandomizeSeed}
|
||||
onChange={handleChangeShouldRandomizeSeed}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
import { NUMPY_RAND_MAX, NUMPY_RAND_MIN } from 'app/constants';
|
||||
import { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAINumberInput from 'common/components/IAINumberInput';
|
||||
import { setSeed } from 'features/parameters/store/generationSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function Seed() {
|
||||
const seed = useAppSelector((state: RootState) => state.generation.seed);
|
||||
const shouldRandomizeSeed = useAppSelector(
|
||||
(state: RootState) => state.generation.shouldRandomizeSeed
|
||||
);
|
||||
const shouldGenerateVariations = useAppSelector(
|
||||
(state: RootState) => state.generation.shouldGenerateVariations
|
||||
);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleChangeSeed = (v: number) => dispatch(setSeed(v));
|
||||
|
||||
return (
|
||||
<IAINumberInput
|
||||
label={t('parameters:seed')}
|
||||
step={1}
|
||||
precision={0}
|
||||
flexGrow={1}
|
||||
min={NUMPY_RAND_MIN}
|
||||
max={NUMPY_RAND_MAX}
|
||||
isDisabled={shouldRandomizeSeed}
|
||||
isInvalid={seed < 0 && shouldGenerateVariations}
|
||||
onChange={handleChangeSeed}
|
||||
value={seed}
|
||||
width="auto"
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import RandomizeSeed from './RandomizeSeed';
|
||||
import Seed from './Seed';
|
||||
import ShuffleSeed from './ShuffleSeed';
|
||||
import Threshold from './Threshold';
|
||||
import Perlin from './Perlin';
|
||||
|
||||
/**
|
||||
* Seed & variation options. Includes iteration, seed, seed randomization, variation options.
|
||||
*/
|
||||
const SeedSettings = () => {
|
||||
return (
|
||||
<Flex gap={2} direction={'column'}>
|
||||
<RandomizeSeed />
|
||||
<Flex gap={2}>
|
||||
<Seed />
|
||||
<ShuffleSeed />
|
||||
</Flex>
|
||||
<Flex gap={2}>
|
||||
<Threshold />
|
||||
</Flex>
|
||||
<Flex gap={2}>
|
||||
<Perlin />
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default SeedSettings;
|
||||
@@ -0,0 +1,30 @@
|
||||
import { Button } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import { NUMPY_RAND_MAX, NUMPY_RAND_MIN } from 'app/constants';
|
||||
import { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import randomInt from 'common/util/randomInt';
|
||||
import { setSeed } from 'features/parameters/store/generationSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function ShuffleSeed() {
|
||||
const dispatch = useAppDispatch();
|
||||
const shouldRandomizeSeed = useAppSelector(
|
||||
(state: RootState) => state.generation.shouldRandomizeSeed
|
||||
);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleClickRandomizeSeed = () =>
|
||||
dispatch(setSeed(randomInt(NUMPY_RAND_MIN, NUMPY_RAND_MAX)));
|
||||
|
||||
return (
|
||||
<Button
|
||||
size={'sm'}
|
||||
isDisabled={shouldRandomizeSeed}
|
||||
onClick={handleClickRandomizeSeed}
|
||||
padding="0 1.5rem"
|
||||
>
|
||||
<p>{t('parameters:shuffle')}</p>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAINumberInput from 'common/components/IAINumberInput';
|
||||
import { setThreshold } from 'features/parameters/store/generationSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function Threshold() {
|
||||
const dispatch = useAppDispatch();
|
||||
const threshold = useAppSelector(
|
||||
(state: RootState) => state.generation.threshold
|
||||
);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleChangeThreshold = (v: number) => dispatch(setThreshold(v));
|
||||
|
||||
return (
|
||||
<IAINumberInput
|
||||
label={t('parameters:noiseThreshold')}
|
||||
min={0}
|
||||
max={1000}
|
||||
step={0.1}
|
||||
onChange={handleChangeThreshold}
|
||||
value={threshold}
|
||||
isInteger={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.upscale-settings {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
column-gap: 1rem;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
|
||||
import { UpscalingLevel } from 'features/parameters/store/postprocessingSlice';
|
||||
import {
|
||||
setUpscalingLevel,
|
||||
setUpscalingStrength,
|
||||
} from 'features/parameters/store/postprocessingSlice';
|
||||
|
||||
import { UPSCALING_LEVELS } from 'app/constants';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { isEqual } from 'lodash';
|
||||
import { ChangeEvent } from 'react';
|
||||
import IAINumberInput from 'common/components/IAINumberInput';
|
||||
import IAISelect from 'common/components/IAISelect';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { postprocessingSelector } from 'features/parameters/store/postprocessingSelectors';
|
||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||
|
||||
const parametersSelector = createSelector(
|
||||
[postprocessingSelector, systemSelector],
|
||||
|
||||
({ upscalingLevel, upscalingStrength }, { isESRGANAvailable }) => {
|
||||
return {
|
||||
upscalingLevel,
|
||||
upscalingStrength,
|
||||
isESRGANAvailable,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Displays upscaling/ESRGAN options (level and strength).
|
||||
*/
|
||||
const UpscaleSettings = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { upscalingLevel, upscalingStrength, 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 (
|
||||
<div className="upscale-settings">
|
||||
<IAISelect
|
||||
isDisabled={!isESRGANAvailable}
|
||||
label={t('parameters:scale')}
|
||||
value={upscalingLevel}
|
||||
onChange={handleChangeLevel}
|
||||
validValues={UPSCALING_LEVELS}
|
||||
/>
|
||||
<IAINumberInput
|
||||
isDisabled={!isESRGANAvailable}
|
||||
label={t('parameters:strength')}
|
||||
step={0.05}
|
||||
min={0}
|
||||
max={1}
|
||||
onChange={handleChangeStrength}
|
||||
value={upscalingStrength}
|
||||
isInteger={false}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UpscaleSettings;
|
||||
@@ -0,0 +1,26 @@
|
||||
import { ChangeEvent } from 'react';
|
||||
import { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAISwitch from 'common/components/IAISwitch';
|
||||
import { setShouldRunESRGAN } from 'features/parameters/store/postprocessingSlice';
|
||||
|
||||
export default function UpscaleToggle() {
|
||||
const isESRGANAvailable = useAppSelector(
|
||||
(state: RootState) => state.system.isESRGANAvailable
|
||||
);
|
||||
|
||||
const shouldRunESRGAN = useAppSelector(
|
||||
(state: RootState) => state.postprocessing.shouldRunESRGAN
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const handleChangeShouldRunESRGAN = (e: ChangeEvent<HTMLInputElement>) =>
|
||||
dispatch(setShouldRunESRGAN(e.target.checked));
|
||||
return (
|
||||
<IAISwitch
|
||||
isDisabled={!isESRGANAvailable}
|
||||
isChecked={shouldRunESRGAN}
|
||||
onChange={handleChangeShouldRunESRGAN}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import React, { ChangeEvent } from 'react';
|
||||
import { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAISwitch from 'common/components/IAISwitch';
|
||||
import { setShouldGenerateVariations } from 'features/parameters/store/generationSlice';
|
||||
|
||||
export default function GenerateVariationsToggle() {
|
||||
const shouldGenerateVariations = useAppSelector(
|
||||
(state: RootState) => state.generation.shouldGenerateVariations
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleChangeShouldGenerateVariations = (
|
||||
e: ChangeEvent<HTMLInputElement>
|
||||
) => dispatch(setShouldGenerateVariations(e.target.checked));
|
||||
|
||||
return (
|
||||
<IAISwitch
|
||||
isChecked={shouldGenerateVariations}
|
||||
width={'auto'}
|
||||
onChange={handleChangeShouldGenerateVariations}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import React, { ChangeEvent } from 'react';
|
||||
import { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAIInput from 'common/components/IAIInput';
|
||||
import { validateSeedWeights } from 'common/util/seedWeightPairs';
|
||||
import { setSeedWeights } from 'features/parameters/store/generationSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function SeedWeights() {
|
||||
const seedWeights = useAppSelector(
|
||||
(state: RootState) => state.generation.seedWeights
|
||||
);
|
||||
|
||||
const shouldGenerateVariations = useAppSelector(
|
||||
(state: RootState) => state.generation.shouldGenerateVariations
|
||||
);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleChangeSeedWeights = (e: ChangeEvent<HTMLInputElement>) =>
|
||||
dispatch(setSeedWeights(e.target.value));
|
||||
|
||||
return (
|
||||
<IAIInput
|
||||
label={t('parameters:seedWeights')}
|
||||
value={seedWeights}
|
||||
isInvalid={
|
||||
shouldGenerateVariations &&
|
||||
!(validateSeedWeights(seedWeights) || seedWeights === '')
|
||||
}
|
||||
isDisabled={!shouldGenerateVariations}
|
||||
onChange={handleChangeSeedWeights}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
import { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAINumberInput from 'common/components/IAINumberInput';
|
||||
import { setVariationAmount } from 'features/parameters/store/generationSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function VariationAmount() {
|
||||
const variationAmount = useAppSelector(
|
||||
(state: RootState) => state.generation.variationAmount
|
||||
);
|
||||
|
||||
const shouldGenerateVariations = useAppSelector(
|
||||
(state: RootState) => state.generation.shouldGenerateVariations
|
||||
);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const handleChangevariationAmount = (v: number) =>
|
||||
dispatch(setVariationAmount(v));
|
||||
|
||||
return (
|
||||
<IAINumberInput
|
||||
label={t('parameters:variationAmount')}
|
||||
value={variationAmount}
|
||||
step={0.01}
|
||||
min={0}
|
||||
max={1}
|
||||
isDisabled={!shouldGenerateVariations}
|
||||
onChange={handleChangevariationAmount}
|
||||
isInteger={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import SeedWeights from './SeedWeights';
|
||||
import VariationAmount from './VariationAmount';
|
||||
|
||||
/**
|
||||
* Seed & variation options. Includes iteration, seed, seed randomization, variation options.
|
||||
*/
|
||||
const VariationsSettings = () => {
|
||||
return (
|
||||
<Flex gap={2} direction={'column'}>
|
||||
<VariationAmount />
|
||||
<SeedWeights />
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default VariationsSettings;
|
||||
@@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
import { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAINumberInput from 'common/components/IAINumberInput';
|
||||
import { setCfgScale } from 'features/parameters/store/generationSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function MainCFGScale() {
|
||||
const dispatch = useAppDispatch();
|
||||
const cfgScale = useAppSelector((state: RootState) => state.generation.cfgScale);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleChangeCfgScale = (v: number) => dispatch(setCfgScale(v));
|
||||
|
||||
return (
|
||||
<IAINumberInput
|
||||
label={t('parameters:cfgScale')}
|
||||
step={0.5}
|
||||
min={1.01}
|
||||
max={200}
|
||||
onChange={handleChangeCfgScale}
|
||||
value={cfgScale}
|
||||
width="auto"
|
||||
styleClass="main-settings-block"
|
||||
textAlign="center"
|
||||
isInteger={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { ChangeEvent } from 'react';
|
||||
import React from 'react';
|
||||
import { HEIGHTS } from 'app/constants';
|
||||
import { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAISelect from 'common/components/IAISelect';
|
||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||
import { setHeight } from 'features/parameters/store/generationSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function MainHeight() {
|
||||
const height = useAppSelector((state: RootState) => state.generation.height);
|
||||
const activeTabName = useAppSelector(activeTabNameSelector);
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleChangeHeight = (e: ChangeEvent<HTMLSelectElement>) =>
|
||||
dispatch(setHeight(Number(e.target.value)));
|
||||
|
||||
return (
|
||||
<IAISelect
|
||||
isDisabled={activeTabName === 'unifiedCanvas'}
|
||||
label={t('parameters:height')}
|
||||
value={height}
|
||||
flexGrow={1}
|
||||
onChange={handleChangeHeight}
|
||||
validValues={HEIGHTS}
|
||||
styleClass="main-settings-block"
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import _ from 'lodash';
|
||||
import React from 'react';
|
||||
import type { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAINumberInput from 'common/components/IAINumberInput';
|
||||
import {
|
||||
GenerationState,
|
||||
setIterations,
|
||||
} from 'features/parameters/store/generationSlice';
|
||||
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() {
|
||||
const dispatch = useAppDispatch();
|
||||
const { iterations } = useAppSelector(mainIterationsSelector);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleChangeIterations = (v: number) => dispatch(setIterations(v));
|
||||
|
||||
return (
|
||||
<IAINumberInput
|
||||
label={t('parameters:images')}
|
||||
step={1}
|
||||
min={1}
|
||||
max={9999}
|
||||
onChange={handleChangeIterations}
|
||||
value={iterations}
|
||||
width="auto"
|
||||
labelFontSize={0.5}
|
||||
styleClass="main-settings-block"
|
||||
textAlign="center"
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
@use '../../../../styles/Mixins/' as *;
|
||||
|
||||
.main-settings {
|
||||
display: grid;
|
||||
row-gap: 1rem;
|
||||
}
|
||||
|
||||
.main-settings-list {
|
||||
display: grid;
|
||||
row-gap: 1rem;
|
||||
}
|
||||
|
||||
.main-settings-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, auto);
|
||||
column-gap: 0.5rem;
|
||||
max-width: $options-bar-max-width;
|
||||
}
|
||||
|
||||
.main-settings-block {
|
||||
border-radius: 0.5rem;
|
||||
display: grid !important;
|
||||
grid-template-columns: auto !important;
|
||||
row-gap: 0.5rem;
|
||||
|
||||
.invokeai__number-input-form-label,
|
||||
.invokeai__select-label {
|
||||
font-weight: bold;
|
||||
font-size: 0.9rem !important;
|
||||
}
|
||||
|
||||
.invokeai__select-label {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import MainCFGScale from './MainCFGScale';
|
||||
import MainHeight from './MainHeight';
|
||||
import MainIterations from './MainIterations';
|
||||
import MainSampler from './MainSampler';
|
||||
import MainSteps from './MainSteps';
|
||||
import MainWidth from './MainWidth';
|
||||
|
||||
export const inputWidth = 'auto';
|
||||
|
||||
export default function MainSettings() {
|
||||
return (
|
||||
<div className="main-settings">
|
||||
<div className="main-settings-list">
|
||||
<div className="main-settings-row">
|
||||
<MainIterations />
|
||||
<MainSteps />
|
||||
<MainCFGScale />
|
||||
</div>
|
||||
<div className="main-settings-row">
|
||||
<MainWidth />
|
||||
<MainHeight />
|
||||
<MainSampler />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { DIFFUSERS_SAMPLERS, SAMPLERS } from 'app/constants';
|
||||
import { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAISelect from 'common/components/IAISelect';
|
||||
import { setSampler } from 'features/parameters/store/generationSlice';
|
||||
import { activeModelSelector } from 'features/system/store/systemSelectors';
|
||||
import { ChangeEvent } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function MainSampler() {
|
||||
const sampler = useAppSelector(
|
||||
(state: RootState) => state.generation.sampler
|
||||
);
|
||||
const activeModel = useAppSelector(activeModelSelector);
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleChangeSampler = (e: ChangeEvent<HTMLSelectElement>) =>
|
||||
dispatch(setSampler(e.target.value));
|
||||
|
||||
return (
|
||||
<IAISelect
|
||||
label={t('parameters:sampler')}
|
||||
value={sampler}
|
||||
onChange={handleChangeSampler}
|
||||
validValues={
|
||||
activeModel.format === 'diffusers' ? DIFFUSERS_SAMPLERS : SAMPLERS
|
||||
}
|
||||
styleClass="main-option-block"
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAINumberInput from 'common/components/IAINumberInput';
|
||||
import { setSteps } from 'features/parameters/store/generationSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function MainSteps() {
|
||||
const dispatch = useAppDispatch();
|
||||
const steps = useAppSelector((state: RootState) => state.generation.steps);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleChangeSteps = (v: number) => dispatch(setSteps(v));
|
||||
|
||||
return (
|
||||
<IAINumberInput
|
||||
label={t('parameters:steps')}
|
||||
min={1}
|
||||
max={9999}
|
||||
step={1}
|
||||
onChange={handleChangeSteps}
|
||||
value={steps}
|
||||
width="auto"
|
||||
styleClass="main-settings-block"
|
||||
textAlign="center"
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import React, { ChangeEvent } from 'react';
|
||||
import { WIDTHS } from 'app/constants';
|
||||
import { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAISelect from 'common/components/IAISelect';
|
||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||
import { setWidth } from 'features/parameters/store/generationSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function MainWidth() {
|
||||
const width = useAppSelector((state: RootState) => state.generation.width);
|
||||
const activeTabName = useAppSelector(activeTabNameSelector);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleChangeWidth = (e: ChangeEvent<HTMLSelectElement>) =>
|
||||
dispatch(setWidth(Number(e.target.value)));
|
||||
|
||||
return (
|
||||
<IAISelect
|
||||
isDisabled={activeTabName === 'unifiedCanvas'}
|
||||
label={t('parameters:width')}
|
||||
value={width}
|
||||
flexGrow={1}
|
||||
onChange={handleChangeWidth}
|
||||
validValues={WIDTHS}
|
||||
styleClass="main-settings-block"
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import { Accordion, ExpandedIndex } from '@chakra-ui/react';
|
||||
import { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import { setOpenAccordions } from 'features/system/store/systemSlice';
|
||||
import InvokeAccordionItem, {
|
||||
InvokeAccordionItemProps,
|
||||
} from './AccordionItems/InvokeAccordionItem';
|
||||
import { ReactElement } from 'react';
|
||||
|
||||
type ParametersAccordionType = {
|
||||
[parametersAccordionKey: string]: InvokeAccordionItemProps;
|
||||
};
|
||||
|
||||
type ParametersAccordionsType = {
|
||||
accordionInfo: ParametersAccordionType;
|
||||
};
|
||||
|
||||
/**
|
||||
* Main container for generation and processing parameters.
|
||||
*/
|
||||
const ParametersAccordion = (props: ParametersAccordionsType) => {
|
||||
const { accordionInfo } = props;
|
||||
|
||||
const openAccordions = useAppSelector(
|
||||
(state: RootState) => state.system.openAccordions
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
/**
|
||||
* Stores accordion state in redux so preferred UI setup is retained.
|
||||
*/
|
||||
const handleChangeAccordionState = (openAccordions: ExpandedIndex) =>
|
||||
dispatch(setOpenAccordions(openAccordions));
|
||||
|
||||
const renderAccordions = () => {
|
||||
const accordionsToRender: ReactElement[] = [];
|
||||
if (accordionInfo) {
|
||||
Object.keys(accordionInfo).forEach((key) => {
|
||||
const { header, feature, content, additionalHeaderComponents } =
|
||||
accordionInfo[key];
|
||||
accordionsToRender.push(
|
||||
<InvokeAccordionItem
|
||||
key={key}
|
||||
header={header}
|
||||
feature={feature}
|
||||
content={content}
|
||||
additionalHeaderComponents={additionalHeaderComponents}
|
||||
/>
|
||||
);
|
||||
});
|
||||
}
|
||||
return accordionsToRender;
|
||||
};
|
||||
|
||||
return (
|
||||
<Accordion
|
||||
defaultIndex={openAccordions}
|
||||
allowMultiple
|
||||
reduceMotion
|
||||
onChange={handleChangeAccordionState}
|
||||
className="advanced-parameters"
|
||||
>
|
||||
{renderAccordions()}
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
export default ParametersAccordion;
|
||||
@@ -0,0 +1,62 @@
|
||||
import { MdCancel } from 'react-icons/md';
|
||||
import { cancelProcessing } from 'app/socketio/actions';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAIIconButton, {
|
||||
IAIIconButtonProps,
|
||||
} from 'common/components/IAIIconButton';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { SystemState } from 'features/system/store/systemSlice';
|
||||
import _ from 'lodash';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||
|
||||
const cancelButtonSelector = createSelector(
|
||||
systemSelector,
|
||||
(system: SystemState) => {
|
||||
return {
|
||||
isProcessing: system.isProcessing,
|
||||
isConnected: system.isConnected,
|
||||
isCancelable: system.isCancelable,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: _.isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export default function CancelButton(
|
||||
props: Omit<IAIIconButtonProps, 'aria-label'>
|
||||
) {
|
||||
const { ...rest } = props;
|
||||
const dispatch = useAppDispatch();
|
||||
const { isProcessing, isConnected, isCancelable } =
|
||||
useAppSelector(cancelButtonSelector);
|
||||
const handleClickCancel = () => dispatch(cancelProcessing());
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
useHotkeys(
|
||||
'shift+x',
|
||||
() => {
|
||||
if ((isConnected || isProcessing) && isCancelable) {
|
||||
handleClickCancel();
|
||||
}
|
||||
},
|
||||
[isConnected, isProcessing, isCancelable]
|
||||
);
|
||||
|
||||
return (
|
||||
<IAIIconButton
|
||||
icon={<MdCancel />}
|
||||
tooltip={t('parameters:cancel')}
|
||||
aria-label={t('parameters:cancel')}
|
||||
isDisabled={!isConnected || !isProcessing || !isCancelable}
|
||||
onClick={handleClickCancel}
|
||||
styleClass="cancel-btn"
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { FaPlay } from 'react-icons/fa';
|
||||
import { readinessSelector } from 'app/selectors/readinessSelector';
|
||||
import { generateImage } from 'app/socketio/actions';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAIButton, { IAIButtonProps } from 'common/components/IAIButton';
|
||||
import IAIIconButton, {
|
||||
IAIIconButtonProps,
|
||||
} from 'common/components/IAIIconButton';
|
||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
interface InvokeButton
|
||||
extends Omit<IAIButtonProps | IAIIconButtonProps, 'aria-label'> {
|
||||
iconButton?: boolean;
|
||||
}
|
||||
|
||||
export default function InvokeButton(props: InvokeButton) {
|
||||
const { iconButton = false, ...rest } = props;
|
||||
const dispatch = useAppDispatch();
|
||||
const { isReady } = useAppSelector(readinessSelector);
|
||||
const activeTabName = useAppSelector(activeTabNameSelector);
|
||||
|
||||
const handleClickGenerate = () => {
|
||||
dispatch(generateImage(activeTabName));
|
||||
};
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
useHotkeys(
|
||||
['ctrl+enter', 'meta+enter'],
|
||||
() => {
|
||||
dispatch(generateImage(activeTabName));
|
||||
},
|
||||
{
|
||||
enabled: () => isReady,
|
||||
preventDefault: true,
|
||||
enableOnFormTags: ['input', 'textarea', 'select'],
|
||||
},
|
||||
[isReady, activeTabName]
|
||||
);
|
||||
|
||||
return (
|
||||
<div style={{ flexGrow: 4 }}>
|
||||
{iconButton ? (
|
||||
<IAIIconButton
|
||||
aria-label={t('parameters:invoke')}
|
||||
type="submit"
|
||||
icon={<FaPlay />}
|
||||
isDisabled={!isReady}
|
||||
onClick={handleClickGenerate}
|
||||
className="invoke-btn"
|
||||
tooltip={t('parameters:invoke')}
|
||||
tooltipProps={{ placement: 'bottom' }}
|
||||
{...rest}
|
||||
/>
|
||||
) : (
|
||||
<IAIButton
|
||||
aria-label={t('parameters:invoke')}
|
||||
type="submit"
|
||||
isDisabled={!isReady}
|
||||
onClick={handleClickGenerate}
|
||||
className="invoke-btn"
|
||||
{...rest}
|
||||
>
|
||||
Invoke
|
||||
</IAIButton>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { FaRecycle } from 'react-icons/fa';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { setShouldLoopback } from 'features/parameters/store/postprocessingSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { postprocessingSelector } from 'features/parameters/store/postprocessingSelectors';
|
||||
|
||||
const loopbackSelector = createSelector(
|
||||
postprocessingSelector,
|
||||
({ shouldLoopback }) => shouldLoopback
|
||||
);
|
||||
|
||||
const LoopbackButton = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const shouldLoopback = useAppSelector(loopbackSelector);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<IAIIconButton
|
||||
aria-label={t('parameters:toggleLoopback')}
|
||||
tooltip={t('parameters:toggleLoopback')}
|
||||
styleClass="loopback-btn"
|
||||
asCheckbox={true}
|
||||
isChecked={shouldLoopback}
|
||||
icon={<FaRecycle />}
|
||||
onClick={() => {
|
||||
dispatch(setShouldLoopback(!shouldLoopback));
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoopbackButton;
|
||||
@@ -0,0 +1,56 @@
|
||||
@use '../../../../styles/Mixins/' as *;
|
||||
|
||||
.process-buttons {
|
||||
display: flex;
|
||||
column-gap: 0.5rem;
|
||||
}
|
||||
|
||||
.invoke-btn {
|
||||
flex-grow: 1;
|
||||
width: 100%;
|
||||
|
||||
@include Button(
|
||||
$btn-color: var(--accent-color),
|
||||
$btn-color-hover: var(--accent-color-hover),
|
||||
$icon-size: 16px
|
||||
);
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
@include Button(
|
||||
$btn-color: var(--destructive-color),
|
||||
$btn-color-hover: var(--destructive-color-hover),
|
||||
$btn-width: 3rem
|
||||
);
|
||||
}
|
||||
|
||||
.loopback-btn {
|
||||
&[data-as-checkbox='true'] {
|
||||
background-color: var(--btn-btn-base-color);
|
||||
border: 3px solid var(--btn-btn-base-color);
|
||||
svg {
|
||||
fill: var(--text-color);
|
||||
}
|
||||
&:hover {
|
||||
background-color: var(--btn-btn-base-color);
|
||||
border-color: var(--btn-checkbox-border-hover);
|
||||
svg {
|
||||
fill: var(--text-color);
|
||||
}
|
||||
}
|
||||
&[data-selected='true'] {
|
||||
border-color: var(--accent-color);
|
||||
background-color: var(--btn-btn-base-color);
|
||||
svg {
|
||||
fill: var(--text-color);
|
||||
}
|
||||
&:hover {
|
||||
border-color: var(--accent-color);
|
||||
background-color: var(--btn-btn-base-color);
|
||||
svg {
|
||||
fill: var(--text-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import InvokeButton from './InvokeButton';
|
||||
import CancelButton from './CancelButton';
|
||||
import LoopbackButton from './Loopback';
|
||||
import { useAppSelector } from 'app/storeHooks';
|
||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||
|
||||
/**
|
||||
* Buttons to start and cancel image generation.
|
||||
*/
|
||||
const ProcessButtons = () => {
|
||||
const activeTabName = useAppSelector(activeTabNameSelector);
|
||||
|
||||
return (
|
||||
<div className="process-buttons">
|
||||
<InvokeButton />
|
||||
{activeTabName === 'img2img' && <LoopbackButton />}
|
||||
<CancelButton />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProcessButtons;
|
||||
@@ -0,0 +1,40 @@
|
||||
import { FormControl, Textarea } from '@chakra-ui/react';
|
||||
import type { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import { setNegativePrompt } from 'features/parameters/store/generationSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const NegativePromptInput = () => {
|
||||
const negativePrompt = useAppSelector(
|
||||
(state: RootState) => state.generation.negativePrompt
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<FormControl>
|
||||
<Textarea
|
||||
id="negativePrompt"
|
||||
name="negativePrompt"
|
||||
value={negativePrompt}
|
||||
onChange={(e) => dispatch(setNegativePrompt(e.target.value))}
|
||||
background="var(--prompt-bg-color)"
|
||||
placeholder={t('parameters:negativePrompts')}
|
||||
_placeholder={{ fontSize: '0.8rem' }}
|
||||
borderColor="var(--border-color)"
|
||||
_hover={{
|
||||
borderColor: 'var(--border-color-light)',
|
||||
}}
|
||||
_focusVisible={{
|
||||
borderColor: 'var(--border-color-invalid)',
|
||||
boxShadow: '0 0 10px var(--box-shadow-color-invalid)',
|
||||
}}
|
||||
fontSize="0.9rem"
|
||||
color="var(--text-color-secondary)"
|
||||
/>
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
|
||||
export default NegativePromptInput;
|
||||
@@ -0,0 +1,34 @@
|
||||
.prompt-bar {
|
||||
display: grid;
|
||||
row-gap: 1rem;
|
||||
|
||||
input,
|
||||
textarea {
|
||||
background-color: var(--prompt-bg-color);
|
||||
font-size: 1rem;
|
||||
border: 2px solid var(--border-color);
|
||||
|
||||
&:hover {
|
||||
border: 2px solid var(--border-color-light);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
border: 2px solid var(--input-border-color);
|
||||
box-shadow: 0 0 10px 0 var(--input-box-shadow-color);
|
||||
}
|
||||
|
||||
&[aria-invalid='true'] {
|
||||
border: 2px solid var(--border-color-invalid);
|
||||
box-shadow: 0 0 10px 0 var(--box-shadow-color-invalid);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
border: 2px solid var(--border-color);
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
textarea {
|
||||
min-height: 10rem;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import { FormControl, Textarea } from '@chakra-ui/react';
|
||||
import { ChangeEvent, KeyboardEvent, useRef } from 'react';
|
||||
import { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import { generateImage } from 'app/socketio/actions';
|
||||
|
||||
import { GenerationState, setPrompt } from 'features/parameters/store/generationSlice';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import _ from 'lodash';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||
import { readinessSelector } from 'app/selectors/readinessSelector';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const promptInputSelector = createSelector(
|
||||
[(state: RootState) => state.generation, activeTabNameSelector],
|
||||
(parameters: GenerationState, activeTabName) => {
|
||||
return {
|
||||
prompt: parameters.prompt,
|
||||
activeTabName,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: _.isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Prompt input text area.
|
||||
*/
|
||||
const PromptInput = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { prompt, activeTabName } = useAppSelector(promptInputSelector);
|
||||
const { isReady } = useAppSelector(readinessSelector);
|
||||
|
||||
const promptRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleChangePrompt = (e: ChangeEvent<HTMLTextAreaElement>) => {
|
||||
dispatch(setPrompt(e.target.value));
|
||||
};
|
||||
|
||||
useHotkeys(
|
||||
'alt+a',
|
||||
() => {
|
||||
promptRef.current?.focus();
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
if (e.key === 'Enter' && e.shiftKey === false && isReady) {
|
||||
e.preventDefault();
|
||||
dispatch(generateImage(activeTabName));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="prompt-bar">
|
||||
<FormControl
|
||||
isInvalid={prompt.length === 0 || Boolean(prompt.match(/^[\s\r\n]+$/))}
|
||||
>
|
||||
<Textarea
|
||||
id="prompt"
|
||||
name="prompt"
|
||||
placeholder={t('parameters:promptPlaceholder')}
|
||||
size={'lg'}
|
||||
value={prompt}
|
||||
onChange={handleChangePrompt}
|
||||
onKeyDown={handleKeyDown}
|
||||
resize="vertical"
|
||||
height={30}
|
||||
ref={promptRef}
|
||||
_placeholder={{
|
||||
color: 'var(--text-color-secondary)',
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PromptInput;
|
||||
@@ -0,0 +1,17 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import _ from 'lodash';
|
||||
import { RootState } from 'app/store';
|
||||
|
||||
export const generationSelector = (state: RootState) => state.generation;
|
||||
|
||||
export const mayGenerateMultipleImagesSelector = createSelector(
|
||||
generationSelector,
|
||||
({ shouldRandomizeSeed, shouldGenerateVariations }) => {
|
||||
return shouldRandomizeSeed || shouldGenerateVariations;
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: _.isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,358 @@
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import * as InvokeAI from 'app/invokeai';
|
||||
import promptToString from 'common/util/promptToString';
|
||||
import { seedWeightsToString } from 'common/util/seedWeightPairs';
|
||||
import { getPromptAndNegative } from 'common/util/getPromptAndNegative';
|
||||
|
||||
export interface GenerationState {
|
||||
cfgScale: number;
|
||||
height: number;
|
||||
img2imgStrength: number;
|
||||
infillMethod: string;
|
||||
initialImage?: InvokeAI.Image | string; // can be an Image or url
|
||||
iterations: number;
|
||||
maskPath: string;
|
||||
perlin: number;
|
||||
prompt: string;
|
||||
negativePrompt: string;
|
||||
sampler: string;
|
||||
seamBlur: number;
|
||||
seamless: boolean;
|
||||
seamSize: number;
|
||||
seamSteps: number;
|
||||
seamStrength: number;
|
||||
seed: number;
|
||||
seedWeights: string;
|
||||
shouldFitToWidthHeight: boolean;
|
||||
shouldGenerateVariations: boolean;
|
||||
shouldRandomizeSeed: boolean;
|
||||
steps: number;
|
||||
threshold: number;
|
||||
tileSize: number;
|
||||
variationAmount: number;
|
||||
width: number;
|
||||
}
|
||||
|
||||
const initialGenerationState: GenerationState = {
|
||||
cfgScale: 7.5,
|
||||
height: 512,
|
||||
img2imgStrength: 0.75,
|
||||
infillMethod: 'patchmatch',
|
||||
iterations: 1,
|
||||
maskPath: '',
|
||||
perlin: 0,
|
||||
prompt: '',
|
||||
negativePrompt: '',
|
||||
sampler: 'k_lms',
|
||||
seamBlur: 16,
|
||||
seamless: false,
|
||||
seamSize: 96,
|
||||
seamSteps: 30,
|
||||
seamStrength: 0.7,
|
||||
seed: 0,
|
||||
seedWeights: '',
|
||||
shouldFitToWidthHeight: true,
|
||||
shouldGenerateVariations: false,
|
||||
shouldRandomizeSeed: true,
|
||||
steps: 50,
|
||||
threshold: 0,
|
||||
tileSize: 32,
|
||||
variationAmount: 0.1,
|
||||
width: 512,
|
||||
};
|
||||
|
||||
const initialState: GenerationState = initialGenerationState;
|
||||
|
||||
export const generationSlice = createSlice({
|
||||
name: 'generation',
|
||||
initialState,
|
||||
reducers: {
|
||||
setPrompt: (state, action: PayloadAction<string | InvokeAI.Prompt>) => {
|
||||
const newPrompt = action.payload;
|
||||
if (typeof newPrompt === 'string') {
|
||||
state.prompt = newPrompt;
|
||||
} else {
|
||||
state.prompt = promptToString(newPrompt);
|
||||
}
|
||||
},
|
||||
setNegativePrompt: (
|
||||
state,
|
||||
action: PayloadAction<string | InvokeAI.Prompt>
|
||||
) => {
|
||||
const newPrompt = action.payload;
|
||||
if (typeof newPrompt === 'string') {
|
||||
state.negativePrompt = newPrompt;
|
||||
} else {
|
||||
state.negativePrompt = promptToString(newPrompt);
|
||||
}
|
||||
},
|
||||
setIterations: (state, action: PayloadAction<number>) => {
|
||||
state.iterations = action.payload;
|
||||
},
|
||||
setSteps: (state, action: PayloadAction<number>) => {
|
||||
state.steps = action.payload;
|
||||
},
|
||||
setCfgScale: (state, action: PayloadAction<number>) => {
|
||||
state.cfgScale = action.payload;
|
||||
},
|
||||
setThreshold: (state, action: PayloadAction<number>) => {
|
||||
state.threshold = action.payload;
|
||||
},
|
||||
setPerlin: (state, action: PayloadAction<number>) => {
|
||||
state.perlin = action.payload;
|
||||
},
|
||||
setHeight: (state, action: PayloadAction<number>) => {
|
||||
state.height = action.payload;
|
||||
},
|
||||
setWidth: (state, action: PayloadAction<number>) => {
|
||||
state.width = action.payload;
|
||||
},
|
||||
setSampler: (state, action: PayloadAction<string>) => {
|
||||
state.sampler = action.payload;
|
||||
},
|
||||
setSeed: (state, action: PayloadAction<number>) => {
|
||||
state.seed = action.payload;
|
||||
state.shouldRandomizeSeed = false;
|
||||
},
|
||||
setImg2imgStrength: (state, action: PayloadAction<number>) => {
|
||||
state.img2imgStrength = action.payload;
|
||||
},
|
||||
setMaskPath: (state, action: PayloadAction<string>) => {
|
||||
state.maskPath = action.payload;
|
||||
},
|
||||
setSeamless: (state, action: PayloadAction<boolean>) => {
|
||||
state.seamless = action.payload;
|
||||
},
|
||||
setShouldFitToWidthHeight: (state, action: PayloadAction<boolean>) => {
|
||||
state.shouldFitToWidthHeight = action.payload;
|
||||
},
|
||||
resetSeed: (state) => {
|
||||
state.seed = -1;
|
||||
},
|
||||
setParameter: (
|
||||
state,
|
||||
action: PayloadAction<{ key: string; value: string | number | boolean }>
|
||||
) => {
|
||||
// TODO: This probably needs to be refactored.
|
||||
// TODO: This probably also needs to be fixed after the reorg.
|
||||
const { key, value } = action.payload;
|
||||
const temp = { ...state, [key]: value };
|
||||
if (key === 'seed') {
|
||||
temp.shouldRandomizeSeed = false;
|
||||
}
|
||||
return temp;
|
||||
},
|
||||
setShouldGenerateVariations: (state, action: PayloadAction<boolean>) => {
|
||||
state.shouldGenerateVariations = action.payload;
|
||||
},
|
||||
setVariationAmount: (state, action: PayloadAction<number>) => {
|
||||
state.variationAmount = action.payload;
|
||||
},
|
||||
setSeedWeights: (state, action: PayloadAction<string>) => {
|
||||
state.seedWeights = action.payload;
|
||||
state.shouldGenerateVariations = true;
|
||||
state.variationAmount = 0;
|
||||
},
|
||||
setAllTextToImageParameters: (
|
||||
state,
|
||||
action: PayloadAction<InvokeAI.Metadata>
|
||||
) => {
|
||||
const {
|
||||
sampler,
|
||||
prompt,
|
||||
seed,
|
||||
variations,
|
||||
steps,
|
||||
cfg_scale,
|
||||
threshold,
|
||||
perlin,
|
||||
seamless,
|
||||
hires_fix,
|
||||
width,
|
||||
height,
|
||||
} = action.payload.image;
|
||||
|
||||
if (variations && variations.length > 0) {
|
||||
state.seedWeights = seedWeightsToString(variations);
|
||||
state.shouldGenerateVariations = true;
|
||||
state.variationAmount = 0;
|
||||
} else {
|
||||
state.shouldGenerateVariations = false;
|
||||
}
|
||||
|
||||
if (seed) {
|
||||
state.seed = seed;
|
||||
state.shouldRandomizeSeed = false;
|
||||
}
|
||||
|
||||
if (prompt) state.prompt = promptToString(prompt);
|
||||
if (sampler) state.sampler = sampler;
|
||||
if (steps) state.steps = steps;
|
||||
if (cfg_scale) state.cfgScale = cfg_scale;
|
||||
if (typeof threshold === 'undefined') {
|
||||
state.threshold = 0;
|
||||
} else {
|
||||
state.threshold = threshold;
|
||||
}
|
||||
if (perlin) state.perlin = perlin;
|
||||
if (typeof perlin === 'undefined') state.perlin = 0;
|
||||
if (typeof seamless === 'boolean') state.seamless = seamless;
|
||||
// if (typeof hires_fix === 'boolean') state.hiresFix = hires_fix; // TODO: Needs to be fixed after reorg
|
||||
if (width) state.width = width;
|
||||
if (height) state.height = height;
|
||||
},
|
||||
setAllImageToImageParameters: (
|
||||
state,
|
||||
action: PayloadAction<InvokeAI.Metadata>
|
||||
) => {
|
||||
const { type, strength, fit, init_image_path, mask_image_path } =
|
||||
action.payload.image;
|
||||
|
||||
if (type === 'img2img') {
|
||||
if (init_image_path) state.initialImage = init_image_path;
|
||||
if (mask_image_path) state.maskPath = mask_image_path;
|
||||
if (strength) state.img2imgStrength = strength;
|
||||
if (typeof fit === 'boolean') state.shouldFitToWidthHeight = fit;
|
||||
}
|
||||
},
|
||||
setAllParameters: (state, action: PayloadAction<InvokeAI.Metadata>) => {
|
||||
const {
|
||||
type,
|
||||
sampler,
|
||||
prompt,
|
||||
seed,
|
||||
variations,
|
||||
steps,
|
||||
cfg_scale,
|
||||
threshold,
|
||||
perlin,
|
||||
seamless,
|
||||
hires_fix,
|
||||
width,
|
||||
height,
|
||||
strength,
|
||||
fit,
|
||||
init_image_path,
|
||||
mask_image_path,
|
||||
} = action.payload.image;
|
||||
|
||||
if (type === 'img2img') {
|
||||
if (init_image_path) state.initialImage = init_image_path;
|
||||
if (mask_image_path) state.maskPath = mask_image_path;
|
||||
if (strength) state.img2imgStrength = strength;
|
||||
if (typeof fit === 'boolean') state.shouldFitToWidthHeight = fit;
|
||||
}
|
||||
|
||||
if (variations && variations.length > 0) {
|
||||
state.seedWeights = seedWeightsToString(variations);
|
||||
state.shouldGenerateVariations = true;
|
||||
state.variationAmount = 0;
|
||||
} else {
|
||||
state.shouldGenerateVariations = false;
|
||||
}
|
||||
|
||||
if (seed) {
|
||||
state.seed = seed;
|
||||
state.shouldRandomizeSeed = false;
|
||||
}
|
||||
|
||||
if (prompt) {
|
||||
const [promptOnly, negativePrompt] = getPromptAndNegative(prompt);
|
||||
if (promptOnly) state.prompt = promptOnly;
|
||||
negativePrompt
|
||||
? (state.negativePrompt = negativePrompt)
|
||||
: (state.negativePrompt = '');
|
||||
}
|
||||
|
||||
if (sampler) state.sampler = sampler;
|
||||
if (steps) state.steps = steps;
|
||||
if (cfg_scale) state.cfgScale = cfg_scale;
|
||||
if (threshold) state.threshold = threshold;
|
||||
if (typeof threshold === 'undefined') state.threshold = 0;
|
||||
if (perlin) state.perlin = perlin;
|
||||
if (typeof perlin === 'undefined') state.perlin = 0;
|
||||
if (typeof seamless === 'boolean') state.seamless = seamless;
|
||||
// if (typeof hires_fix === 'boolean') state.hiresFix = hires_fix; // TODO: Needs to be fixed after reorg
|
||||
if (width) state.width = width;
|
||||
if (height) state.height = height;
|
||||
|
||||
// state.shouldRunESRGAN = false; // TODO: Needs to be fixed after reorg
|
||||
// state.shouldRunFacetool = false; // TODO: Needs to be fixed after reorg
|
||||
},
|
||||
resetParametersState: (state) => {
|
||||
return {
|
||||
...state,
|
||||
...initialGenerationState,
|
||||
};
|
||||
},
|
||||
setShouldRandomizeSeed: (state, action: PayloadAction<boolean>) => {
|
||||
state.shouldRandomizeSeed = action.payload;
|
||||
},
|
||||
setInitialImage: (
|
||||
state,
|
||||
action: PayloadAction<InvokeAI.Image | string>
|
||||
) => {
|
||||
state.initialImage = action.payload;
|
||||
},
|
||||
clearInitialImage: (state) => {
|
||||
state.initialImage = undefined;
|
||||
},
|
||||
setSeamSize: (state, action: PayloadAction<number>) => {
|
||||
state.seamSize = action.payload;
|
||||
},
|
||||
setSeamBlur: (state, action: PayloadAction<number>) => {
|
||||
state.seamBlur = action.payload;
|
||||
},
|
||||
setSeamStrength: (state, action: PayloadAction<number>) => {
|
||||
state.seamStrength = action.payload;
|
||||
},
|
||||
setSeamSteps: (state, action: PayloadAction<number>) => {
|
||||
state.seamSteps = action.payload;
|
||||
},
|
||||
setTileSize: (state, action: PayloadAction<number>) => {
|
||||
state.tileSize = action.payload;
|
||||
},
|
||||
setInfillMethod: (state, action: PayloadAction<string>) => {
|
||||
state.infillMethod = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const {
|
||||
clearInitialImage,
|
||||
resetParametersState,
|
||||
resetSeed,
|
||||
setAllImageToImageParameters,
|
||||
setAllParameters,
|
||||
setAllTextToImageParameters,
|
||||
setCfgScale,
|
||||
setHeight,
|
||||
setImg2imgStrength,
|
||||
setInfillMethod,
|
||||
setInitialImage,
|
||||
setIterations,
|
||||
setMaskPath,
|
||||
setParameter,
|
||||
setPerlin,
|
||||
setPrompt,
|
||||
setNegativePrompt,
|
||||
setSampler,
|
||||
setSeamBlur,
|
||||
setSeamless,
|
||||
setSeamSize,
|
||||
setSeamSteps,
|
||||
setSeamStrength,
|
||||
setSeed,
|
||||
setSeedWeights,
|
||||
setShouldFitToWidthHeight,
|
||||
setShouldGenerateVariations,
|
||||
setShouldRandomizeSeed,
|
||||
setSteps,
|
||||
setThreshold,
|
||||
setTileSize,
|
||||
setVariationAmount,
|
||||
setWidth,
|
||||
} = generationSlice.actions;
|
||||
|
||||
export default generationSlice.reducer;
|
||||
@@ -0,0 +1,3 @@
|
||||
import { RootState } from 'app/store';
|
||||
|
||||
export const postprocessingSelector = (state: RootState) => state.postprocessing;
|
||||
@@ -0,0 +1,94 @@
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { FACETOOL_TYPES } from 'app/constants';
|
||||
|
||||
export type UpscalingLevel = 2 | 4;
|
||||
|
||||
export type FacetoolType = typeof FACETOOL_TYPES[number];
|
||||
|
||||
export interface PostprocessingState {
|
||||
codeformerFidelity: number;
|
||||
facetoolStrength: number;
|
||||
facetoolType: FacetoolType;
|
||||
hiresFix: boolean;
|
||||
hiresStrength: number;
|
||||
shouldLoopback: boolean;
|
||||
shouldRunESRGAN: boolean;
|
||||
shouldRunFacetool: boolean;
|
||||
upscalingLevel: UpscalingLevel;
|
||||
upscalingStrength: number;
|
||||
}
|
||||
|
||||
const initialPostprocessingState: PostprocessingState = {
|
||||
codeformerFidelity: 0.75,
|
||||
facetoolStrength: 0.8,
|
||||
facetoolType: 'gfpgan',
|
||||
hiresFix: false,
|
||||
hiresStrength: 0.75,
|
||||
shouldLoopback: false,
|
||||
shouldRunESRGAN: false,
|
||||
shouldRunFacetool: false,
|
||||
upscalingLevel: 4,
|
||||
upscalingStrength: 0.75,
|
||||
};
|
||||
|
||||
const initialState: PostprocessingState = initialPostprocessingState;
|
||||
|
||||
export const postprocessingSlice = createSlice({
|
||||
name: 'postprocessing',
|
||||
initialState,
|
||||
reducers: {
|
||||
setFacetoolStrength: (state, action: PayloadAction<number>) => {
|
||||
state.facetoolStrength = action.payload;
|
||||
},
|
||||
setCodeformerFidelity: (state, action: PayloadAction<number>) => {
|
||||
state.codeformerFidelity = action.payload;
|
||||
},
|
||||
setUpscalingLevel: (state, action: PayloadAction<UpscalingLevel>) => {
|
||||
state.upscalingLevel = action.payload;
|
||||
},
|
||||
setUpscalingStrength: (state, action: PayloadAction<number>) => {
|
||||
state.upscalingStrength = action.payload;
|
||||
},
|
||||
setHiresFix: (state, action: PayloadAction<boolean>) => {
|
||||
state.hiresFix = action.payload;
|
||||
},
|
||||
setHiresStrength: (state, action: PayloadAction<number>) => {
|
||||
state.hiresStrength = action.payload;
|
||||
},
|
||||
resetPostprocessingState: (state) => {
|
||||
return {
|
||||
...state,
|
||||
...initialPostprocessingState,
|
||||
};
|
||||
},
|
||||
setShouldRunFacetool: (state, action: PayloadAction<boolean>) => {
|
||||
state.shouldRunFacetool = action.payload;
|
||||
},
|
||||
setFacetoolType: (state, action: PayloadAction<FacetoolType>) => {
|
||||
state.facetoolType = action.payload;
|
||||
},
|
||||
setShouldRunESRGAN: (state, action: PayloadAction<boolean>) => {
|
||||
state.shouldRunESRGAN = action.payload;
|
||||
},
|
||||
setShouldLoopback: (state, action: PayloadAction<boolean>) => {
|
||||
state.shouldLoopback = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const {
|
||||
resetPostprocessingState,
|
||||
setCodeformerFidelity,
|
||||
setFacetoolStrength,
|
||||
setFacetoolType,
|
||||
setHiresFix,
|
||||
setHiresStrength,
|
||||
setShouldLoopback,
|
||||
setShouldRunESRGAN,
|
||||
setShouldRunFacetool,
|
||||
setUpscalingLevel,
|
||||
setUpscalingStrength,
|
||||
} = postprocessingSlice.actions;
|
||||
|
||||
export default postprocessingSlice.reducer;
|
||||
Reference in New Issue
Block a user