resolve conflicts

This commit is contained in:
Lincoln Stein
2023-07-15 19:47:50 -04:00
324 changed files with 10448 additions and 7616 deletions

View File

@ -2,22 +2,26 @@ import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAISlider from 'common/components/IAISlider';
import { roundToMultiple } from 'common/util/roundDownToMultiple';
import {
canvasSelector,
isStagingSelector,
} from 'features/canvas/store/canvasSelectors';
import { setBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
import { uiSelector } from 'features/ui/store/uiSelectors';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
const selector = createSelector(
[canvasSelector, isStagingSelector],
(canvas, isStaging) => {
[canvasSelector, isStagingSelector, uiSelector],
(canvas, isStaging, ui) => {
const { boundingBoxDimensions } = canvas;
const { aspectRatio } = ui;
return {
boundingBoxDimensions,
isStaging,
aspectRatio,
};
},
defaultSelectorOptions
@ -25,7 +29,8 @@ const selector = createSelector(
const ParamBoundingBoxWidth = () => {
const dispatch = useAppDispatch();
const { boundingBoxDimensions, isStaging } = useAppSelector(selector);
const { boundingBoxDimensions, isStaging, aspectRatio } =
useAppSelector(selector);
const { t } = useTranslation();
@ -36,6 +41,15 @@ const ParamBoundingBoxWidth = () => {
height: Math.floor(v),
})
);
if (aspectRatio) {
const newWidth = roundToMultiple(v * aspectRatio, 64);
dispatch(
setBoundingBoxDimensions({
width: newWidth,
height: Math.floor(v),
})
);
}
};
const handleResetHeight = () => {
@ -45,6 +59,15 @@ const ParamBoundingBoxWidth = () => {
height: Math.floor(512),
})
);
if (aspectRatio) {
const newWidth = roundToMultiple(512 * aspectRatio, 64);
dispatch(
setBoundingBoxDimensions({
width: newWidth,
height: Math.floor(512),
})
);
}
};
return (

View File

@ -0,0 +1,57 @@
import { Flex, Spacer, Text } from '@chakra-ui/react';
import { useAppDispatch } from 'app/store/storeHooks';
import IAIIconButton from 'common/components/IAIIconButton';
import { flipBoundingBoxAxes } from 'features/canvas/store/canvasSlice';
import { useTranslation } from 'react-i18next';
import { MdOutlineSwapVert } from 'react-icons/md';
import ParamAspectRatio from '../../Core/ParamAspectRatio';
import ParamBoundingBoxHeight from './ParamBoundingBoxHeight';
import ParamBoundingBoxWidth from './ParamBoundingBoxWidth';
export default function ParamBoundingBoxSize() {
const dispatch = useAppDispatch();
const { t } = useTranslation();
return (
<Flex
sx={{
gap: 2,
p: 4,
borderRadius: 4,
flexDirection: 'column',
w: 'full',
bg: 'base.150',
_dark: {
bg: 'base.750',
},
}}
>
<Flex alignItems="center" gap={2}>
<Text
sx={{
fontSize: 'sm',
width: 'full',
color: 'base.700',
_dark: {
color: 'base.300',
},
}}
>
{t('parameters.aspectRatio')}
</Text>
<Spacer />
<ParamAspectRatio />
<IAIIconButton
tooltip={t('ui.swapSizes')}
aria-label={t('ui.swapSizes')}
size="sm"
icon={<MdOutlineSwapVert />}
fontSize={20}
onClick={() => dispatch(flipBoundingBoxAxes())}
/>
</Flex>
<ParamBoundingBoxWidth />
<ParamBoundingBoxHeight />
</Flex>
);
}

View File

@ -2,22 +2,26 @@ import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAISlider from 'common/components/IAISlider';
import { roundToMultiple } from 'common/util/roundDownToMultiple';
import {
canvasSelector,
isStagingSelector,
} from 'features/canvas/store/canvasSelectors';
import { setBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
import { uiSelector } from 'features/ui/store/uiSelectors';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
const selector = createSelector(
[canvasSelector, isStagingSelector],
(canvas, isStaging) => {
[canvasSelector, isStagingSelector, uiSelector],
(canvas, isStaging, ui) => {
const { boundingBoxDimensions } = canvas;
const { aspectRatio } = ui;
return {
boundingBoxDimensions,
isStaging,
aspectRatio,
};
},
defaultSelectorOptions
@ -25,7 +29,8 @@ const selector = createSelector(
const ParamBoundingBoxWidth = () => {
const dispatch = useAppDispatch();
const { boundingBoxDimensions, isStaging } = useAppSelector(selector);
const { boundingBoxDimensions, isStaging, aspectRatio } =
useAppSelector(selector);
const { t } = useTranslation();
@ -36,6 +41,15 @@ const ParamBoundingBoxWidth = () => {
width: Math.floor(v),
})
);
if (aspectRatio) {
const newHeight = roundToMultiple(v / aspectRatio, 64);
dispatch(
setBoundingBoxDimensions({
width: Math.floor(v),
height: newHeight,
})
);
}
};
const handleResetWidth = () => {
@ -45,6 +59,15 @@ const ParamBoundingBoxWidth = () => {
width: Math.floor(512),
})
);
if (aspectRatio) {
const newHeight = roundToMultiple(512 / aspectRatio, 64);
dispatch(
setBoundingBoxDimensions({
width: Math.floor(512),
height: newHeight,
})
);
}
};
return (

View File

@ -1,23 +1,21 @@
import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIMantineSelect from 'common/components/IAIMantineSelect';
import { generationSelector } from 'features/parameters/store/generationSelectors';
import { setInfillMethod } from 'features/parameters/store/generationSlice';
import { systemSelector } from 'features/system/store/systemSelectors';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { useGetAppConfigQuery } from 'services/api/endpoints/appInfo';
const selector = createSelector(
[generationSelector, systemSelector],
(parameters, system) => {
const { infillMethod } = parameters;
const { infillMethods } = system;
[stateSelector],
({ generation }) => {
const { infillMethod } = generation;
return {
infillMethod,
infillMethods,
};
},
defaultSelectorOptions
@ -25,7 +23,11 @@ const selector = createSelector(
const ParamInfillMethod = () => {
const dispatch = useAppDispatch();
const { infillMethod, infillMethods } = useAppSelector(selector);
const { infillMethod } = useAppSelector(selector);
const { data: appConfigData, isLoading } = useGetAppConfigQuery();
const infill_methods = appConfigData?.infill_methods;
const { t } = useTranslation();
@ -38,9 +40,11 @@ const ParamInfillMethod = () => {
return (
<IAIMantineSelect
disabled={infill_methods?.length === 0}
placeholder={isLoading ? 'Loading...' : undefined}
label={t('parameters.infillMethod')}
value={infillMethod}
data={infillMethods}
data={infill_methods ?? []}
onChange={handleChange}
/>
);

View File

@ -1,7 +1,7 @@
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIMantineSelect from 'common/components/IAIMantineSelect';
import IAIMantineSearchableSelect from 'common/components/IAIMantineSearchableSelect';
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
import { setBoundingBoxScaleMethod } from 'features/canvas/store/canvasSlice';
import {
@ -35,7 +35,7 @@ const ParamScaleBeforeProcessing = () => {
};
return (
<IAIMantineSelect
<IAIMantineSearchableSelect
label={t('parameters.scaleBeforeProcessing')}
data={BOUNDING_BOX_SCALES_DICT}
value={boundingBoxScale}

View File

@ -2,12 +2,13 @@ import { Divider, Flex } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIButton from 'common/components/IAIButton';
import IAICollapse from 'common/components/IAICollapse';
import IAIIconButton from 'common/components/IAIIconButton';
import ControlNet from 'features/controlNet/components/ControlNet';
import ParamControlNetFeatureToggle from 'features/controlNet/components/parameters/ParamControlNetFeatureToggle';
import {
controlNetAdded,
controlNetModelChanged,
controlNetSelector,
} from 'features/controlNet/store/controlNetSlice';
import { getValidControlNets } from 'features/controlNet/util/getValidControlNets';
@ -15,6 +16,8 @@ import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { map } from 'lodash-es';
import { Fragment, memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { FaPlus } from 'react-icons/fa';
import { useGetControlNetModelsQuery } from 'services/api/endpoints/models';
import { v4 as uuidv4 } from 'uuid';
const selector = createSelector(
@ -39,10 +42,23 @@ const ParamControlNetCollapse = () => {
const { controlNetsArray, activeLabel } = useAppSelector(selector);
const isControlNetDisabled = useFeatureStatus('controlNet').isFeatureDisabled;
const dispatch = useAppDispatch();
const { firstModel } = useGetControlNetModelsQuery(undefined, {
selectFromResult: (result) => {
const firstModel = result.data?.entities[result.data?.ids[0]];
return {
firstModel,
};
},
});
const handleClickedAddControlNet = useCallback(() => {
dispatch(controlNetAdded({ controlNetId: uuidv4() }));
}, [dispatch]);
if (!firstModel) {
return;
}
const controlNetId = uuidv4();
dispatch(controlNetAdded({ controlNetId }));
dispatch(controlNetModelChanged({ controlNetId, model: firstModel }));
}, [dispatch, firstModel]);
if (isControlNetDisabled) {
return null;
@ -51,16 +67,39 @@ const ParamControlNetCollapse = () => {
return (
<IAICollapse label="ControlNet" activeLabel={activeLabel}>
<Flex sx={{ flexDir: 'column', gap: 3 }}>
<ParamControlNetFeatureToggle />
<Flex gap={2} alignItems="center">
<Flex
sx={{
flexDirection: 'column',
w: '100%',
gap: 2,
px: 4,
py: 2,
borderRadius: 4,
bg: 'base.200',
_dark: {
bg: 'base.850',
},
}}
>
<ParamControlNetFeatureToggle />
</Flex>
<IAIIconButton
tooltip="Add ControlNet"
aria-label="Add ControlNet"
icon={<FaPlus />}
isDisabled={!firstModel}
flexGrow={1}
size="md"
onClick={handleClickedAddControlNet}
/>
</Flex>
{controlNetsArray.map((c, i) => (
<Fragment key={c.controlNetId}>
{i > 0 && <Divider />}
<ControlNet controlNet={c} />
<ControlNet controlNetId={c.controlNetId} />
</Fragment>
))}
<IAIButton flexGrow={1} onClick={handleClickedAddControlNet}>
Add ControlNet
</IAIButton>
</Flex>
</IAICollapse>
);

View File

@ -3,6 +3,7 @@ import { RootState } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIButton from 'common/components/IAIButton';
import { setAspectRatio } from 'features/ui/store/uiSlice';
import { activeTabNameSelector } from '../../../../ui/store/uiSelectors';
const aspectRatios = [
{ name: 'Free', value: null },
@ -17,6 +18,10 @@ export default function ParamAspectRatio() {
);
const dispatch = useAppDispatch();
const shouldFitToWidthHeight = useAppSelector(
(state: RootState) => state.generation.shouldFitToWidthHeight
);
const activeTabName = useAppSelector(activeTabNameSelector);
return (
<Flex gap={2} flexGrow={1}>
@ -26,6 +31,9 @@ export default function ParamAspectRatio() {
key={ratio.name}
size="sm"
isChecked={aspectRatio === ratio.value}
isDisabled={
activeTabName === 'img2img' ? !shouldFitToWidthHeight : false
}
onClick={() => dispatch(setAspectRatio(ratio.value))}
>
{ratio.name}

View File

@ -1,8 +1,8 @@
import { Box, Flex } from '@chakra-ui/react';
import ModelSelect from 'features/system/components/ModelSelect';
import VAESelect from 'features/system/components/VAESelect';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { memo } from 'react';
import { useFeatureStatus } from '../../../../system/hooks/useFeatureStatus';
import ParamMainModelSelect from '../MainModel/ParamMainModelSelect';
import ParamVAEModelSelect from '../VAEModel/ParamVAEModelSelect';
import ParamScheduler from './ParamScheduler';
const ParamModelandVAEandScheduler = () => {
@ -11,12 +11,12 @@ const ParamModelandVAEandScheduler = () => {
return (
<Flex gap={3} w="full" flexWrap={isVaeEnabled ? 'wrap' : 'nowrap'}>
<Box w="full">
<ModelSelect />
<ParamMainModelSelect />
</Box>
<Flex gap={3} w="full">
{isVaeEnabled && (
<Box w="full">
<VAESelect />
<ParamVAEModelSelect />
</Box>
)}
<Box w="full">

View File

@ -2,10 +2,10 @@ import { createSelector } from '@reduxjs/toolkit';
import { SCHEDULER_LABEL_MAP, SCHEDULER_NAMES } from 'app/constants';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIMantineSelect from 'common/components/IAIMantineSelect';
import IAIMantineSearchableSelect from 'common/components/IAIMantineSearchableSelect';
import { generationSelector } from 'features/parameters/store/generationSelectors';
import { setScheduler } from 'features/parameters/store/generationSlice';
import { SchedulerParam } from 'features/parameters/store/parameterZodSchemas';
import { SchedulerParam } from 'features/parameters/types/parameterSchemas';
import { uiSelector } from 'features/ui/store/uiSelectors';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
@ -48,7 +48,7 @@ const ParamScheduler = () => {
);
return (
<IAIMantineSelect
<IAIMantineSearchableSelect
label={t('parameters.scheduler')}
value={scheduler}
data={data}

View File

@ -8,6 +8,7 @@ import { MdOutlineSwapVert } from 'react-icons/md';
import ParamAspectRatio from './ParamAspectRatio';
import ParamHeight from './ParamHeight';
import ParamWidth from './ParamWidth';
import { activeTabNameSelector } from '../../../../ui/store/uiSelectors';
export default function ParamSize() {
const { t } = useTranslation();
@ -15,6 +16,7 @@ export default function ParamSize() {
const shouldFitToWidthHeight = useAppSelector(
(state: RootState) => state.generation.shouldFitToWidthHeight
);
const activeTabName = useAppSelector(activeTabNameSelector);
return (
<Flex
sx={{
@ -50,13 +52,24 @@ export default function ParamSize() {
size="sm"
icon={<MdOutlineSwapVert />}
fontSize={20}
isDisabled={
activeTabName === 'img2img' ? !shouldFitToWidthHeight : false
}
onClick={() => dispatch(toggleSize())}
/>
</Flex>
<Flex gap={2} alignItems="center">
<Flex gap={2} flexDirection="column" width="full">
<ParamWidth isDisabled={!shouldFitToWidthHeight} />
<ParamHeight isDisabled={!shouldFitToWidthHeight} />
<ParamWidth
isDisabled={
activeTabName === 'img2img' ? !shouldFitToWidthHeight : false
}
/>
<ParamHeight
isDisabled={
activeTabName === 'img2img' ? !shouldFitToWidthHeight : false
}
/>
</Flex>
</Flex>
</Flex>

View File

@ -1,7 +1,7 @@
import { FACETOOL_TYPES } from 'app/constants';
import { RootState } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIMantineSelect from 'common/components/IAIMantineSelect';
import IAIMantineSearchableSelect from 'common/components/IAIMantineSearchableSelect';
import {
FacetoolType,
setFacetoolType,
@ -20,7 +20,7 @@ export default function FaceRestoreType() {
dispatch(setFacetoolType(v as FacetoolType));
return (
<IAIMantineSelect
<IAIMantineSearchableSelect
label={t('parameters.type')}
data={FACETOOL_TYPES.concat()}
value={facetoolType}

View File

@ -1,30 +1,24 @@
import { Flex, Icon, Text } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks';
import { useMemo } from 'react';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIDndImage from 'common/components/IAIDndImage';
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
import { skipToken } from '@reduxjs/toolkit/dist/query';
import { FaImage } from 'react-icons/fa';
import { stateSelector } from 'app/store/store';
import {
TypesafeDraggableData,
TypesafeDroppableData,
} from 'app/components/ImageDnd/typesafeDnd';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIDndImage from 'common/components/IAIDndImage';
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
import { useMemo } from 'react';
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
const selector = createSelector(
[stateSelector],
(state) => {
const { initialImage } = state.generation;
const { asInitialImage: useBatchAsInitialImage, imageNames } = state.batch;
return {
initialImage,
useBatchAsInitialImage,
isResetButtonDisabled: useBatchAsInitialImage
? imageNames.length === 0
: !initialImage,
isResetButtonDisabled: !initialImage,
};
},
defaultSelectorOptions

View File

@ -1,22 +1,14 @@
import { Flex, Spacer, Text } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { clearInitialImage } from 'features/parameters/store/generationSlice';
import { useCallback, useMemo } from 'react';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
import { skipToken } from '@reduxjs/toolkit/dist/query';
import IAIIconButton from 'common/components/IAIIconButton';
import { FaLayerGroup, FaUndo, FaUpload } from 'react-icons/fa';
import useImageUploader from 'common/hooks/useImageUploader';
import { useImageUploadButton } from 'common/hooks/useImageUploadButton';
import IAIButton from 'common/components/IAIButton';
import { stateSelector } from 'app/store/store';
import {
asInitialImageToggled,
batchReset,
} from 'features/batch/store/batchSlice';
import BatchImageContainer from 'features/batch/components/BatchImageContainer';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIIconButton from 'common/components/IAIIconButton';
import { useImageUploadButton } from 'common/hooks/useImageUploadButton';
import useImageUploader from 'common/hooks/useImageUploader';
import { clearInitialImage } from 'features/parameters/store/generationSlice';
import { useCallback } from 'react';
import { FaUndo, FaUpload } from 'react-icons/fa';
import { PostUploadAction } from 'services/api/thunks/image';
import InitialImage from './InitialImage';
@ -24,59 +16,34 @@ const selector = createSelector(
[stateSelector],
(state) => {
const { initialImage } = state.generation;
const { asInitialImage: useBatchAsInitialImage, imageNames } = state.batch;
return {
initialImage,
useBatchAsInitialImage,
isResetButtonDisabled: useBatchAsInitialImage
? imageNames.length === 0
: !initialImage,
isResetButtonDisabled: !initialImage,
};
},
defaultSelectorOptions
);
const postUploadAction: PostUploadAction = {
type: 'SET_INITIAL_IMAGE',
};
const InitialImageDisplay = () => {
const { initialImage, useBatchAsInitialImage, isResetButtonDisabled } =
useAppSelector(selector);
const { isResetButtonDisabled } = useAppSelector(selector);
const dispatch = useAppDispatch();
const { openUploader } = useImageUploader();
const {
currentData: imageDTO,
isLoading,
isError,
isSuccess,
} = useGetImageDTOQuery(initialImage?.imageName ?? skipToken);
const postUploadAction = useMemo<PostUploadAction>(
() =>
useBatchAsInitialImage
? { type: 'ADD_TO_BATCH' }
: { type: 'SET_INITIAL_IMAGE' },
[useBatchAsInitialImage]
);
const { getUploadButtonProps, getUploadInputProps } = useImageUploadButton({
postUploadAction,
});
const handleReset = useCallback(() => {
if (useBatchAsInitialImage) {
dispatch(batchReset());
} else {
dispatch(clearInitialImage());
}
}, [dispatch, useBatchAsInitialImage]);
dispatch(clearInitialImage());
}, [dispatch]);
const handleUpload = useCallback(() => {
openUploader();
}, [openUploader]);
const handleClickUseBatch = useCallback(() => {
dispatch(asInitialImageToggled());
}, [dispatch]);
return (
<Flex
layerStyle={'first'}
@ -114,40 +81,22 @@ const InitialImageDisplay = () => {
Initial Image
</Text>
<Spacer />
{/* <IAIButton
tooltip={useBatchAsInitialImage ? 'Disable Batch' : 'Enable Batch'}
aria-label={useBatchAsInitialImage ? 'Disable Batch' : 'Enable Batch'}
leftIcon={<FaLayerGroup />}
isChecked={useBatchAsInitialImage}
onClick={handleClickUseBatch}
>
{useBatchAsInitialImage ? 'Batch' : 'Single'}
</IAIButton> */}
<IAIIconButton
tooltip={
useBatchAsInitialImage ? 'Upload to Batch' : 'Upload Initial Image'
}
aria-label={
useBatchAsInitialImage ? 'Upload to Batch' : 'Upload Initial Image'
}
tooltip={'Upload Initial Image'}
aria-label={'Upload Initial Image'}
icon={<FaUpload />}
onClick={handleUpload}
{...getUploadButtonProps()}
/>
<IAIIconButton
tooltip={
useBatchAsInitialImage ? 'Reset Batch' : 'Reset Initial Image'
}
aria-label={
useBatchAsInitialImage ? 'Reset Batch' : 'Reset Initial Image'
}
tooltip={'Reset Initial Image'}
aria-label={'Reset Initial Image'}
icon={<FaUndo />}
onClick={handleReset}
isDisabled={isResetButtonDisabled}
/>
</Flex>
<InitialImage />
{/* {useBatchAsInitialImage ? <BatchImageContainer /> : <InitialImage />} */}
<input {...getUploadInputProps()} />
</Flex>
);

View File

@ -0,0 +1,100 @@
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIMantineSearchableSelect from 'common/components/IAIMantineSearchableSelect';
import { SelectItem } from '@mantine/core';
import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { modelSelected } from 'features/parameters/store/actions';
import { MODEL_TYPE_MAP } from 'features/parameters/types/constants';
import { modelIdToMainModelParam } from 'features/parameters/util/modelIdToMainModelParam';
import { forEach } from 'lodash-es';
import { useGetMainModelsQuery } from 'services/api/endpoints/models';
const selector = createSelector(
stateSelector,
(state) => ({ model: state.generation.model }),
defaultSelectorOptions
);
const ParamMainModelSelect = () => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const { model } = useAppSelector(selector);
const { data: mainModels, isLoading } = useGetMainModelsQuery();
const data = useMemo(() => {
if (!mainModels) {
return [];
}
const data: SelectItem[] = [];
forEach(mainModels.entities, (model, id) => {
if (!model) {
return;
}
data.push({
value: id,
label: model.model_name,
group: MODEL_TYPE_MAP[model.base_model],
});
});
return data;
}, [mainModels]);
// grab the full model entity from the RTK Query cache
// TODO: maybe we should just store the full model entity in state?
const selectedModel = useMemo(
() =>
mainModels?.entities[`${model?.base_model}/main/${model?.model_name}`] ??
null,
[mainModels?.entities, model]
);
const handleChangeModel = useCallback(
(v: string | null) => {
if (!v) {
return;
}
const newModel = modelIdToMainModelParam(v);
if (!newModel) {
return;
}
dispatch(modelSelected(newModel));
},
[dispatch]
);
return isLoading ? (
<IAIMantineSearchableSelect
label={t('modelManager.model')}
placeholder="Loading..."
disabled={true}
data={[]}
/>
) : (
<IAIMantineSearchableSelect
tooltip={selectedModel?.description}
label={t('modelManager.model')}
value={selectedModel?.id}
placeholder={data.length > 0 ? 'Select a model' : 'No models available'}
data={data}
error={data.length === 0}
disabled={data.length === 0}
onChange={handleChangeModel}
/>
);
};
export default memo(ParamMainModelSelect);

View File

@ -27,6 +27,9 @@ const ParamNoiseCollapse = () => {
const { t } = useTranslation();
const isNoiseEnabled = useFeatureStatus('noise').isFeatureEnabled;
const isPerlinNoiseEnabled = useFeatureStatus('perlinNoise').isFeatureEnabled;
const isNoiseThresholdEnabled =
useFeatureStatus('noiseThreshold').isFeatureEnabled;
const { activeLabel } = useAppSelector(selector);
@ -42,8 +45,8 @@ const ParamNoiseCollapse = () => {
<Flex sx={{ gap: 2, flexDirection: 'column' }}>
<ParamNoiseToggle />
<ParamCpuNoiseToggle />
<ParamPerlinNoise />
<ParamNoiseThreshold />
{isPerlinNoiseEnabled && <ParamPerlinNoise />}
{isNoiseThresholdEnabled && <ParamNoiseThreshold />}
</Flex>
</IAICollapse>
);

View File

@ -1,7 +1,7 @@
import { UPSCALING_LEVELS } from 'app/constants';
import type { RootState } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIMantineSelect from 'common/components/IAIMantineSelect';
import IAIMantineSearchableSelect from 'common/components/IAIMantineSearchableSelect';
import {
UpscalingLevel,
setUpscalingLevel,
@ -24,7 +24,7 @@ export default function UpscaleScale() {
dispatch(setUpscalingLevel(Number(v) as UpscalingLevel));
return (
<IAIMantineSelect
<IAIMantineSearchableSelect
disabled={!isESRGANAvailable}
label={t('parameters.scale')}
value={String(upscalingLevel)}

View File

@ -0,0 +1,110 @@
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIMantineSearchableSelect from 'common/components/IAIMantineSearchableSelect';
import { SelectItem } from '@mantine/core';
import { forEach } from 'lodash-es';
import { useGetVaeModelsQuery } from 'services/api/endpoints/models';
import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIMantineSelectItemWithTooltip from 'common/components/IAIMantineSelectItemWithTooltip';
import { vaeSelected } from 'features/parameters/store/generationSlice';
import { MODEL_TYPE_MAP } from 'features/parameters/types/constants';
import { modelIdToVAEModelParam } from 'features/parameters/util/modelIdToVAEModelParam';
const selector = createSelector(
stateSelector,
({ generation }) => {
const { model, vae } = generation;
return { model, vae };
},
defaultSelectorOptions
);
const ParamVAEModelSelect = () => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const { model, vae } = useAppSelector(selector);
const { data: vaeModels } = useGetVaeModelsQuery();
const data = useMemo(() => {
if (!vaeModels) {
return [];
}
// add a "default" option, this means use the main model's included VAE
const data: SelectItem[] = [
{
value: 'default',
label: 'Default',
group: 'Default',
},
];
forEach(vaeModels.entities, (vae, id) => {
if (!vae) {
return;
}
const disabled = model?.base_model !== vae.base_model;
data.push({
value: id,
label: vae.model_name,
group: MODEL_TYPE_MAP[vae.base_model],
disabled,
tooltip: disabled
? `Incompatible base model: ${vae.base_model}`
: undefined,
});
});
return data.sort((a, b) => (a.disabled && !b.disabled ? 1 : -1));
}, [vaeModels, model?.base_model]);
// grab the full model entity from the RTK Query cache
const selectedVaeModel = useMemo(
() =>
vaeModels?.entities[`${vae?.base_model}/vae/${vae?.model_name}`] ?? null,
[vaeModels?.entities, vae]
);
const handleChangeModel = useCallback(
(v: string | null) => {
if (!v || v === 'default') {
dispatch(vaeSelected(null));
return;
}
const newVaeModel = modelIdToVAEModelParam(v);
if (!newVaeModel) {
return;
}
dispatch(vaeSelected(newVaeModel));
},
[dispatch]
);
return (
<IAIMantineSearchableSelect
itemComponent={IAIMantineSelectItemWithTooltip}
tooltip={selectedVaeModel?.description}
label={t('modelManager.vae')}
value={selectedVaeModel?.id ?? 'default'}
placeholder="Default"
data={data}
onChange={handleChangeModel}
disabled={data.length === 0}
clearable
/>
);
};
export default memo(ParamVAEModelSelect);

View File

@ -17,8 +17,10 @@ import { FaPlay } from 'react-icons/fa';
const IN_PROGRESS_STYLES: ChakraProps['sx'] = {
_disabled: {
bg: 'none',
color: 'base.600',
cursor: 'not-allowed',
_hover: {
color: 'base.600',
bg: 'none',
},
},